本次研究是参加EMC杯智慧校园开放数据大赛的比赛报告,比赛举办方是交大网络信息中心;比赛数据由上海交通大学网络信息中心和OMNILab联合提供。
整体上来说,本研究要达到以下几个目标:
数据分为以下几个部分:
用户信息(account.txt)中包含了 2014.09.01~2015.01.31 上海交通大学闵行校区使用过校园卡消费的学生信息。包扩卡号(匿名化)、学号(匿名化)、性别、年龄、入学年、学生类型。
交易记录(trade.txt)中包含了 2014.09.01~2015.01.31, 上海交通大学闵行校区的校园卡消费信息。每条交易记录包括消费者卡号、商户号、商户所属系统、交易时间、交易金额。
商户信息(merchant.txt)中包含了上海交通大学大学闵行校区各个商户的信息。包括商户所 属系统代码、商户所属系统名称、商户代码、商户名称、商户地点(有缺失)、 商户成立时间。
上海天气数据(shanghaiqixiang_jiaoda.txt)包含了2014.09.01~2015.01.31,上海交通大学闵行校区的天气数据,留待稍后分析。
首先清理数据中的NA值,发现数据的格式较好,并不存在NA值(除部分商铺地点外),数据格式比较好。为了更好的分析每次持卡消费的用户数据,我们将trade和account数据合并,先查视数据的概况:
## 'data.frame': 7913633 obs. of 11 variables:
## $ fromaccount: Factor w/ 30861 levels "A10261","A11648",..: 1 2 2 2 2 2 2 2 2 2 ...
## $ toaccount : int 1000143 1000003 1000029 1000091 1000108 1000091 1000001 1000011 1000003 1000029 ...
## $ syscode : int 81 30 34 19 267 19 30 30 30 34 ...
## $ timestamp : POSIXlt, format: "2014-09-27 16:25:08" "2014-12-16 11:41:35" ...
## $ amount : int 580 550 250 122 1000 146 516 450 150 250 ...
## $ cat : chr "supermarket" "mensa" "mensa" "bathwater" ...
## $ studentcode: int 36157082 92011843 92011843 92011843 92011843 92011843 92011843 92011843 92011843 92011843 ...
## $ gender : Factor w/ 2 levels "female","male": 1 2 2 2 2 2 2 2 2 2 ...
## $ yearofbirth: int 1988 1997 1997 1997 1997 1997 1997 1997 1997 1997 ...
## $ grade : int 2013 2014 2014 2014 2014 2014 2014 2014 2014 2014 ...
## $ type : Factor w/ 3 levels "undergraduate",..: 3 1 1 1 1 1 1 1 1 1 ...
整理数据后,可以发现有一些错误数据,比如用户的消费金额小于等于0这类数据共有120条;还会有一些用户的单笔消费数据非常之大,消费金额在100~1600元不等;我们先剔除这些异常数据。
接下来画出直方图统计消费金额比较高的频次分布:
由图可以发现,消费金额在20元(2000分)以上的持卡人群十分少。可以考虑从数据中选取消费金额在(0,2000)区间的用户。利用3- σ原则,我们得到有效数据的上限是’μ+3σ’,为2091.491分,约为21元,这也符合可视化后的结果以及我们现实生活中的消费经验。因此我们选取有效数据的上限为2091分。
接下来我们要对数据的概况有所了解,先观察交易记录的概况。如下:
| fromaccount | toaccount | syscode | timestamp | amount |
|---|---|---|---|---|
| E71810 | 1000095 | 22 | 2014-09-01 11:27:39 | 17 |
| B78761 | 1000095 | 64 | 2014-09-01 11:27:39 | 35 |
| E65677 | 1000095 | 64 | 2014-09-01 11:28:05 | 200 |
| D59966 | 1000016 | 28 | 2014-09-01 11:29:30 | 540 |
| A79503 | 1000016 | 28 | 2014-09-01 11:30:03 | 540 |
| E55762 | 1000005 | 28 | 2014-09-01 11:30:05 | 700 |
为了发现校园用户的消费倾向,我们将商户信息分为以下几类:
以饼图展示用户消费分布:
可以看出校园卡消费用户大部分的消费记录都集中在食堂(72.31%)上;此外洗浴及热水(19.23%)和超市(6.86%)消费也占据了一部分,其他的消费记录(其他服务、运动、图书馆)只占据了极小部分(1.60%)。
由图中我们可以可以得出学校持卡用户的总体消费情况:
结合项目的分析目标,我将重点集中在对校园卡持卡用户对餐饮的消费数据。
为了便于餐饮系统进行改进,还要尽可能描述校园卡餐饮消费人群的人群画像,即利用数据分析需求人群的关键字特征,准确的概括消费行为频繁、需求大的用户。我们先看一组不同性别用户消费需求的直方图:
那么不同学历的持卡人群会有怎样的分布呢?
因为SJTU女生比男生数量少(笑),因此要观察男女生消费规律异同,还需要进行合理的采样,不妨选出100000条男生样本和100000条女生样本进行分析,对不同性别的消费数据进行添加扰动绘制散点图如下:
我们可以从图中看出,在食堂的花销方面,女生集中在5-7.5元区间的人比较多,而男生集中在7.5-10元区间的人比较多。但带扰动的散点图只能大概可视化不同性别的集聚区域,因此可以考虑用箱线图更精确的表达,还可以考虑本科、研究生以及博士生的分布:
除去离群点外,可以发现这张图更清晰的表现了男女以及不同学历的用餐消费分布:
可以看出男生的用餐消费均值比女生更大,这也符合我们的直观体验(笑)。
此外还有一点需要注意的就是男生的用餐消费范围普遍比女生更大;也就是说女生的用餐消费区间比较集中。
可以看出男生中,本硕博的用餐消费均值基本保持不变;而女生中硕博则要比本科消费略多,有可能是女生硕士生博士生脑力和体力消耗都比较大,所以营养补充更多(笑)。
综合以上分析,我们可以对餐饮系统提出以下建议:
食堂就餐峰值是指食堂在一天营业过程中所呈现的最多就餐人数;食堂实际容积率是指食堂在一定时期内平均每天接纳就餐人数。 高校用户刷卡行为主要集中在食堂用餐环节,因此通过分析持卡用户的食堂刷卡记录就不难推算出学校食堂容量是否合理,食堂设备资源是否存在闲置浪费。而工作日和双休日的消费峰值一定会存在差异,以闵行第一食堂为例,我们选取11月份(不调休,不补假)中一个工作周的数据加权得到工作日食堂的消费频次数据,如下图所示为工作日的食堂消费峰值曲线图:
## Don't know how to automatically pick scale for object of type ts. Defaulting to continuous
通过对工作日就餐峰值曲线图的分析得知,食堂用餐高峰主要集中在一天当中的11:50 - 12:00时段,每天的早晚就餐时段内均略低于中午的就餐峰值。 从图中我们可以推测学生的早餐用餐情况不佳,有一部分学生没有吃早点的习惯
而周末的数据如下:
## Don't know how to automatically pick scale for object of type zoo. Defaulting to continuous
从图中我们可以发现早餐的高峰推迟到近9点左右,说明很多同学周末喜爱睡懒觉(笑)。而且早、午、晚的就餐峰值也明显低于工作日的数据。
我们还可以得到2014年9月-2015年1月,食堂日消费频次的整体变化趋势,如下图
## Don't know how to automatically pick scale for object of type ts. Defaulting to continuous
从图中我们可以发现图中有几个明显的日均消费频次减少的时间点,有10月初、1月初等,结合2014年下半年SJTU的节假日安排,可以发现这几个时间节点是法定节假日:
## Don't know how to automatically pick scale for object of type ts. Defaulting to continuous
可以发现,食堂的节假日消费与平时有着显著不同,去食堂消费的学生会显著减少。我们推测可能是以下几个原因:
外出游玩,不在校内就餐
周末、假期时,学生喜欢在寝室里休息,与去食堂用餐相比,更倾向于叫外卖
第一种推测很难得到验证;我们先分析第二种可能,为了验证这一结论要得到2014年9月-2015年1月用户的外卖订餐数据,因为时间有限我们采取一种简单的approach去尽可能逼近用户真实的外卖订餐数据,在这里我们采用一些外卖网站的百度指数,来还原一个时期内用户的外卖行为。上海交通大学是饿了么外卖网站的发源地,因此我们不妨以饿了么的搜索量为指标,下图是上海地区2014年9月-2015年1月的“饿了么网上订餐”这一关键词的百度指数变化趋势:
我们可以发现与食堂消费频次数据在周末时出现波谷这一事实相对应的,“饿了么网上订餐”的百度指数在周末时取得峰值;又因为饿了么使用人群大多数是在校大学生,结合身边的生活实际也确实是周末订餐相比于平时工作日更频繁,因此这一关键词确实能一定程度上体现用户的外卖订餐趋势,并且可以做出推测,互联网外卖订餐在周末时会对食堂消费造成影响,分散了部分食堂消费的人群。
但通过观察百度指数的变化趋势我们发现,在10.1这样的法定假日,外卖的搜索量与食堂的消费频次都大幅度降低,这很可能证明了我们的第一个推论,即节假日时学生们更喜欢外出游玩并就餐。
综合上述分析,针对节假日的消费特点,学校后勤部门可以根据节假日用户消费行为的特点按照如下几方面改进:
节假日学生作息时间不规律,食堂应适当延长营业时间,为学生提供中高档菜品,适应学生群体的节假日消费行为特点;
鉴于节假日学生群体的就餐行为较少,食堂可适当地轮休,关闭部分售饭档口,减少工作人员,节约人力开支;
利用上海气象局提供的2014年9月-2015年2月SJTU的气象数据,可以分析气温变化和降水量对食堂消费数据的影响。绘制Missing Map,可以看到天气数据缺少了一段连续序列,整理数据发现为2014年9月6日~9月28日的数据。
我们利用温度变化的趋势平滑拟合缺失数据,可以绘制如下的趋势图:
## Don't know how to automatically pick scale for object of type zoo. Defaulting to continuous
同理可绘制降水量变化的趋势图:
## Don't know how to automatically pick scale for object of type zoo. Defaulting to continuous
我们先来看气温对食堂消费频次的影响,整体来说气温呈现下降趋势的,这与食堂消费频次的变化趋势相吻合,但我们不能说这两者具有相关性,因为我们这很可能是期末考试周的临近以或其他原因导致食堂的消费频次变少,我们先进行相关性分析。要进行相关分析,就需要对气温和降水量的缺失数据进行修补,这里我们同样利用曲线拟合时采用的LOESS模型进行数据的修补。补充后的降水数据和气温数据如下图:
可以看出原却是区域的数据得到了平滑的修补,能体现气温与降水的整体变化趋势。接下来我们分别就气温、降水与食堂消费频次的相关性进行分析。主要分为以下几个步骤:
原始数据标准化
判断两随机变量分布类型,选择适用的相关系数分析
相关系数显著性检验
load("trend_freq_0130.Rdata")
# Sort Data (data frame)
test <- data.frame(
freq = trend.freq1$tf,
water = water,
temp = temp
)
# Scaling Data
test <- scale(test)
# Shapiro-Wilk Test
shapiro.test(water)
##
## Shapiro-Wilk normality test
##
## data: water
## W = 0.3615, p-value < 2.2e-16
shapiro.test(temp)
##
## Shapiro-Wilk normality test
##
## data: temp
## W = 0.9337, p-value = 1.573e-06
shapiro.test(trend.freq1$tf)
##
## Shapiro-Wilk normality test
##
## data: trend.freq1$tf
## W = 0.9187, p-value = 1.489e-07
p值全部小于0.05,因此这三个变量都不服从正态分布,也就是气温-消费频次、降水量-消费频次之间不服从双变量正态分布,皮尔森相关系数(Pearson Correlation Coefficient)不适用,我们考虑用斯皮尔曼相关系数(Spearman’s correlation coefficient)。
# Spearman's Correlation Coefficient
# Precipitation vs. Freq
cor.test(test[,2],test[,1], method="spearman")
##
## Spearman's rank correlation rho
##
## data: test[, 2] and test[, 1]
## S = 631592.4, p-value = 0.3325
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## -0.07913592
可以发现p值 = 0.3325 > 0.05,故降水与消费频次没有相关性。
# Temperature vs. Freq
cor.test(test[,3],test[,1], method="spearman")
##
## Spearman's rank correlation rho
##
## data: test[, 3] and test[, 1]
## S = 428998.5, p-value = 0.0008829
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.2670151
可以发现p值 = 0.0008829 < 0.05,故温度与消费频次有相关性。rho = 0.26,可以看出是正相关,但相关性不强。
与我们平时的认知不符合的是,降水量对学生食堂就餐行为并没有影响;我们仔细查视数据,可以发现,在10月末-12月末,周末的用餐频次呈先降低后升高的趋势,这与降水量10月末-12月末的变化趋势惊人的吻合,如下图:
将这张图与降水量曲线相比较后我们可以得出一个推论,因为周末不需要去上课,学生不会因为客观因素比如上课、出勤等目的去户外,所以此时如果下雨的话,会对学生用餐行为造成一定影响,学生可能懒得冒雨去食堂就餐。也就是说此时降水量对食堂消费行为的影响才开始体现出来。
综合上述分析,我们可以得到结论:
在工作日,降水量对食堂的用餐量并无显著影响,我们推测原因是学校无论是否降水都回正常进行教学任务,因此对学生上课与否并无影响,因此在上下课后的时间依旧会按照习惯去食堂就餐;
在周末,降水量对食堂的用餐量有一定影响,会减少学生的食糖消费行为,我们推测原因是因为学生没有课程时,会因为一定的惰性滞留在寝室等场所,不会冒雨去食堂用餐;
气温对食堂的用餐量有一定影响,即在一定范围内呈正相关。
利用这些数据我们还可以挖掘到更多规律。举例来说,校园卡消费人群具有许多特性,不同人群每周去食堂、超市、洗浴中心的次数,以及在不同场所的花销也不同,但总体来说人群会根据消费习惯的不同形成不同的类别;如宅男可能很少去食堂但经常去超市买泡面、女神晚上不去食堂但经常去洗澡等等……因此我们对校园卡消费人群进行聚类分析。这里我们需要分析消费人群在不同消费场景的总体分布,所以我们除了考虑食堂的消费外,也要考虑在超市及热水洗浴的消费。
聚类分析法是一种无监督的机器学习方法,即根据事物某方面特性把它们划分成为多个类别,使得属于同一类别的个体具有相似的特性或比较高的相似度,而不属于同一类别的个体具有不同的特征或比较低的相似度。同一类别的相似性越大,不同类别间差别越大,聚类效果就越好。因为我们的数据集比较大,而涉及到的数值型的属性比较多(消费数额,消费次数等),所以这里我们选用基于划分的K-Means聚类。
聚类特征的选取很重要,考虑到对消费人群进行画像,我们选取学生在食堂、洗浴中心及超市的消费次数、平均消费金额作为特征。整理好数据集后我们查视一下数据集的格式:
| studentcode | mensa_times | mensa_amount | super_times | super_amount | bath_times | bath_amount | gender | type | age |
|---|---|---|---|---|---|---|---|---|---|
| 11000737 | 2 | 550.0000 | 0 | 0.0000 | 0 | 0.00000 | female | undergraduate | 24 |
| 11001313 | 516 | 684.2907 | 5 | 508.0000 | 166 | 42.47590 | male | undergraduate | 19 |
| 11003224 | 181 | 727.0442 | 22 | 835.0000 | 17 | 150.70588 | female | undergraduate | 20 |
| 11004849 | 137 | 478.2044 | 2 | 530.0000 | 53 | 165.28302 | female | undergraduate | 22 |
| 11005182 | 144 | 622.7917 | 113 | 750.0885 | 129 | 134.20155 | male | undergraduate | 21 |
| 11008939 | 520 | 580.6269 | 41 | 845.3659 | 0 | 0.00000 | male | undergraduate | 20 |
| 11009973 | 23 | 417.2174 | 130 | 863.7231 | 15 | 174.13333 | female | undergraduate | 20 |
| 11010061 | 98 | 640.2245 | 22 | 655.9091 | 87 | 61.49425 | male | undergraduate | 22 |
| 11011413 | 220 | 662.6273 | 5 | 1812.0000 | 36 | 115.50000 | male | undergraduate | 21 |
| 11012018 | 374 | 737.9920 | 0 | 0.0000 | 75 | 137.44000 | male | undergraduate | 20 |
我们从中选取数据的数值特征,为了保证可靠的方差分析,我们先用scale函数对样本特征的取值范围进行归一化,并绘制组内方差图选取合适的K值,下面的图表现了不同K值下组内方差的结果。x轴代表聚类个数,y轴代表平均组内方差。我们可以看到,当聚类数目越大的时候,每组的组内方差就越小。:
source("wssplot.R")
## choose numerical data
ds <- scale(ds.merge[,-c(1,8,9,10)])
wssplot(ds)
可以看出当聚类个数K>5后组内方差下降趋势变缓,同时也保证了K的数目足够小,所以我们选取K=5为聚类个数。
利用R的kmeans函数可以很快实现K-Means,如下:
## k = 5
fit.km <- kmeans(ds, 5, nstart=25)
每一类的样本个数为:
## [1] 9227 3544 6189 6999 1877
建立好聚类模型后,我们看一下这5大类的聚类中心:
## center
knitr::kable(round(aggregate(ds, by=list(cluster=fit.km$cluster), mean), 2))
| cluster | mensa_times | mensa_amount | super_times | super_amount | bath_times | bath_amount |
|---|---|---|---|---|---|---|
| 1 | -0.28 | 0.17 | -0.17 | 0.27 | -0.09 | 0.81 |
| 2 | 0.56 | -0.15 | -0.16 | 0.14 | 1.88 | 0.05 |
| 3 | -1.06 | 0.27 | -0.34 | -0.55 | -0.81 | -1.27 |
| 4 | 1.07 | -0.36 | -0.23 | 0.01 | -0.20 | -0.05 |
| 5 | -0.18 | -0.11 | 3.11 | 0.20 | 0.33 | 0.31 |
我们简单看一下不同性别、学历在各组中的分布情况:
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| female | 3295 | 1509 | 2157 | 1497 | 468 |
| male | 5932 | 2035 | 4032 | 5502 | 1409 |
| 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|
| undergraduate | 5540 | 1535 | 2322 | 3277 | 1849 |
| phd | 1186 | 638 | 1306 | 1268 | 14 |
| master | 2501 | 1371 | 2561 | 2454 | 14 |
得到聚类中心后,我们可以可视化我们的聚类结果,我们不妨比较一下去食堂次数以及去超市次数的聚类散点图:
可以从图中看出形成了比较明显的聚类,即属于第五聚类的人群去超市次数多于其他各类,去食堂次数则相对较少,从生活习性我们可以推断这些人可能是比较喜欢买泡面解决饮食的宅男们。
不妨再比较一下去食堂次数以及去洗浴中心等次数的聚类散点图:
我们从图中可以果断的找出女神人群就是第二聚类,爱洗澡+生活有规律(去食堂)+女生占比大的种类,非女神莫属!
接下来的聚类人群我们可以根据不同性别,不同学历在各组间出现的比例来推测这个组的性质,我们来看一下:
就此我们可以得到这5类人群的画像:
第一聚类:人民群众,去食堂、超市、洗浴中心的次数都比较均衡,是我们广大人民群众中的一员。
第二聚类:女神or男神,这一聚类女生比例较高,去食堂次数十分规律稳定,而且注重个人卫生,经常去洗澡。
第三聚类:奇行种,去食堂、超市、洗浴中心的次数都很少,不知道他们每天都在哪里。
第四聚类:潜力股or学术帝,男生很多,博士生的比例最高,相对于第一类生活更规律,去食堂的次数更多。
第五聚类:宅男,这一聚类男生比例极高,去食堂的次数相对较少,反而经常去超市,推测经常购买的物品一定是泡面纸巾……
看到了这么多奇葩分类,想必许多同学开始跃跃欲试想测一下自己的分类了,我开发了这一应用想必可以满足各位的需求,请点击这里。
因为本次比赛公开了足够多的数据,因此我们可以考虑基于历史数据对上海交通大学闵行校区的学生食堂就餐行为进行预测。基于历史数据对用餐频率进行预测,因为周末的数据和周一至周五的数据有较大差别且周期性不是太强,我们这里这里只考虑工作日的用餐人流量(消费频次)预测。
通过观察几个工作日的数据,我们发现周一到周五总体呈现递减趋势,并且有一定的周期性(午饭、晚饭时段会出现的周期性用餐峰值),即数据具有明显的趋势(Trend)以及季节性变化(Seasonal Variations),我们可以考虑利用时序分析中的霍尔特-温特方法(The Holt-Winters Forecasting Method)来实现对消费频率的预测。
在R语言中,实现预测的方法主要是利用stl包;我们先选择用户就餐比较稳定的一段时间20141103-20141108的工作日数据,将每天05:00~22:00的数据,以5分钟为粒度汇总,即可得到比较标准的时间序列。结合ts包和stl包即可得到比较好的预测效果,注意的是,这里stl的参数选取’periodic’,即fit = stl(dt.ts, ‘periodic’)
最后我们再根据预测数据绘制可视化图表,如下图:
我构建了一个基于shinyapp的应用,已经发布在这里。简单介绍一下该应用,应用的界面如下:
用户可以自行调节时间轴,地图上会显示相应时间食堂的拥挤程度,从绿色至红色显示食堂的容量趋近饱和;点击各个食堂还会显示两个小时区间的食堂消费频次预测;可以通过变化预测时间观察食堂的消费频次变化趋势。
我还利用ECharts+WebService搭建了一个食堂消费人群实时监测系统,数据是利用一天的真实数据动态监测,数据库利用MySql,后端是WebService,前端利用EChart实现,效果如下:
针对用户不同时段在餐厅的消费信息,我们还绘制了用户分布热力图,方便用户在高峰时段选择人群较少的食堂就餐。效果如下:
利用本次比赛开放的数据,我们即可以进行很多有深度的研究,也可以在分析数据的过程中得到很多有趣的结果;我们分析了闵行校区各大食堂的供餐特色,并进一步分析了一些有趣的各项之最,简单绘制了餐饮系统消费人群画像。在这里我们先给出结果,具体的可视化图表我们集成在了本地服务器的网站上。
最受欢迎的食堂非闵行第二食堂莫属。
稍后揭晓女神经常在哪些食堂活动….(我知道你们最关注这个,么么哒)
好基友的定义有很多种,利用餐饮数据,我们简单的定义最佳好基友为帮基友或舍友带饭的童鞋;而好男友简单的定义为每次都会帮女友刷卡的童鞋们。。。他们之间共同的特征是会在连续打若干次饭,并且价钱相近。利用这条规则,我们在找到了20000多次刷卡记录,剔除掉无效的数据(同一时间出现两条数据的、联系两次但价格相差很大的),共有10000余条这样的记录,这说明至少有10000次这样代为打饭的同学。前5名如下所示:
| account | studentcode | gender | yearofbirth | grade | type | |
|---|---|---|---|---|---|---|
| 24436 | D78873 | 76106146 | male | 1995 | 2013 | undergraduate |
| 17896 | C76128 | 17148582 | male | 1991 | 2013 | master |
| 22572 | D66400 | 89059741 | male | 1993 | 2011 | undergraduate |
| 13994 | C44402 | 98140062 | male | 1990 | 2011 | phd |
| 13438 | C37125 | 51051246 | male | 1995 | 2013 | undergraduate |
可以发现清一色都是男生,第一名的童鞋打饭次数达到了一学期19次,一周至少1-2次,还是很勤劳的。
另外,榜单上没有显示的信息是,带打饭次数top10都是男生。。。
显然闺蜜的界定方式也和好基友类似,但较为不同的是基本可以排除情侣的存在,毕竟傲娇的男友很少让女朋友带自己刷卡。。。我们发现带打饭的妹子都很少,一学期带打饭次数超过10次的只有两名女同学:
| account | studentcode | gender | yearofbirth | grade | type | |
|---|---|---|---|---|---|---|
| 16934 | C69634 | 32133473 | male | 1991 | 2014 | phd |
| 7461 | B40021 | 34008326 | male | 1993 | 2011 | undergraduate |
结果已出,紧锣密鼓组织展示中…
结果已出,紧锣密鼓组织展示中…