第一部分 赛题解读
1.1 赛题背景介绍
1.蚂蚁金服拥有上亿会员,每天都涉及大量的资金流入和流出,资金管理压力会非常大。
2.在既保证资金流动性风险最小,又满足日常业务运转的情况下精确地预测资金的流入出情况变得尤为重要。
赛题需要我们根据余额宝用户的申购赎回数据等信息预测未来每日的资金流入流出情况。
1.2 赛题数据介绍
解压数据集后可见五个文件。
1.2.1 用户信息表
用户信息表: user_profile_table 。总共随机抽取了约 3 万用户,其中部分用户在 2014 年 9 月份第一次出现,这部分用户只在测试数据中 。因此用户信息表是约 2.8 万 个用户的基本数据,在原始数据的基础上处理后,主要包含了用户的性别、城市和星座。具体的字段如下表 1 :
1.2.2 用户申购赎回数据表
用户申购赎回数据表: user_balance_table 。里面有 20130701 至 20140831 申购和赎回信息、以及所有的子类目信息, 数据经过脱敏处理。脱敏之后的数据,基本保持了原数据趋势。数据主要包括用户操作时间和操作记录,其中操作记录包括申购和赎回两个部分。金额的单位是分,即 0.01 元人民币。 如果用户今日消费总量为0,即consume_amt=0,则四个字类目为空。
注 1 :上述的数据都是经过脱敏处理的,收益为重新计算得到的,计算方法按照简化后的计算方式处理,具体计算方式在下节余额宝收益计算方式中描述。
注 2 :脱敏后的数据保证了今日余额 = 昨日余额 + 今日申购 - 今日赎回,不会出现负值。
1.2.3 收益率表
收益表为余额宝在 14 个月内的收益率表: mfd_day_share_interest 。具体字段如表格 3 中所示。
1.2.4 上海银行间同业拆放利率(Shibor)表
银行间拆借利率表是 14 个月期间银行之间的拆借利率(皆为年化利率): mfd_bank_shibor 。具体字段如下表格 4 所示:
1.2.5 选手提交结果示例表
选手提交结果表: tc_comp_predict_table
每一行数据是一天对申购、赎回总额的预测值, 2014 年 9 月每天一行数据,共 30 行数据。 Purchase 和 redeem 都是金额数据,精确到分,而不是精确到元。
评分数据格式要求与“选手结果数据样例文件”一致,结果表命名为:tc_comp_predict_table, 字段之间以逗号为分隔符,格式如下图:
1.2.6 余额宝收益计算方式
此处计算方式基于实际余额宝收益计算方法,但是进行了一定的简化。
1.3 赛题任务与评估指标
1.3.1 赛题任务
利用 2013 年7月~2014 年8月的数据, 预测 2014 年9月每一天申购和赎回的总量。
1.3.2 评估指标
(1)传统评估指标
申购总量和赎回均为连续型变量,常采用 MSE (均方误差)、 MAE (平均绝对误差)、 MAPE MAPE(平均绝对相误差)等为评价指标 。
(2)scikit-learn中的各种衡量指标
from sklearn.metrics import mean_squared_error mean_squared_error #均方误差
from sklearn.metrics import mean_absolute_error mean_absolute_error #平均绝对误差
import import numpy numpy as np
#调用
mean_squared_error(y_test, predict)
mean_absolute_error(y_test, predict)
np.mean np.mean((y_test - y_predict) / y_test) #平均绝对误差
(3)赛题评估指标
积分制,每天10分,共300分;使用相对误差,通过得分函数映射得到一个每天预测结果的得分,映射函数未知。具体操作如下:
1.计算所有用户在测试集上每天的申购及赎回总额与实际情况总额的误差。
2.若某天预测误差较小(例如 <0.1, ),则该天可获得满分( 10 分),若某天预测误差较大(例如 >0.3 ),则该天得分为 0。
3.最后公布总积分 = 申购预测得分 *45%+ 赎回预测得分 *55% 。
1.4 赛题方案解析
1.4.1 资金流入流出预测
1.需要先统计每天的申购总额和赎回总额;
2.时间序列预测问题;
3.在比赛前应先查询以往比赛的经典方案。
1.4.2 方案
1.基准方法:临近数据、中位数等;
2.时间序列规则与模型;
3.抽取特征、利用机器学习方法建模。
第二部分 数据分析与探索
2.1 数据探索和准备步骤
2.1.1 时间序列图
1820 年~1869年的太阳黑子数依时间画在下图中:
时间序列图便于观察数据特点,例如,是否具有周期性,震荡幅度等等,是用于观察数据随时间的变化的一种好方法。
2.1.1 数据可视化
2.1.1.1 直方图
2.1.1.2 密度曲线图
直方图的变种:
2.1.1.3 箱型图
箱型图用数据中的五个统计量:最小值、上四分位数(Q3)、中位数、下四分位数(Q1)与最大值来描述数据的一种方法,它也可以粗略地看出数据是否具有有对称性,分布的分散程度等信息,特别可以用于对几个样本的比较。
异常值为:<Q1-1.5IQR;>Q3+1.5IQR
其中IQR = Q3- Q1,为四分位差。
2.1.1.4 小提琴图
小提琴图用来展示类别变量的值的分布情况,宽的地方数据分布多。
• 细黑线代表 95% 置信区间
• 黑色粗条代表四分位数范围
• 白点代表中位数
小提琴图 (Violin Plot)是用来展示多组数据的分布状态以及概率密度。这种图表结合了箱形图和密度图的特征,主要用来显示数据的分布形状。在数据量非常大不方便一个一个展示的时候小提琴图特别适用。
95%置信区间就是说某个统计量落在这个区间里的可能性是95%。
2.1.3 变量间相关性分析与独立性分析
2.1.3.1 相关性分析
• 定类变量:按事物某种属性分类或分组,数字大小代表类别。比如性别。
• 定序变量:不仅分类,还按某种特性排序; 两值的差无意义;如教育程度。
• 定距变量:表现为数值,有单位,可以做加减运算,不能做乘除运算。比如温度。
2.1.3.2 独立性分析/检验
• 变量间无线性相关,还可能存在非线性关联
• 假设 X为连续型变量, Y为离散型变量(有 R种取值)
• 𝐻0:X与Y独立; 𝐻1:X与Y不独立
• 定义 𝐹(𝑋)=𝑃(𝑋≤𝑥),𝐹𝑟(𝑋)=𝑃(𝑋≤𝑥|𝑌=𝑦𝑟)
计算复杂度低,易于实现链接.
分位数表:
2.2 数据分析
主要讲解数据分析中使用到的方法。
2.2.1 读取数据
data_balance = pd.read_csv('user_balance_table.csv')#用户申购赎回数据表
pd.read_csv()默认用逗号分隔符读取数据,并转化为dataframe的格式。
2.2.2 添加时间信息
查看数据后发现日期特征‘report_date’的格式为20140805,需要转化为计算机可读的形式2014-08-05。
data_balance['date'] = pd.to_datetime(data_balance['report_date'], format="%Y%m%d")
to_datetime方法可以解析多种不同的日期表示形式。
然后添加我们需要的时间戳:
data_balance['day'] = data_balance['date'].dt.day
data_balance['month'] = data_balance['date'].dt.month
data_balance['year'] = data_balance['date'].dt.year
data_balance['week'] = data_balance['date'].dt.week
data_balance['weekday'] = data_balance['date'].dt.weekday
需要注意weekday从0开始,week计算日期为今年的第几周。
2.2.3 分组聚合
也就是统计某个特征的总量,可以使用groupby()方法
#统计每天申购总量和赎回总量
total_balance = data_balance.groupby(['date'])['total_purchase_amt', 'total_redeem_amt'].sum()
total_balance.reset_index(inplace=True)#重置索引防止转换后乱序
按‘date’列分组,获取其他列的总和。
2.2.4 绘制时序图
首先是决定绘制图的大小
fig = plt.figure(figsize=(20,6))
然后使用plt.plot()方法绘制曲线
plt.plot(total_balance['date'], total_balance['total_purchase_amt'], label='purchase')
plt.plot(total_balance['date'], total_balance['total_redeem_amt'], label='redeem')
这里第一条曲线日期作为x轴,购买量作为y轴,标签为purchase,即购买量,需要绘制多条曲线按这种形式添加下一行代码即可
相应的,可以设置标签位置,一般让其自动选择最佳位置;以及设置x轴名与y轴名
plt.legend(loc='best')#缺省也可
plt.xlabel('Time')
plt.ylabel('Amount')
需要选择绘制某个时间段的时序图可以使用以下方法
total_balance1 = total_balance[total_balance['date'] >= pd.Timestamp(2014,4,1)]
total_balance2 = total_balance[(total_balance['date'] >= pd.Timestamp(2014,5,1)) & (total_balance['date'] < pd.Timestamp(2014,6,1))]
这里在数据分析中已有相似内容。
2.2.4 其他绘图方式
需要使用seaborn库
import seaborn as sns
绘制小提琴图
seaborn.violinplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, bw='scott', cut=2, scale='area', scale_hue=True, gridsize=100, width=0.8, inner='box', split=False, dodge=True, orient=None, linewidth=None, color=None, palette=None, saturation=0.75, ax=None, **kwargs)
结合箱型图与核密度估计绘图。显示了一个(或多个)分类变量多个属性上的定量数据的分布,从而可以比较这些分布。
虽然很多参数,但是大部分是为了美化,使用时知道传入什么数据就好了。
绘制分布图
seaborn.kdeplot(data, data2=None, shade=False, vertical=False, kernel='gau', bw='scott', gridsize=100, cut=3, clip=None, legend=True, cumulative=False, shade_lowest=True, cbar=False, cbar_ax=None, cbar_kws=None, ax=None, **kwargs)
拟合并绘制单变量或双变量核密度估计图。
实际应用:
a = plt.figure(figsize=(10,10))
scatter_para = {'marker':'.', 's':3, 'alpha':0.3}
line_kws = {'color':'k'}
plt.subplot(2,2,1)
plt.title('The distrubution of total purchase')
sns.violinplot(x='weekday', y='total_purchase_amt', data = total_balance1, scatter_kws=scatter_para, line_kws=line_kws)
plt.subplot(2,2,2)
plt.title('The distrubution of total purchase')
sns.kdeplot(total_balance1['total_purchase_amt'])
plt.subplot(2,2,3)
plt.title('The distrubution of total redeem')
sns.violinplot(x='weekday', y='total_redeem_amt', data = total_balance1, scatter_kws=scatter_para, line_kws=line_kws)
plt.subplot(2,2,4)
plt.title('The distrubution of total redeem')
sns.kdeplot(total_balance1['total_redeem_amt'])
这里还有另一个知识点,就是绘制多个子图。
plt.subplot(2,2,1)
也就是在2×2的第1个区域中绘图,一共有1-4,4个区域,修改区域只需修改第三个数值。
绘制条形图
seaborn.barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=<function mean>, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=0.75, errcolor='.26', errwidth=None, capsize=None, dodge=True, ax=None, **kwargs)
条形图主要展现的是每个矩形高度的数值变量的中心趋势的估计。条形图仅显示均值(或其他估计量),但是在许多情况下,显示分类变量的每个级别上的值分布可能更有益。在这种情况下,其他方法(例如盒子或小提琴图)可能更合适。
实际应用:
# 按每周对数据聚合后取均值
week_sta = total_balance1[['total_purchase_amt', 'total_redeem_amt', 'weekday']].groupby('weekday', as_index=False).mean()
#分析每周的中位数特征
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
sns.barplot(x='weekday', y='total_purchase_amt', data = week_sta, label='Purchase')
plt.legend()
plt.subplot(1,2,2)
sns.barplot(x='weekday', y='total_redeem_amt', data = week_sta, label='Redeem')
plt.legend()
条形图可以和sns.lineplot()分布曲线结合,便于观察趋势
sns.lineplot(x='weekday', y='total_purchase_amt', data = week_sta, label='Purchase')
绘制箱型图
seaborn.boxplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, orient=None, color=None, palette=None, saturation=0.75, width=0.8, dodge=True, fliersize=5, linewidth=None, whis=1.5, notch=False, ax=None, **kwargs)
箱形图(或盒须图)以一种利于变量之间比较或不同分类变量层次之间比较的方式来展示定量数据的分布。图中矩形框显示数据集的上下四分位数,而矩形框中延伸出的线段(触须)则用于显示其余数据的分布位置,剩下超过上下四分位间距的数据点则被视为“异常值”。
实际应用
#绘制每周的箱型图
plt.figure(figsize=(12, 5))
plt.subplot(1,2,1)
sns.boxplot(x='weekday', y='total_purchase_amt',data=total_balance1)
plt.subplot(1,2,2)
sns.boxplot(x='weekday', y='total_redeem_amt',data=total_balance1)
plt.show()
图中看出有几天的数据还是有异常值的。
另外还有一种绘图方式fig,ax = plt.subplots(),也可以用于绘制多个子图。
2.2.5 分析特征相关性
使用了onehot方法
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder()
#执行one-hot编码
week_feature = encoder.fit_transform(np.array(total_balance['weekday']).reshape(-1, 1)).toarray()
#转换为Dataframe对象
week_feature = pd.DataFrame(week_feature,columns=['weekday_onehot']*len(week_feature[0]))
feature = pd.concat([total_balance, week_feature], axis = 1)[['total_purchase_amt', 'total_redeem_amt','weekday_onehot','date']]
feature.columns = list(feature.columns[0:2]) + [x+str(i) for i,x in enumerate(feature.columns[2:-1])] + ['date']
one-hot编码是只有0和1的矩阵,可以查看编码后的week_feature
一共是427×7的矩阵,每一行只有一个数值为1。
我们再查看feature
下面根据这些特征绘制热力图查看相关性
绘制热力图
seaborn.heatmap(data,vmin = None,vmax = None,cmap = None,center = None,robust = False,annot = None,fmt ='。2g',annot_kws = None,linewidths = 0,linecolor ='white',cbar = True,cbar_kws = None,cbar_ax = None,square = False,xticklabels ='auto',yticklabels ='auto',mask = None,ax = None,** kwargs )
绘制矩形数据作为颜色编码矩阵,即使用颜色的方式表示数值。
corr()方法计算相关性,实际应用:
# 画出划分后翌日特征与标签的斯皮尔曼相关性
f, ax = plt.subplots(figsize=(15,8))
plt.subplot(1,2,1)
sns.heatmap(feature[[x for x in feature.columns if x not in ['total_redeem_amt', 'date']]].corr('spearman'), linewidths=0.1, vmax=0.2,vmin=-0.2)
plt.subplot(1,2,2)
sns.heatmap(feature[[x for x in feature.columns if x not in ['total_purchase_amt', 'date']]].corr('spearman'), linewidths=0.1, vmax=0.2,vmin=-0.2)
plt.show()
这里使用了链表推导式
[x for x in feature.columns if x not in ['total_purchase_amt', 'date']]
一个除去了[‘total_purchase_amt’, ‘date’],另一个除去了[‘total_redeem_amt’, ‘date’],然后绘制剩余特征相关性的热力图。
可以看出交易量跟周几还是存在依赖关系的。
查看独立性也可以使用提供的函数方法:
from mvtpy.mvtest import mvtest
mv = mvtest()
total_balance1['weekday'] = total_balance1['date'].dt.weekday
mv.test(total_balance1['total_purchase_amt'], total_balance1['weekday'])
其中的代码来自link.
总结
掌握了许多绘图的方法,通过可视化可以观察异常数据、趋势、相关性等等。
通过分析可以发现,交易日是周几、周末、节假日、月初、月底、某些特殊日子都可能对交易量有所影响。