任务的分析和方法(技术支持结尾联系)
1 任务一:数据预处理与分析
1.1 任务1的解决
任务分析:在data1表中的CardNo,AccessCardNo都不能有重复值,故需对这两列做去重处理。在data2表中的时间在0点到6点之间应剔除,因为在这段时间进行消费是不合理的。
任务方法:
data1表:利用drop_duplicates对指定列做去重处理。
data2表:先找出0点到6点的数据,再在data2表删掉这些数据。
最后将预处理后的data1和data2表根据CardNo为连接点,进行内连接。
2 任务二:数据分析与可视化
2.1 任务2.1的解决
绘制各食堂早上的就餐人次的饼图。
任务分析:操作过程中需要每个时间的天数和小时数,故将时间细分为天数和小时。计算第一食堂早上的就餐人次,其中在短时间连续消费也算作一人,故在同一天的7,8点(早餐时间定义可更改)不管消费多少次都算作一人。
任务方法:利用pivot_table或groupby函数对天数和CardNo二次分组,对Money(可改为其他参数)进行count计数。计算第二食堂早上的就餐人次的方法也如此,故可利用for循环将五个食堂早上的就餐人次计算出来并画图。
可看出就餐人次占比第二食堂占比最大,其次是第五食堂,然后是第一食堂,第三和第四食堂早上基本没有人去就餐,很有可能是因为第三和第四食堂不提供早餐供应。
各食堂午餐、晚餐就餐人次绘制方法同早餐就餐人次。
各食堂午餐的就餐人次的饼图如下:
可以看出占比相差并不会特别大,占比大小顺序为第四食堂>第五食堂>第二食堂>第三食堂>第一食堂。
各食堂晚餐的就餐人次的饼图如下:
可以看出占比大小顺序为第四食堂>第二食堂>第五食堂>第三食堂>第一食堂。午餐和晚餐食堂就餐人次比例相近,其中第四食堂午餐,晚餐人数最多,深受学生喜爱,不提供早餐。第二,第五食堂一整天都有较大的人流量。第一食堂整天都只有比例较小的人流量,学生最不喜欢的食堂,第三食堂不提供早餐的供应,人流量仅高于第一食堂。
2.2 任务2.2的解决
任务分析:任务需要计算食堂工作日的就餐时间曲线,故先提取食堂的数据,用时间得到星期数可提取工作日(星期数为1-5),因用小时所做曲线不够平滑,可以利用分钟数来绘制图形。
任务做法:用groupby对分钟分组,绘制每分钟的Money总数绘制折线图(消费越高,就餐人数就会越多)。
食堂工作日的就餐时间曲线绘制如下:
可以看出早上就餐高峰为7-8点,中午就餐高峰为11.30-12.30,晚上就餐高峰为5.30-6.30。
非工作日就餐时间曲线做法与工作日就餐时间曲线做法相近,绘制如下:
可以看出在工作日是7-10点消费的金额相近也就是就餐人数相近,原因可能是非工作日同学起床时间不统一,中午和晚上的就餐时间大体与工作日就餐时间相近。
2.3 任务2.3的解决
给食堂的意见:
- :适当改进食堂一,使得更多人去该食堂吃饭,增加同学们的选择。
- :可以在食堂三和食堂四增设早餐的服务。
- :在11点前准备好午餐,在17点前准备好晚餐。
- :在工作日时早上7点准备好早餐,早上8点左右就可撤去早餐服务。在非工作日时可适当延长早餐的就餐时间。
3 任务三:生成自动售货机的画像
3.1 任务3.1的解决
任务分析:任务3.1可分为俩个任务,任务①为计算总体的人均刷卡频次和人均消费,任务②为分析不同专业不同性别学生群体的消费特征,可以利用不同专业不同性别学生群体的人均刷卡频次和人均消费来分析消费特征。
任务①做法:先将CardNo进行分组,求出每个人的刷卡次数,再加总,除以个数就得到人均刷卡频次,计算得人均刷卡频次为72.66。人均消费也是先将CardNo进行分组,求出每个人的消费总数,再加总,除以个数就得到人均消费,人均消费为281.16。
任务②做法:对Major和Sex进行分组,得到每个专业不同性别的消费总额和消费频次,再计算每个专业不同性别的人数,就可以计算不同专业不同性别学生群体的人均刷卡频次和人均消费。
不同专业不同性别学生群体的人均刷卡频次柱状图绘图如下;
可以看出大部分专业女生的刷卡频次都略大于男生的刷卡频次。
不同专业不同性别学生群体的人均消费柱状图绘图如
可以看出大部分专业男生的消费总额都略大于女生的消费总额。
3.2 任务3.2的解决
任务分析:在此次事例中,需要衡量学生的消费特点,可借用RFM模型。RFM模型是衡量客户价值和客户创利能力的重要工具和手段。与本次任务要求相近,故可用消费频次和消费总额来评判学生的经济情况。
任务方法:对CardNo进行分组,计算每个人的消费频次和消费总额,对其进行均值方差标准化,并用这两个指标作为聚类标准,给每个学生贴上对应的标签,并根据标签绘制聚类图。
计算可得聚类中心分别为[120.56,27.51],[287.71,75.02],[457,51,121.29] 在[120.56,27.51]聚类中心附近的是label=0的同学,此类同学经济较差,学校可适当对这部分同学给予部分补贴,在[287.71,75.02]聚类中心附近的是label=2的同学,此类同学经济良好,在[457,51,121.29]聚类中心附近的是label=1的同学,此类同学经济富裕。(注:标签并没有顺序之分,在实验中有一个点过于离群,影响聚类效果,已剔除。)
3.3 代码如下:
# %%
"""准备库函数、自定义函数、常量"""
import os
import pandas as pd
import matplotlib.pyplot as plt
import datetime
from collections import Counter
from sklearn.preprocessing import MinMaxScaler
# @github https://github.com/LKI/chinese-calendar
from chinese_calendar import is_workday
from IPython.display import display
# 解决matplotlib无法正常显示中文的问题
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.sans-serif'] = ['STHeiti']
# 默认字体是mac用户的,windows使用下面的这条代码
# plt.rcParams['font.sans-serif'] = ['Simhei']
def time_map(clock: str):
"""判断用餐时段的函数"""
time_limit = [['06:00', '09:30'], ['10:30', '14:30'], ['16:00', '22:00']]
if time_limit[0][0] <= clock < time_limit[0][1]:
return '早餐'
elif time_limit[1][0] <= clock < time_limit[1][1]:
return '午餐'
elif time_limit[2][0] <= clock < time_limit[2][1]:
return '晚餐'
else:
return ''
def isWork(date: list):
"""判断是否工作"""
return is_workday(datetime.date(date[0], date[1], date[2]))
# 制作需要用到的常量
color_map = {
'第一食堂': 'b',
'第二食堂': 'g',
'第三食堂': 'r',
'第四食堂': 'c',
'第五食堂': 'm',
'教师食堂': 'y'
}
# 生成这一个月所有的工作日
counter = Counter(list(map(isWork, [[2019, 4, i] for i in range(1, 31)])))
# %%
"""数据预处理"""
# 创建食堂的数据
canteen = pd.read_csv(os.path.join('data', 'consume.csv')) # 读取数据
canteen = canteen[canteen.Dept.str.contains('食堂')]
# 格式化时间,每十分钟为一个时间段
canteen['Time'] = canteen['Time'].apply(lambda time: time[:-1] + '0')
# 判断是否是工作日
canteen['isWork'] = canteen['Date'].apply(
lambda date: isWork(list(map(int, date.split('/')))))
# 创建用餐时段数据
have_meal = canteen.copy()
have_meal['Meal'] = have_meal['Time'].apply(time_map)
have_meal = have_meal[have_meal.Meal != '']
display(have_meal)
# %%
"""研究早中晚时段各个食堂就餐人次占比"""
for name, group in have_meal.groupby(have_meal['Meal']):
plt.figure(name)
count = []
labels = []
for name_t, group_t in group.groupby(group['Dept']):
if group_t.shape[0] > 10:
count.append(group_t.shape[0])
labels.append(name_t)
explode = tuple(map(lambda x: 0.1 if x == max(count) else 0,
count)) # 创建explode向量,凸显出图中占比最大的部分
colors = list(map(lambda x: color_map[x], labels)) # 固定每个食堂的颜色
plt.pie(count,
explode=explode,
labels=labels,
autopct='%3.1f%%',
colors=colors) # autopct是显示在标签上的数据格式,这里设置了百分比
plt.axis('equal') # 设置横纵坐标相等,使饼图变成圆形
plt.legend(fontsize='small',
bbox_to_anchor=(0, 1.02, 1, 0.2),
loc="upper right") # 设置图例的位置,防止与标签重合
plt.savefig(os.path.join('img', '1-1-' + name + '.png'),
bbox_inches='tight',
dpi=300) # 保存图片
# %%
"""研究每个学生在各个食堂的月人均消费"""
dict_list = []
for name, group in have_meal.groupby(have_meal['Dept']):
dict_t = {'Dept': name}
for name_t, group_t in group.groupby(group['Meal']):
dict_t[name_t + '(人均月消费)'] = group_t.groupby(
group_t['CardNo'])['Money'].agg(['sum']).mean().values[0]
dict_list.append(dict_t)
display(pd.DataFrame(dict_list[0:]))
# %%
"""研究工作日和非工作日食堂就餐时间峰值"""
# 统计出所有时段的客流量
dict_list = []
for name, group in canteen.groupby(canteen['isWork']):
for name_t, group_t in group.groupby([group['Time'], group['Dept']]):
count = 0
for name_tt, group_tt in group_t.groupby(group_t['Date']):
count += group_tt.shape[0]
count /= counter[name]
dict_list.append({
'Type': '工作日' if name else '休息日',
'Dept': name_t[1],
'Time': name_t[0],
'Count': count
})
# 填补缺失的时段数据(如果不填补会导致后面画出来的图像的x轴混乱)
sequence = pd.DataFrame(dict_list[0:])
for name, group in sequence.groupby([sequence['Type'], sequence['Dept']]):
for t in sequence['Time'].unique():
if t not in group['Time'].values:
sequence = sequence.append([{
'Type': name[0],
'Dept': name[1],
'Time': t,
'Count': 0
}])
# 筛选掉基本上无用的时间段
sequence = sequence[sequence['Time'] > '06:00']
# 画图
for name, group in sequence.groupby(sequence['Type']):
print(name)
group.sort_values(by="Time", inplace=True)
plt.figure(name, figsize=(18, 5))
plt.xlabel('时间')
plt.ylabel('平均人数')
for name_t, group_t in group.groupby(group['Dept']):
group_t.sort_values(by="Time", inplace=True)
plt.plot(group_t['Time'],
group_t['Count'],
label=name_t,
c=color_map[name_t])
plt.legend(prop={'size': 20})
# 选装x轴标签,防止重叠
plt.xticks(rotation=60) # 让x轴的标签旋转60度,避免标签重叠
plt.tight_layout() # 让对坐标轴的操作生效
plt.savefig(os.path.join('img', '1-2-' + name + '.png'), dpi=300)
# %%
"""研究食堂物价与学生消费情况的关系"""
dict_list = []
for name, group in have_meal.groupby([have_meal['Dept'], have_meal['Meal']]):
dict_t = {
'Dept': name[0],
'Meal': name[1],
'AvgMoney': group['Money'].agg(['sum'])[0] / group.shape[0],
'Count': group.shape[0]
}
dict_list.append(dict_t)
cost = pd.DataFrame(dict_list[0:])
# print(cost)
# 通过Pearson线性相关系数验证物价越高,学生消费意愿越低
dict_list = []
for name, group in cost.groupby('Meal'):
# 求解Pearson相关系数
std = MinMaxScaler(feature_range=(0, 1)) # 线性处理
group['AvgMoney'] = std.fit_transform(group['AvgMoney'].values.reshape(
-1, 1)) # 因为函数只接收纵向的向量,所以需要reshape变换
group['Count'] = std.fit_transform(group['Count'].values.reshape(-1, 1))
# print(group)
dict_t = {'时间段': name, '线性相关系数': group['AvgMoney'].corr(group['Count'])}
dict_list.append(dict_t)
relationship = pd.DataFrame(dict_list[0:])
display(relationship)
4 任务四:数据可视化分析
针对问题一,我们在对已知的数据进行预处理的基础上,对表二的消费记录进行深入的分析,我们的研究方向主要有三个:早午晚各食堂就餐人次占比的可视化,分析每个食堂的三餐就餐情况;工作日与休息日就餐人数与时间的关系的可视化,分析每个时段食堂的就餐高峰期,还有工作日和休息日的高峰期有什么不同;食堂餐费和食堂就餐人数的关系,从统计的角度出发,通过Pearson线性相关系数来评判两者之间的联系,得出了食堂餐费和食堂就餐人数之间存在比较紧密的负相关现象。
第一食堂三餐需要大幅改进,可以考虑降低价格、增加菜式、改善食堂就餐环境等措施;第二和第五食堂在三餐中都比较受欢迎,可以考虑增开窗口来缓解目前就餐压力;第三、第四食堂的早餐比较冷门,建议只开一个窗口或直接取消;工作日的9:40和16:20存在就餐高峰,食堂要提前做好准备;休息日食堂注意减少供应,以免造成浪费,休息日的早餐持续时间较长,食堂要注意进行保温和分批供应。
针对问题二,通过对学生的消费记录的进一步探究,提出了DFM模型,主要建立在月平均余额、消费频率和月总消费额三个指标上。通过Elbow Method法对K-Means算法的最佳K值进行了分析,然后采用了优化过后的K-Means++方法,对学生进行聚类,在此过程中发现了存在部分异常数据,比如一个月消费次数个位数的人群,经过了进一步清洗后再此聚类,发现结果有了比较大的提升。最终,分析了每个类型学生的消费特点。
通过聚类绘制三维散点图,将学生消费人群大致分成了6类,对低中高等的消费人群进行了初步的分类,简要推测了不同消费行为对应的消费人群。我们定义的DFM模型能初步根据消费特点对学生群体进行分类。
针对问题三,在问题二的模型基础上,我们选择了其中的需要补助的可能性最大的一类人进行了基于AHP的打分,最终得到的区间为1-5分,分数越低说明消费水平比较低,最后根据分数划分群体后,提出梯度补助的策略。
对分值1的人群为重点补助人群,分值2和3的人群为基本补助人群,分值为4的人群可以少量补助,分值为5的人群极有可能是上一个模型的漏网之鱼,不补助。综上可知,我们建立的结合AHP分析的DFM模型具有较高的实际价值。
1.1问题的背景
校园一卡通是集身份认证、金融消费、数据共享等多项功能于一体的信息集成系统。在为师生提供优质、高效信息化服务的同时,系统自身也积累了大量的历史记录,其中蕴含着学生的消费行为以及学校食堂等各部门的运行状况等信息。很多高校基于校园一卡通系统进行“智慧校园”的相关建设,例如《扬子晚报》2016年1月27日的报道:《南理工给贫困生“暖心饭卡补助”》。 不用申请,不用审核,饭卡上竟然能悄悄多出几百元……记者昨天从南京理工大学独家了解到,南理工教育基金会正式启动了“暖心饭卡”项目,针对特困生的温饱问题进行“精准援助”。 项目专门针对贫困本科生的“温饱问题”进行援助。在学校一卡通中心,教育基金会的工作人员找来了全校一万六千余名在校本科生9月中旬到11月中旬的刷卡记录,对所有的记录进行了大数据分析。最终圈定了500余名“准援助对象”。 南理工教育基金会将拿出“种子基金”100万元作为启动资金,根据每位贫困学生的不同情况确定具体的补助金额,然后将这些钱“悄无声息”的打入学生的饭卡中,保证困难学生能够吃饱饭。
——《扬子晚报》2016年1月27日:南理工给贫困生“暖心饭卡补助”
1.2问题的相关信息
附件包含3张数据表,分别为data1.csv、data2.csv、data3.csv。可知如下数据:
表1为学生ID表,给出4341条记录,包含字段为序号、校园卡号、性别、专业名称和门禁卡号。
表2为消费记录表,给出519367条记录,包含字段为流水号、校园卡号、校园卡编号、消费时间、消费金额、存储金额、余额、消费次数、消费类型、消费项目的编码、消费项目的序列号、消费操作的编号、操作编码和消费地点。
表3为门禁记录表,给出43156条记录,包含字段为序号、门禁卡号、进出时间、进出地点、是否通过和描述。
1.3需解决的问题
本赛题提供国内某高校校园一卡通系统一个月的运行数据,请使用附件数据分析和建模,分析学生在校园内的学习生活行为,为改进学校服务并为相关部门的决策提供信息支持。
问题一:分析学生的消费行为和食堂的运营状况,为食堂运营提供建议。
问题二:根据学生的整体校园消费行为,选择合适的特征,构建模型,分析每一类学生群体的消费特点。
问题三:构建学生消费细分模型,为学校判定学生的经济状况提供参考意见。
2.1 直接看效果
微信 | |
![]() | ![]() |