任务1.1
1、数据导入
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import random
plt.rcParams['font.family'] = 'SimHei' # 正常显示中文
plt.rcParams['axes.unicode_minus'] = False
data1 = pd.read_csv('data1.csv',sep=',',encoding='gbk')
data1.columns =['序号','校园卡号','性别','专业名称','门禁卡号']
data2 = pd.read_csv('data2.csv',sep=',',encoding='gbk')
data2.columns=['流水号','校园卡号','学号','消费时间','消费金额','充值金额','余额',
'消费次数','消费类型','消费项目编码','消费项目序号','消费操作编码','操作编码','消费地点']
data3 = pd.read_csv(r'data3.csv',sep=',',encoding='gbk')
data3.columns =['序号','门禁卡号','出入日期','出入地点','进出成功编号','通过权限']
data1:
data2:
data3:
2、缺失值分析:
data2数据的消费项目序号、消费项目编码缺失超过九成,已无实际分析意义,将其去除:
# 删除缺失值过多的列
data2 = data2.drop(['消费项目序号','消费操作编码'],axis = 1)
3、异常值分析:
data1箱线图分析:
def boxplot(data):
fig = plt.figure(figsize = (20,20))
for i,col in enumerate(data.columns):
plt.subplot(4,3,i+1)
data[[col]].boxplot()
plt.show()
boxplot(data1[['校园卡号','门禁卡号']])
探索校园卡号异常数据:
由以上我们可知异常的两个校园卡号很可能是原本为18开头而变成了16,将其修改即可。
data1['校园卡号'].replace({164340:184340,164341:184341},inplace=True)
针对门禁卡号异常数据:
data2箱线图分析:
def get_colors(color_style):
cnames = sns.xkcd_rgb
if color_style =='light':
colors = list(filter(lambda x:x[:5]=='light',cnames.keys()))
elif color_style =='dark':
colors = list(filter(lambda x:x[:4]=='dark',cnames.keys()))
elif color_style =='all':
colors = cnames.keys()
colors = list(map(lambda x:cnames[x], colors))
return colors
# 封装箱线图
def boxplot(data, rows = 3, cols = 4, figsize = (13, 8), vars =None, hue = None, width = 0.25,
color_style ='light',subplots_adjust = (0.2, 0.2)):
fig = plt.figure(figsize = figsize)
hue = data[hue] if isinstance(hue,str) and hue in data.columns else hue
data = data if not vars else data[vars]
colors = get_colors(color_style)
ax_num = 1
for col in data.columns:
if isinstance(data[col].values[0],(np.int64,np.int32,np.int16,np.int8,np.float16,np.float32,np.float64)):
plt.subplot(rows, cols, ax_num)
sns.boxplot(x = hue,y = data[col].values,color=random.sample(colors,1)[0],width= width)
plt.xlabel(col)
# data[col].plot(kind = 'box',color=random.sample(colors,1)[0])
ax_num+=1
plt.subplots_adjust(hspace = subplots_adjust[0],wspace=subplots_adjust[1])
plt.show()
boxplot(data2)
对data2数据结合现实场景主观决定否去除异常值。
对消费时间特征进行分析:
# 将消费时间特征转换为datetime类型数据
time = pd.to_datetime(data2['消费时间']).dt.time
# 对消费时间点进行统计并按照时间排序后进行可视化分析
time.value_counts().sort_index().plot()
plt.title('消费记录统计')
plt.show()
如上可知消费时间在0点的数据量最多,明显不符合显示情况,推测可能是录入时间时出现错误等系统情况导致时间为默认的0点整点。
0点的数据量占有7000多条,且基于这些数据对后续分析仍有价值,因此暂时不进行删除等的处理。
data3箱线图分析:
time = pd.to_datetime(data3['出入日期']).dt.time
time.value_counts().sort_index().plot()
plt.title('门禁出入统计')
plt.show()
分析与data2一致,暂不处理。
保存数据:
data1.to_csv('task1_1_1.csv')
data2.to_csv('task1_1_2.csv')
data3.to_csv('task1_1_3.csv')
任务1.2
1、联结表
对data1与data2根据校园卡号进行联结,并取出为消费记录的数据。
data_2_1 = pd.merge(data1,data2,left_on='校园卡号')
data_2_1 = data_2_1[data_2_1['消费类型']=='消费']
data_2_2 = pd.merge(data1,data3,on = '门禁卡号')
简单根据校园卡号分析学生数量:
a = data_2_1.校园卡号.unique().size
b = data2.校园卡号.unique().size
sns.set_style('whitegrid',rc = {'font.family': 'SimHei'})
AxesSubplot = sns.barplot(x = ['总校园卡号','为消费类型的校园卡号'],y = [b,a])
plt.bar_label(AxesSubplot.containers[0])
可知在提供的现实某时段消费记录数据内,约有3200多名学生拥有消费记录。
data_2_1:
data_2_2:
任务2.1
绘制各食堂就餐人次的占比饼图,分析学生早中晚餐的就餐地点 是否有显著差别,并在报告中进行描述。(提示:时间间隔非常接近的多次刷卡 记录可能为一次就餐行为)
定义早中晚餐:
import datetime
from datetime import time
# 取出食堂的消费记录数据
data_shitang = data2[(data2['消费地点'].map(lambda x:'食堂' in x)) & (data2['消费类型'] =='消费')]
data_shitang['消费时间'] = pd.to_datetime(data_shitang.消费时间)
def eating_time(x):
y = []
for i in x:
if time(5,0)<=i.time()
统计分析每个食堂的早午晚餐刷卡次数。
fig, axes = plt.subplots(2, 3, figsize = (12, 7))
ax = axes.ravel()
labels = data_shitang['消费地点'].unique()
colors = list(map(lambda x:sns.xkcd_rgb[x], sns.xkcd_rgb.keys()))
colors = np.random.choice(colors,5)
ax_num = 0
for label in labels:
data_ = data_shitang[data_shitang['消费地点']==label] # 取出一个类别的数据
# 对该类别数据每个特征进行统计
d = data_['就餐类型'].value_counts()
ax[ax_num].pie(labels = d.index, x = d.values, autopct='%.1f%%',colors = colors)
# ax.pie(d.values, labels = d.values)
ax[ax_num].set_title(label, fontsize = 13)
ax_num+=1
plt.subplots_adjust(0.2,0.2)
由上可分析每个食堂的刷卡消费情况以及出餐类型,如教师食堂只提供午餐、学生在第三、第四食堂主要消费午餐、晚餐。
分析早中晚的各食堂就餐行为并绘制饼图:
该任务难点主要在于就餐次数的确定(依题意不能直接将刷卡次数当作就餐次数),结合题意及现实,定义30分钟内同个食堂的多次刷卡为一次消费行为即一次就餐次数,不同食堂30分钟内的多次刷卡仍视为多次就餐次数。
# 使30分钟内的多次刷卡为一次刷卡记录
def time_filter(x):
import datetime
# 初始化消费次数为刷卡次数
consums = len(x)
# 对消费时间进行降序
x = x.sort_values(ascending= False)
# 定义变量使得能跳出datetime1已经计算过的在十分钟内的datetime2
position = 0
for num,datetime1 in enumerate(x):
if position != 0:
position -= 1
continue
for datetime2 in x[num+1:]:
# 当时间小于30分钟时,consums消费次数-1
if datetime1-datetime2
data_shitang_zaocan = data_shitang[data_shitang['就餐类型'] =='早餐']
data_shitang_wucan = data_shitang[data_shitang['就餐类型'] =='午餐']
data_shitang_wancan = data_shitang[data_shitang['就餐类型'] =='晚餐']
data_shitang_ = [data_shitang_zaocan, data_shitang_wucan, data_shitang_wancan]
data_leixing = ['早餐', '午餐', '晚餐']
fig,axes = plt.subplots(1,3, figsize = (14,6))
counts = [] # 存储早午晚餐统计数据
for d, title, ax in zip(data_shitang_, data_leixing, axes):
d = d.groupby(['消费地点','校园卡号'],as_index =False)['消费时间'].agg(time_filter)
xiaofei_counts=d.groupby('消费地点')['消费时间'].sum()
xiaofei_counts.name = '消费次数'
counts.append(xiaofei_counts)
ax.pie(labels = xiaofei_counts.index,x = xiaofei_counts, autopct='%.1f%%')
ax.set_title(f'{title}各食堂就餐人次占比饼图')
plt.show()
使用pyecharts绘制饼图:
from pyecharts.charts import Pie
from pyecharts import options as opts
def pie_(xiaofei_counts, label):
pie = Pie()
pie.add('就餐次数统计',[list(z) for z in zip(xiaofei_counts.index,xiaofei_counts)],radius = ['50%','70%'],
rosetype = 'are',center=["50%", "53%"])
pie.set_global_opts(title_opts = opts.TitleOpts(title=f'{label}行为分析饼图'),
legend_opts=opts.LegendOpts(pos_bottom = 0))
# formatter中 a表示data_pair,b表示类别名,c表示类别数量,d表示百分数
pie.set_series_opts(label_opts=opts.LabelOpts(
position="outside",
formatter="{a|{a}}{abg|}\n{hr|}\n {b|{b}: }{c} {per|{d}%} ",
background_color="#eee",
border_color="#aaa",
border_width=1,
border_radius=4,
rich={
"a": {"color": "#999", "lineHeight": 22, "align": "center"},
"abg": {
"backgroundColor": "#e3e3e3",
"width": "100%",
"align": "right",
"height": 18,
"borderRadius": [4, 4, 0, 0],
},
"hr": {
"borderColor": "#aaa",
"width": "100%",
"borderWidth": 0.3,
"height": 0,
},
"b": {"fontSize": 14, "lineHeight": 33},
"per": {
"color": "#eee",
"backgroundColor": "#334455",
"padding": [2, 4],
"borderRadius": 2,
},
},
),legend_opts =opts.LegendOpts(type_ = 'scroll',
orient = 'horizontal',align ='left',
item_gap = 10,item_width = 25,item_height = 15,
inactive_color = 'break'))
pie.set_colors(['red',"orange", "yellow", "Cyan", "purple" ,"green","blue","#61e160","#d0fe1d"])
return pie.render_notebook()
任务 2.2 通过食堂刷卡记录,分别绘制工作日和非工作日食堂就餐时间曲 线图,分析食堂早中晚餐的就餐峰值,并在报告中进行描述。
统计:
# 获取小时数据
data_shitang['就餐时间'] = data_shitang['消费时间'].apply(lambda x:x.hour)
# 获取是否工作日
from chinese_calendar import is_workday,is_holiday
data_shitang['是否工作日'] = data_shitang['消费时间'].apply(lambda x: '工作日' if is_workday(x) else '非工作日')
# 获取工作日与非工作日的每个时间刷卡次数统计
data_isor_workday = data_shitang.groupby(['就餐时间','是否工作日']).size().unstack()
print(data_isor_workday)
# 工作日除以21天,非工作日除以9,得到日均刷卡次数
data_isor_workday = data_isor_workday/np.array([21,9])
# 缺失值填0处理(有的时段无刷卡次数,如凌晨)
data_isor_workday = data_isor_workday.fillna(0).astype(np.int)
可视化:
plt.plot(data_isor_workday.index,data_isor_workday['工作日'], label = '工作日')
plt.plot(data_isor_workday.index,data_isor_workday['非工作日'], label = '非工作日')
plt.xlabel('时间')
plt.ylabel('日均刷卡次数')
plt.xticks(range(24))
plt.legend()
plt.show()
pyecharst可视化:
import pyecharts
from pyecharts.charts import Line
from pyecharts import options as opts
from pyecharts.globals import ThemeType
from pyecharts import *
# 常用全局参数配置封装
def global_opts(line,x_name,y_name,title,bottom = None,left = None,split_line = False):
line.set_global_opts(title_opts=opts.TitleOpts(title = title),
xaxis_opts=opts.AxisOpts(name= x_name,type_='category', name_location='center',name_gap=25,max_interval =0),
yaxis_opts=opts.AxisOpts(name= y_name,type_='value', name_location='end',name_gap=15,
splitline_opts=opts.SplitLineOpts(is_show=split_line,
linestyle_opts=opts.LineStyleOpts(opacity=1)),),
legend_opts =opts.LegendOpts(type_ = 'scroll',
pos_bottom=bottom, pos_left = left,
orient = 'horizontal',align ='left',
item_gap = 10,item_width = 25,item_height = 15,
inactive_color = 'break'),
tooltip_opts=opts.TooltipOpts(trigger="axis", axis_pointer_type="cross"),
)
def mul_line_plot(data_x,data_y,x_name,y_name,title,):
line =Line(init_opts=opts.InitOpts(theme=ThemeType.DARK,bg_color = '',width='900px',height = '550px'))
line.add_xaxis(data_x)
for i in data_y.columns:
line.add_yaxis(series_name = i,y_axis =data_y.loc[:,i],is_smooth =True,symbol_size = 6,
linestyle_opts=opts.LineStyleOpts( width=2, type_="solid"),
label_opts = opts.LabelOpts(is_show=True,position = 'top',font_size =12,
font_style = 'italic',font_family= 'serif',))
global_opts(line,x_name,y_name,title,bottom = 0,left = 20)
return line.render_notebook()
mul_line_plot(data_x=(data_isor_workday).index.tolist(),data_y=(data_isor_workday),
x_name ='时间',y_name='日均刷卡次数',title ='就餐时间曲线图')
任务 3.1 根据学生的整体校园消费数据,计算本月人均刷卡频次和人均消 费额,并选择 3 个专业,分析不同专业间不同性别学生群体的消费特点。
d = data_2_1.groupby('校园卡号').agg({'消费次数':np.size,'消费金额':np.sum})[['消费金额','消费次数']]
# 封装箱线图
boxplot(data = d)
# 依据箱线图去除异常数据
d = d[ (d['消费金额'] < 800) & (d['消费次数'] < 180)]
# 本月人均刷卡次数约72次 、人均消费总额288
print(d.mean())
选择三个专业进行消费行为分析:
不同专业不同性别人均刷卡金额对比图:
data_3_zhuanye = data_2_1.query("专业名称 in ['18产品艺术','18会计','18动漫设计']")
a = data_3_zhuanye.groupby(['专业名称','性别'])['消费金额'].mean().unstack()
a = np.round(a,2) # 小数点两位且四舍五入
with sns.color_palette('rainbow_r'):
bar = a.plot.bar()
plt.xticks(rotation =0)
plt.title('平均每次刷卡金额')
for i in bar.containers:
plt.bar_label(i)
使用pyecharts:
bar = Bar()
bar.add_xaxis(a.index.tolist())
bar.add_yaxis('女',a.iloc[:,0].tolist(),itemstyle_opts=opts.ItemStyleOpts(color='red'))
bar.add_yaxis('男',a.iloc[:,1].tolist(),itemstyle_opts=opts.ItemStyleOpts(color='blue'))
global_opts(line = bar,title = '不同专业不同性别学生群体的关系',x_name = '专业',y_name = '平均刷卡金额/元')
bar.render_notebook()
不同专业不同性别就餐地点对比饼图:
with sns.color_palette('rainbow'):
# 封装函数,源程序在作者博客seaborn封装中可以找到
count_pieplot(data_3_zhuanye,3,2,vars = ['消费地点','专业名称','性别'],hue = '专业名称',qita_percentage_max= 0.02,figsize=(6,11))
pyecharts:
d_ = pd.pivot_table(data =data_3_zhuanye ,index =['消费地点'],columns = '专业名称',aggfunc='size',).fillna(0)
pie_(d_['18产品艺术'].sort_values(ascending=False)[:8],'18产品艺术消费地点')
# pie_(d_['18会计'].sort_values(ascending=False)[:8],'18会计消费地点')
# pie_(d_['18动漫设计'].sort_values(ascending=False)[:8],'18动漫设计消费地点')
with sns.color_palette('rainbow'):
# 作者封装函数,需要源程序可在作者博客seaborn封装中寻找
count_pieplot(data_3_zhuanye,1,2,vars = ['消费地点'],hue = '性别',qita_percentage_max= 0.02,figsize=(10,4))
不同专业男生消费地点饼图:
with sns.color_palette('rainbow'):
count_pieplot(data_3_zhuanye.query("性别 == '男'"),1,3,vars = ['消费地点','专业名称'],hue = '专业名称',qita_percentage_max= 0.02,figsize=(16,4))
不同专业女生消费地点饼图:
pyecharts:
d_ = pd.pivot_table(data =data_3_zhuanye ,index =['消费地点'],columns = '性别',aggfunc='size',).fillna(0)
pie_(d_['男'].sort_values(ascending=False)[:10],'男生消费地点')
# pie_(d_['女'].sort_values(ascending=False)[:10],'女生消费地点')
任务 3.2 根据学生的整体校园消费行为,选择合适的特征,构建聚类模型, 分析每一类学生群体的消费特点。
结合背景分析,取出每次刷卡平均消费金额、总消费次数、消费总金额三个特征进行聚类
import sklearn
from sklearn import cluster
from sklearn.preprocessing import StandardScaler
# 取出日常消费类型数据
data_2_1_1 = data_2_1.query("消费地点 in ['第四食堂','第一食堂','第二食堂', '红太阳超市','第五食堂','第三食堂', '好利来食品店']")
# 取出每次刷卡平均消费金额、总消费次数、消费总金额三个特征进行聚类
data = data_2_1_1.groupby(['校园卡号'],as_index=False)['消费金额'].mean()
data['本月内消费累计次数'] = data_2_1_1.groupby('校园卡号')['消费次数'].size().values
data['消费总金额'] = data_2_1_1.groupby('校园卡号')['消费金额'].sum().values
data = data.set_index('校园卡号')
data.columns = ['平均每次刷卡消费金额','本月内累计消费次数','消费总金额']
print(data)
Kmeans聚类:
# Kmeans聚类模型,七个聚类簇
model = cluster.KMeans(n_clusters=7)
# 标准化模型
scaler = StandardScaler()
# 标准化
data_ = scaler.fit_transform(data.iloc[:,:])
# 模型训练
model.fit(data_)
# 对数据进行聚类得到标签
labels = model.predict(data_)
# 将标签加入到data数据中
data['labels'] = labels
可视化:
二维散点图
sns.set(font='SimHei')
sns.scatterplot(data =data , x = '本月内累计消费次数',y= '平均每次刷卡消费金额',hue = 'labels',palette = 'rainbow')
plt.title('七个消费群体散点图')
plt.show()
三维散点图:
colors = ['#a88f59', '#da467d', '#fdb915', '#69d84f', '#380282','r','b']
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(15,8))
ax = fig.add_subplot(121, projection='3d')
for i in data['labels'].unique():
d = data[data['labels']==i]
ax.scatter(d['本月内累计消费次数'],d['平均每次刷卡消费金额'],d['消费总金额'],c=colors[i],label =i)
ax.set_xlabel('本月内累计消费次数')
ax.set_ylabel('平均每次刷卡消费金额')
ax.set_zlabel('消费总金额')
plt.title('高钾:层次聚类结果图',fontsize = 15)
plt.legend()
plt.show()
任务 3.3 通过对低消费学生群体的行为进行分析,探讨是否存在某些特征, 能为学校助学金评定提供参考。
分析并探讨低消费群体的特征为助学金评定提供建议:
基于任务3.2的聚类图并结合现实情况,低消费群体应为具有中等消费次数,低平均消费金额以及低消费总额的特点,因为消费次数太低说明可能很少在食堂消费,可能多为在校外饭馆等消费,不能判定为贫困学生,而消费次数高即使平均消费低也会导致高消费总额,也不能判定为贫困学生,据此分析,我们判定1号群体满足低消费贫困学生的特点,并且2号群体消费次数分布在100左右,符合一个月内正常的每天三餐的消费次数,所以若学校需要对贫困学生执行助学金政策,可在1号群体学生中从左下角开始选择(越靠近左下角代表消费总额越低)。
其他自主分析可视化图: