印象盐城·数创未来大数据竞赛 - 盐城汽车上牌量预测
从本次经历来看这份成绩我还是满意的。
毕竟我并没有像那些大佬们从数据的行列之间进行关联性分析,独立性检验之类的专业技术流操作。我只是个机器学习的小浪花。
通过这次比赛,我明白了要更多的联系社会实际生活的话,还是需要时间序列这块的理论知识和技能手段作支撑才能走的更远。
接下来分享一下大佬做比赛的思路流程,我只是复现。
寒武纪の盐城车牌预测数据初探 之 三
麻婆豆腐
第一步 导入数据
#调包侠
importpandas as pd
importmatplotlib.pyplot as plt
#第一步导入数据
dir ='data/'
train =pd.read_table(dir + 'train_20171215.txt',engine='python')
test_A =pd.read_table(dir + 'test_A_20171225.txt',engine='python')
sample_A =pd.read_table(dir + 'sample_A_20171225.txt',engine='python',header=None)
先查看数据train和test_A的数据样式
print("train.info():")
print(train.info())
print("test_A.info():")
print(test_A.info())
train.info():
RangeIndex: 4773entries, 0 to 4772
Data columns (total 4columns):
date 4773 non-null int64
day_of_week 4773 non-null int64
brand 4773 non-null int64
cnt 4773 non-null int64
dtypes: int64(4)
memory usage: 149.2KB
None
test_A.info():
RangeIndex: 276entries, 0 to 275
Data columns (total 2columns):
date 276 non-null int64
day_of_week 276 non-null int64
dtypes: int64(2)
memory usage: 4.4 KB
None
这里呈现给我们的train和test中字段形式并不一样,test中缺少了品牌即brand和需要预测的数量cnt(这个是合理的,因为需要预测)
通过对题目的阅读,第一赛季只需要预测量而不需要预测具体品牌的量,这样可以理解为,无论白猫还是黑猫,在一起就是两只猫。之后,数据类型是int64,即这个题目给我们的都是数值型的数据。
其中date根据题目,为脱敏数据,brand和cnt也是脱敏,但是dayofweek的值是真是反应星期的数值,首先观察一下这个唯一一个相对而言是真实值的值的形式
print(train['day_of_week'].unique())
print(test_A['day_of_week'].unique())
[3 4 5 6 7 1 2]
[4 5 6 1 2 3 7]
dow(dayofweek)的范围是1-7,可以理解为对应的周一到周日,也就是monday到sunday。
第二步:找出目标值(即需要我们预测的值)
这里,我们需要去观察目标值的范围变化,看一下目标值的大体趋势如何,首先以箱型图去观察一下目标值的变化。
箱形图有5个参数:
下边缘(Q1),表示最小值;
下四分位数(Q2),又称“第一四分位数”,等于该样本中所有数值由小到大排列后第25%的数字;
中位数(Q3),又称“第二四分位数”等于该样本中所有数值由小到大排列后第50%的数字;
上四分位数(Q4),又称“第三四分位数”等于该样本中所有数值由小到大排列后第75%的数字;
上边缘(Q5),表述最大值。
第三四分位数与第一四分位数的差距又称四分位间距。
功能:
1.为了反映原始数据的分布情况,比如数据的聚散情况和偏态。看看《统计学》这本书的插图
2..箱型图有个功能就是可以检测这组数据是否存在异常值。.箱型图有个功能就是可以检测这组数据是否存在异常值。
plt.boxplot(train['cnt'])
plt.show()
根据这个图,结合1所示的例子,大概可以估计出数据是右偏分布,以正态分布的角度观察,异常值存在于大于1000的地方,之后,绘制一个分布图,观察一下数据的分布。
importseaborn as sns
color =sns.color_palette()
sns.set_style('darkgrid')
from scipyimport stats
fromscipy.stats import norm, skew
sns.distplot(train['cnt'],fit=norm)
源代码,但是自己就是得不到结果(下图是大佬原图)
通过绘制分布图,可以看出来数据分布确实符合右偏分布,这里大概初步了解数据的分布尺度在 0 到 2000 左右,且在0-500/1000的数量最密集。
第三步:找出与目标最相关的变量X(即非目标值中找到与目标最相关的值)
因为数据本身与时间相关,所以我们可以绘制一下随脱敏时间和星期的变化
plt.plot(train['date'],train['cnt'])
plt.show()
结合箱图和分布图,可以确定,密集区域集中中500,之后我们以具体的数字反应意思刚才的图的信息。
print(train['cnt'].describe())
count 4773.000000
mean 380.567358
std 252.720918
min 12.000000
25% 221.000000
50% 351.000000
75% 496.000000
max 2102.000000
Name: cnt, dtype: float64
2)预测结果以mean square error作为评判标准,具体公式如下:
这里,可以以统计数据去确定一下,这些统计数据,在评测函数的指标。
fromsklearn.metrics import mean_squared_error
train['25%']= 221
train['50%']= 351
train['75%']= 496
train['median']= train['cnt'].median()
train['mean']= train['cnt'].mean()
print(mean_squared_error(train['cnt'],train['25%']))
print(mean_squared_error(train['cnt'],train['50%']))
print(mean_squared_error(train['cnt'],train['75%']))
print(mean_squared_error(train['cnt'],train['median']))
print(mean_squared_error(train['cnt'],train['mean']))
89316.2231301
64728.7100356
77179.1761995
64728.7100356
63854.4813732
可以大概看出来,由于存在异常点较多,导致统计量在时间轴上的表现并不是那么理想。现在还可以用的信息,只剩下了星期了,救命稻草之星期信息。
开始对星期信息统计,分别分析周一周五的分布情况
monday =train[train['day_of_week']==1]
plt.plot(range(len(monday)),monday['cnt'])
plt.show()
很明显了,可以把1-5和6,7分为两组去分析
简单分析一下按照星期的评测分数
res =train.groupby(['day_of_week'],as_index=False).cnt.mean()
xx =train.merge(res,on=['day_of_week'])
print(xx.head())
print('mse:',mean_squared_error(xx['cnt_x'],xx['cnt_y']))
mse明显小于之前的结果,所以这里暂时可以估计,以星期去统计分布。
# 因为第一赛季只是预测与时间相关的cnt的数量
# 所以可以对数据以dat和dow进行数据合并
train =train.groupby(['date','day_of_week'],as_index=False).cnt.sum()
plt.plot(train['day_of_week'],train['cnt'],'*')
plt.show()
这样很明显,看到在合并品牌之后,观察星期的分布情况,这样的观察,是观察周1-7的cnt的分布情况,可以初步认为距离密集区域较远的为异常数据。
for i inrange(7):
tmp = train[train['day_of_week']==i+1]
plt.subplot(7, 1, i+1)
plt.plot(tmp['date'],tmp['cnt'],'*')
plt.show()
从上往下分别是1,2,3,4,5,6,7这样就很清楚的看见了。
训练集和测试集的分布
这样,我们首先要做的就是线下的验证机,模拟线上的数据。
此时的数据就是按星期聚类之后的数据集。
xx_train =train[train['date']<=756]
xx_test =train[train['date']>756]
print('testshape',xx_test.shape)
print('trainshape',xx_train.shape)
方案零:均值大法(原始数据验证)
# 线下统计每周的均值数据,不加权
xx_train =xx_train.groupby(['day_of_week'],as_index=False).cnt.mean()
xx_result =pd.merge(xx_test,xx_train,on=['day_of_week'],how='left')
print('xx_resultshape',xx_result.shape)
print(xx_result)
print(mean_squared_error(xx_result['cnt_x'],xx_result['cnt_y']))
查看周一到周日的情况,其mse得分如下所示
for i inrange(7):
tmp =xx_result[xx_result['day_of_week']==i+1]
print('周%d'%(i+1),mean_squared_error(tmp['cnt_x'],tmp['cnt_y']))
感觉好差,所以要进一步优化结果
查看一下我们划分的线下数据的方差情况,说明数据的波动很明显,又是是周日的数据,根据前面的图,可以看出,数据中的异常点分布,看起来规律并不明显。而且,周日的数据本身就存在缺失,这种情况下。根据图分布可以看出来。
方案一:加权平均大法
这个方案主要是采取历史纪录*一个权值(可选函数为反比例函数,指数函数和简单的递减函数)
最后以之前分析的星期为周期,进行权重融合,求得最后结果。
def xx(df):
df['w_cnt'] = (df['cnt'] * df['weight']).sum() / sum(df['weight'])
return df
xx_train =train[train['date']<=756]
xx_train['weight'] =((xx_train['date'] + 1) / len(xx_train)) ** 6
xx_train =xx_train.groupby(['day_of_week'],as_index=False).apply(xx).reset_index()
xx_test =train[train['date']>756]
print('test shape',xx_test.shape)
print('trainshape',xx_train.shape)
# #
from sklearn.metrics importmean_squared_error
# # 这里是加权的方案
xx_train =xx_train.groupby(['day_of_week'],as_index=False).w_cnt.mean()
xx_result =pd.merge(xx_test,xx_train,on=['day_of_week'],how='left')
print('xx_resultshape',xx_result.shape)
print(xx_result)
print(mean_squared_error(xx_result['cnt'],xx_result['w_cnt']))
test shape (276, 3)
train shape (756, 6)
xx_result shape (276,4)
date day_of_week cnt w_cnt
0 757 6 314 419.121951
1 758 1 3309 2593.503011
2 759 2 1948 2615.940149
3 760 3 1722 2285.466506
4 761 4 1520 1839.909973
5 762 5 2232 1928.241036
6 763 6 497 419.121951
7 764 1 2037 2593.503011
8 765 2 2246 2615.940149
9 766 3 1447 2285.466506
10 767 4 1478 1839.909973
11 768 5 1631 1928.241036
12 769 6 128 419.121951
13 770 1 2102 2593.503011
14 771 2 2114 2615.940149
15 772 3 1964 2285.466506
16 773 4 1427 1839.909973
17 774 5 1416 1928.241036
18 775 6 319 419.121951
19 776 1 2147 2593.503011
20 777 2 1925 2615.940149
21 778 3 1668 2285.466506
22 779 4 1692 1839.909973
23 780 5 1517 1928.241036
24 781 6 381 419.121951
25 782 1 2327 2593.503011
26 783 2 1926 2615.940149
27 784 3 1387 2285.466506
28 785 4 1533 1839.909973
29 786 5 1946 1928.241036
.. ... ... ... ...
246 1003 4 1618 1839.909973
247 1004 5 2259 1928.241036
248 1005 6 426 419.121951
249 1006 1 2203 2593.503011
250 1007 2 2344 2615.940149
251 1008 3 2392 2285.466506
252 1009 4 1870 1839.909973
253 1010 5 1772 1928.241036
254 1011 6 610 419.121951
255 1012 1 2437 2593.503011
256 1013 2 2326 2615.940149
257 1014 3 1954 2285.466506
258 1015 4 1569 1839.909973
259 1016 5 1777 1928.241036
260 1017 6 442 419.121951
261 1018 1 2476 2593.503011
262 1019 2 1934 2615.940149
263 1020 3 2048 2285.466506
264 1021 4 1586 1839.909973
265 1022 5 2268 1928.241036
266 1023 6 506 419.121951
267 1024 1 3439 2593.503011
268 1025 2 3208 2615.940149
269 1026 3 2277 2285.466506
270 1027 4 2144 1839.909973
271 1028 5 2519 1928.241036
272 1029 6 195 419.121951
273 1030 2 4003 2615.940149
274 1031 3 2513 2285.466506
275 1032 4 1306 1839.909973
[276 rows x 4columns]
828419.30779
可以发现,加权后的结果好于直接均值的效果,其思想考虑了近期影响大于远期影响。其实这个线下分数,只能算是一个开始,只要是模型的线下,理论应该会好于这个结果。
根据预测结果估计,可以预测到,每条数据的偏差应该在1000左右,其实这样而言,明显是差别很大。(⊙﹏⊙)接下来,要采取一些方案处理一下原始数据。
暂时这样吧,大概的两个过程是这样,应该还可以优化,如果有问题和意见可以留言,一起改善。 现在看起来,需要补充一下数据,对周日的数据补充一下。暂时思路吧。80多万的mse,说明每条数据平均和真实值的差距在1000左右。
在大佬的思路支撑下,我就按照大佬的数据处理思路,对data和day_of_week,在日期上按照星期进行训练数据的聚类。
XGBT线下测试成绩是最好的,但是能力有限并没有解决模型不能测试的问题。
最后采用了lgm+gbdt的方法进行优化,从结果上有了不小的提升。
这次比赛自己收获了一些心得,期待以后能走的更远。
为了方便大家 数据集百度网盘的链接
链接: https://pan.baidu.com/s/14clDc4O71L76rduwIeEZQA 提取码: gm1y