任务目标:使用python、tableau等工具,通过数据分析和建模的方法,挖掘数据中所蕴含的信息,分析学生在校园内的学习生活行为,为改进学校服务并为相关部门的决策提供信息支持
目录
一、数据预处理
加载data1、data2、data3数据,理解字段含义,缺失值、异常值处理
1.加载数据
import pandas as pd
data1=pd.read_csv(r'data1.csv',sep=',',encoding='gbk')
data2=pd.read_csv(r'data2.csv',sep=',',encoding='gbk')
data3=pd.read_csv(r'data3.csv',sep=',',encoding='gbk')
2.字段含义
data1.columns=['序号','校园卡号','性别','专业名称','门禁卡号']
data2.columns=['流水号','校园卡号','校园卡编号','消费时间','消费金额','存储金额','余额','消费次数','消费类型','消费项目编码','消费项目序列号','消费操作编码','操作编码','消费地点']
data3.columns=['序号','门禁卡号','进出时间','进出地点','是否通过','描述']
3.查看缺失值
查看缺失值整体情况:
combine=[data1,data2,data3]
for i in combine:
print(i.isna().any())
#运行结果
Index False
CardNo False
Sex False
Major False
AccessCardNo False
dtype: bool
Index False
CardNo False
PeoNo False
Date False
Money False
FundMoney False
Surplus False
CardCount False
Type False
TermNo False
TermSerNo True
conOperNo True
OperNo False
Dept False
dtype: bool
Index False
AccessCardNo False
Date False
Address False
Access False
Describe False
dtype: bool
仅‘data2.csv’存在缺失值,查看'data2.csv’缺失值占比情况:
print(data2.isna().sum()/data2.shape[0]*100)
nul=[i[0] for i in data2.isna().any().iteritems() if i[1]==True]
nul
#运行结果
Index 0.000000
CardNo 0.000000
PeoNo 0.000000
Date 0.000000
Money 0.000000
FundMoney 0.000000
Surplus 0.000000
CardCount 0.000000
Type 0.000000
TermNo 0.000000
TermSerNo 98.601952
conOperNo 99.951672
OperNo 0.000000
Dept 0.000000
dtype: float64
['TermSerNo', 'conOperNo']
'data2.csv’中TermSerNo、conOperNo两列缺失值太多舍去
data2_new=data2[['Index','CardNo','PeoNo','Date','Money','FundMoney','Surplus','CardCount','Type','TermNo','OperNo','Dept']]
二、数据清洗
1.合并表格
df_2=pd.merge(data1, data2_new,how='right',on='CardNo')
df_3=pd.merge(data1, data3,how='right',on='AccessCardNo')
查看缺失值情况
#df_2,df_3两表存在哪些缺失值
combine=[df_2,df_3]
for i in combine:
print(i.isna().any())
#df_2缺失值占比情况
print(df_2.isna().sum()/df_2.shape[0]*100)
nul=[i[0] for i in df_2.isna().any().iteritems() if i[1]==True]
nul
#df_3缺失值占比情况
print(df_3.isna().sum()/df_3.shape[0]*100)
nul=[i[0] for i in df_3.isna().any().iteritems() if i[1]==True]
nul
删除无用数据
#删除通过校园卡号未匹配到的数据
print("删前",df_2.shape)
df_2=df_2.dropna(subset=['AccessCardNo'],how='any')
print("删后",df_2.shape)
#删除通过门禁卡号未匹配到的数据
print("删前",df_3.shape)
df_3=df_3.dropna(subset=['CardNo'],how='any')
print("删后",df_3.shape)
2.格式转换
时间格式转换
#print(df_2.dtypes)
#print(df_3.dtypes)
df_2['Date']=pd.to_datetime(df_2['Date'])
df_3['Date']=pd.to_datetime(df_3['Date'])
df_2["Index_x"] = df_2["Index_x"].astype("int64")
df_2["AccessCardNo"] = df_2["AccessCardNo"].astype("int64")
df_2["Sex"] = df_2["Sex"].astype("object")
df_2["Major"] = df_2["Major"].astype("object")
df_3["Index_x"] = df_3["Index_x"].astype("int64")
df_3["CardNo"] = df_3["CardNo"].astype("int64")
df_3["Sex"] = df_3["Sex"].astype("object")
df_3["Major"] = df_3["Major"].astype("object")
3.生成辅助字段
I:【is_weekday】是否为工作日
from chinese_calendar import is_workday
df_2['Date'].map(lambda x:is_workday(x))
df_2['is_weekday']=df_2['Date'].map(lambda x:is_workday(x))
df_3['Date'].map(lambda x:is_workday(x))
df_3['is_weekday']=df_3['Date'].map(lambda x:is_workday(x))
II:【is_repeat_consumption】是否为重复消费
相隔时间较近的记录算作一次消费行为(不超过五分钟),但消费金额应该是他们的总和
#【第一步】按校园卡号、消费时间对数据进行排序
df_2.sort_values(by=['CardNo','Date'],axis=0,ascending=[True,True],inplace=True)
df_2 = df_2.reset_index(drop=True)
df_2 = df_2.reset_index()
# df_2[:10]
#【第二步】将每次消费记录对应号数、小时、分钟抽取来
date_list=df_2['Date'].to_list()
df_2['day']=[i.day for i in date_list]
df_2['hour']=[i.hour for i in date_list]
df_2['minute']=[i.minute for i in date_list]
df_2['day']=df_2['day'].astype(int)
df_2['hour']=df_2['hour'].astype(int)
df_2['minute']=df_2['minute'].astype(int)
#【第三步】获取重复消费index列表
#学生对应唯一编号列表
stu_no=df_2['CardNo'].value_counts().index.to_list()
#只分析消费类型数据
df_20=df_2[df_2['Type']=='消费']
del_index0=[]
for i in stu_no:
#同一学生所有消费行为
sd=df_20[df_20['CardNo']==i]
day_list=sd['day'].value_counts().index.to_list()
for j in day_list:
#同一学生同一天所有消费行为
dd=sd[sd['day']==j]
for m in range(len(dd)-1):
for n in range(m+1,len(dd)):
h1=dd.iloc[m:m+1,:]['hour'].to_list()[0]
h2=dd.iloc[n:n+1,:]['hour'].to_list()[0]
m1=dd.iloc[m:m+1,:]['minute'].to_list()[0]
m2=dd.iloc[n:n+1,:]['minute'].to_list()[0]
if (h1==h2) & (abs(m1-m2)<=5):
del_index0.append(dd.iloc[n:n+1,:]['index'].to_list()[0])
continue
del_index=list(set(del_index0))
#【第四步】判断并赋值
df_2_2=df_2_1.copy()
df_2_2.insert(df_2_2.shape[1], 'is_repeat_consumption', False)
for l in del_index:
df_2_2.loc[l,'is_repeat_consumption']=True
df_2_1.to_csv(r'G:\jupyter_project\student_consumption\消费综合表.csv',index=False,mode='w',encoding='gbk')
df_3.to_csv(r'G:\jupyter_project\student_consumption\门禁综合表.csv',index=False,mode='w',encoding='gbk')
三、数据分析
任务:
I:分析学生的消费行为和食堂的运营状况,为食堂运营提供建议
II:构建学生消费细分模型,为学校判定学生的经济状况提供参考意见
1.食堂就餐行为分析
1.1 按时间段就餐地点差异分析
I:三餐营业情况分析
仅分析和三餐相关的数据,筛选条件:‘Dept’与就餐相关的地点;去除重复消费’Is Repeat Consumption‘=false;‘Type’=消费

由分析结果可知:
第三、第四食堂营业高峰在午餐和晚餐时段,第二、第五食堂三餐均为主要营业时段,在凌晨到早餐这个时段第二食堂处于营业高峰,说明第二食堂在该时间段相比其他食堂更有竞争力,可能需要合理安排人员保证这段时间的正常营业
II:不同时段营业高峰分析

由分析结果可知不同时段的营业高峰:早餐:7-9点,午餐:11-12点,晚餐:16-18点,各食堂可根据自身情况在高峰期适当增加营业人员,保证高峰期正常运转
1.2 工作日和非工作日就餐时间曲线分析
I:工作日、非工作日就餐时间曲线对比

II:非工作日就餐时间曲线

由上图分析可知,非工作日除早餐高峰期时段有一延长之外和工作日高峰时段基本重合,且在这一时段非工作日人流量较工作日人流量急剧下滑,各食堂在日程安排和物料采购上需注意上述变化,以免不必要的损失
2.学生消费行为分析
import pandas as pd
#取学生的信息表
df2=pd.read_csv(r'data1.csv',encoding='gbk')
#取消费数据表
df=pd.read_csv(r'消费综合表.csv',encoding='gbk')
#消费类型数据
df1=df[df["Type"] == "消费"]
#消费类型且排除重复消费数据
dp=df1[df1["is_repeat_consumption"] == 0]
#计算所有的学生数
stu_num=df1['CardNo'].value_counts().count()
#计算消费的总金额
consum_total=df1['Money'].sum()
#总体人均月平均消费金额
money_average=round(consum_total/stu_num,2)#288.78
#总体人均月平均消费频率
fre_average=round(dp['CardNo'].count()/stu_num,2)#46.77
#计算所有的学生月消费次数
CardNo=dp['CardNo'].value_counts().index.to_list()
count=dp['CardNo'].value_counts().to_list()
df_count=pd.DataFrame({"count":count,'CardNo':CardNo})
#获得每个学生的月消费均值
Mean=df1.groupby('CardNo').mean()['Money'].to_list()
cardno=df1.groupby('CardNo').mean()['Money'].index.to_list()
df_mean=pd.DataFrame({'mean':Mean,'CardNo':cardno})
#合并表格
data1=pd.merge(df_mean,df_count,how='outer',on='CardNo')
data2=pd.merge(df2,data1,how='right',on='CardNo')
data2.to_csv(r'G:\jupyter_project\student_consumption\学生消费详情表.csv',index=False,mode='w',encoding='gbk')
2.1 多维度分析
选择【18电子商务、18工程造价、18工商企管】三个专业做为特征分析对象
从专业角度分析:

工程造价专业学生虽然消费频次最低,但人均消费额最高;工商企管专业学生消费频次和人均消费额均略低于电子商务专业学生
从性别角度分析:

电子商务专业学生男女消费差异很小;工商企管、工程造价专业学生男生消费频次均低于女生、人均消费额均高于女生
2.2 构建模型
根据消费行为挑选消费特征,构建聚类模型,分析每一类群体的消费特点
2.2.1 抽取特征
除去学生性别、所在专业外,学生消费行为分析中已经提取到2个特征即每个学生的月消费均值【mean】和月消费频率【count】,除此之外我们还可以从门禁数据中提取每个学生最常去的地点即按‘Address’中地址信息分组统计排序后取最大值,从每个学生消费数据表中提取月消费总次数即‘CardCount’字段最大值,和消费最常去地方即按‘Dept’字段分组排序取最大值
| Sex | 性别 |
| Major | 专业 |
| mean | 单次消费平均额 |
| count | 一个月的消费数 |
| address_most | 门禁表格中统计的最常去的地方 |
| total_card_comsuption | 校园卡的总共消费次数 |
| consum_most_add | 消费最常去的地方 |
import pandas as pd
df=pd.read_csv(r"学生消费详情表.csv",sep=",",encoding="gbk")
data2=pd.read_csv(r"消费综合表.csv",sep=",",encoding="gbk")
data3=pd.read_csv(r"门禁综合表.csv",sep=",",encoding="gbk")
【feature】address_most
df3=data3.copy()
#正则提取地址
df3['Address']=df3['Address'].str.extract('(.*?)\[')
#地址列表
# add_nama=df3['Address'].value_counts().index.to_list()
#获取门禁id列表
data3_stu=df3['AccessCardNo'].value_counts().index.to_list()
result=[]
for i in data3_stu:
#同一学生所有出入记录
tem_da=df3[df3['AccessCardNo']==i]
#同一学生出入地点对应次数字典
tem_dic=tem_da['Address'].value_counts().to_dict()
result.append(max(tem_dic,key=tem_dic.get))
address_most=pd.DataFrame({'AccessCardNo':data3_stu,'address_most':result})
【feature】total_card_comsuption
df2=data2[(data2["Type"] == "消费")&(data2["is_repeat_consumption"] == 0)]
total_card_comsuption=pd.DataFrame(df2.groupby('CardNo')['CardCount'].max().reset_index())
【feature】consum_most_add
#获取校园卡id列表
data2_stu=df2['CardNo'].value_counts().index.to_list()
result_2=[]
for i in data2_stu:
#同一学生所有消费行为
tem_da = df2[df2['CardNo'] == i]
#消费地点对应次数字典
tem_dic = tem_da['Dept'].value_counts().to_dict()
result_2.append(max(tem_dic, key=tem_dic.get))
consum_most_add=pd.DataFrame({'CardNo':data2_stu,'consum_most_add':result_2})
合并所有【feature】
###合并所有的feature
df_1=pd.merge(df,address_most,on='AccessCardNo',how='left')
df_2=pd.merge(df_1,total_card_comsuption,on='CardNo',how='left')
df_3=pd.merge(df_2,consum_most_add,on='CardNo',how='left')
2.2.2 标量化处理数据
对合并后的数据进行标注化处理,转化为数值型数据
import copy
import numpy as np
#copy.deepcopy()是深拷贝,会拷贝对象及其子对象,有改动也不会影响源数据
deal=copy.deepcopy(df_3)
#删除没有用的列
deal.drop('Index',axis=1,inplace=True)
deal.drop('AccessCardNo',axis=1,inplace=True)
#查看一下每一列数的数据情况
# for col in deal.columns:
# print(f"{col}: \n{deal[col].unique()}\n")
#得到非数据类型(对象类型)的数据的列名。
obj=[i for i in deal.columns if deal[i].dtype=='O']
#['Sex', 'Major', 'address_most', 'consum_most_add']
#定义函数:对其中的不同类型的值转化为对应的数字。
def rep(repname):
element = deal[repname].unique().tolist()
element_dic = dict(zip(element, np.arange(len(element))))
deal[repname] = deal[repname].map(element_dic)
# print(element_dic)
for i in obj:
rep(i)
#定义函数:对每一列的数进行取log(x+1),标准化
def adj(i):
deal[i]=np.log(deal[i]+1)
#查看每列数据间的方差,并把方差大于5的列名输出,同时排除CardNo列(为学号,后期分类不考虑)。
adj_name=[i for i in deal.columns if (deal[i].var()>5)&(i!='CardNo')]
for i in adj_name:
adj(i)
2.2.3 构建K-Means聚类模型
from sklearn.cluster import KMeans
model=KMeans(n_clusters=5, random_state=0).fit(deal)
deal['type']=model.labels_
pd.DataFrame(model.labels_).value_counts()
#聚类的中心
model.cluster_centers_
#得出分类的结果后,添加进原数据列,进行数据的保存导出为'特征表'
final_result=pd.merge(df_3,deal[['CardNo','type']],how='left',on='CardNo')
final_result.to_csv(r'G:\jupyter_project\student_consumption\特征表.csv',index=False,encoding='gbk')
tableau可视化结果:
I:不同类型的消费单价和消费频次情况

以上划分的5种类型对应的消费特征:
1:【消费能力★★★★★】人均消费单价高、刷卡频次较低、总消费次数较低
3:【消费能力★★★★】人均消费单价高、刷卡频次低、总消费次数低
2:【消费能力★★★】人均消费单价较低、刷卡频次较高、总消费次数较低
4:【消费能力★★】人均消费单价较高、刷卡频次高、总消费次数高
0:【消费能力★】人均消费单价低、刷卡频次高、总消费次数高
II:不同类型的消费地点占比情况

III:不同类型常去的地点情况

2.2.4 为学校判定学生的经济状况提供参考意见
通过对0、4、2这三类低消费人群进行行为分析再确定有哪些特征可以纳入助学金批判标准

1万+

被折叠的 条评论
为什么被折叠?



