机器学习-共享单车使用量统计预测项目

  • Post author:
  • Post category:其他




共享单车租赁量预测




一个项目最重要的可能不是代码,而是项目的流程和实际项目情况


首先这个项目的意义是什么,对使用量的预测,能够帮助企业精确投放,来优化客户体验,节约成本。不做没有意义的投放,这就是实际的需求。

这是一个关于自行车租赁预测的题目,相当于国内的ofo,摩拜单车 通过对数据的预测,可以观测到共享单车在某个时间段的租赁数量,如果预测值和实际租赁值存在很大差异,就应该去调查情况,从而保证共享单车的正常运营


适用场景:



回归

场景,需要对数据进行分析处理

import pandas as pd;
import numpy as np
import pylab
import calendar
import numpy as np
import pandas as pd
import seaborn as sn # matplotlib更高级封装的库
from scipy import stats
import missingno as msno #缺失值可视化处理
from datetime import datetime
import matplotlib.pyplot as plt
import warnings
from sklearn.model_selection import GridSearchCV
pd.options.mode.chained_assignment = None # 避免pandas报错
warnings.filterwarnings("ignore") # 忽略警告
%matplotlib inline



一、检视数据信息

1.帮助我们查看字段的特征和字段的名称

2.查看是否存在所谓的缺失值

3.数据的分布状态

本应用train和test两个数据集,因为kaggle上test数据上需要上网验证效果,所以在这里只使用train进行处理,所有数据都是每个月的前20天的信息

df=pd.read_csv("./input_data/train.csv")


查看样本的所有数据,能够查看数据字段进行分析


第一点,需要对datetime进行处理,

第二点, casual + register = count

在这里插入图片描述

可以通过info方法,查看数据类型,是否有

缺失值

等信息

可以看出,用车量是casual 和registered 的总和

在这里插入图片描述
df.describe()主要查看数据分布状态,可以更好的对数据进行分析,注意下面的mean均值,方差std

在这里插入图片描述
在进行数据分析之前,对数据进行切分,只查看训练集进行数据分析

from sklearn.model_selection import train_test_split
train_df, test_df = train_test_split(df)#将数据集进行切分,训练集和测试集比例为7.5:2.5

查看训练集信息
在这里插入图片描述



二、数据处理


其实算法的选择会比较单一和硬性,但是数据的处理可能和经验有关,留下有用的数据,

去掉垃圾数据

,对于模型的准确度来说是很重要的!


为了能够统一设置数据集,将训练集和测试集设置名称,将进行拼接成整体

all_df统一设置, 后续做预处理时,

需要对训练集和测试集统一处理


因为我们真实状态下观察样本信息时,只能通过训练集观察,如果观察测试集数据,现实工程中不可行,而且会影响模型预测精度

test_df["traintest"]='test';
train_df["traintest"]='train';
all_df=pd.concat((train_df,test_df))#拼接到一起处理

datetime字段比较复杂,将它进行切分处理

date 为日期

month 为英文

monthnum 为中文

lambda表达式,是一种匿名函数,返回值、参数: 方法体



日期的处理可以说是一个典型了!!基本上对于日期时间的处理都可以用这种方法!!



在这里插入图片描述
daynum:为每月第几天

hour: 为小时

weekday: 为周几

all_df["daynum"]=all_df.datetime.apply(lambda x : int(x.split()[0].split('-')[2]))
all_df["hour"] = all_df.datetime.apply(lambda x : int(x.split()[1].split(":")[0]))
all_df["weekday"] = all_df.date.apply(
    lambda dateString : calendar.day_name[datetime.strptime(dateString,"%Y-%m-%d").weekday()])

在这里插入图片描述
根据小时用车辆对数据进行分析

通过图像可以看出,0-6点用车人数少,7-10点用车人数多,11-15为低谷,16-20点为高峰,21-24用车人数少

在这里插入图片描述
数据分层典型处理方法

# 数据分层 1.可以简化模型运算  2.更好的对数据分布情况进行分析计算
def hour_section(hour):
    if hour>=0 and hour<=6:
        return 0
    elif hour>=7 and hour<=10:
        return 1
    elif hour>=11 and hour<=15:
        return 2
    elif hour>=16 and hour<=20:
        return 3
    else :return 4

创建一个新的字段,用于表示用车时间段

all_df["hour_section"]=all_df.hour.apply(hour_section)



三、数据降噪

对数据是否有噪音进行查看

只针对训练集进行降噪,为了更好拟合数据,让模型进度更优

因为测试集就代表未来的数据信息,而未来的数据信息中必定包含噪音,所以不对测试集进行降噪处理


新图



箱形图(Box-plot)又称为盒须图、盒式图或箱线图,是一种用作显示一组数据分散情况资料的统计图。

因形状如箱子而得名。在各种领域也经常被使用,常见于品质管理。它主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比 较。箱线图的绘制方法是:先找出一组数据的最大值、最小值、中位数和两个四分位数;然后, 连接两个四分位数画出箱子;再将最大值和最小值与箱子相连接,中位数在箱子中间。

fig, axes = plt.subplots(nrows=2,ncols=2)
fig.set_size_inches(12, 10)
sn.boxplot(data=all_df.loc[all_df.traintest=='train'],y="count",ax=axes[0][0])
sn.boxplot(data=all_df.loc[all_df.traintest=='train'],y="count",x="season",ax=axes[0][1])
sn.boxplot(data=all_df.loc[all_df.traintest=='train'],y="count",x="hour",ax=axes[1][0])
sn.boxplot(data=all_df.loc[all_df.traintest=='train'],y="count",x="workingday",ax=axes[1][1])

axes[0][0].set(ylabel='Count',title="Box Plot On Count")
axes[0][1].set(xlabel='Season', ylabel='Count',title="Box Plot On Count Across Season")
axes[1][0].set(xlabel='Hour Of The Day', ylabel='Count',title="Box Plot On Count Across Hour Of The Day")
axes[1][1].set(xlabel='Working Day', ylabel='Count',title="Box Plot On Count Across Working Day")

在这里插入图片描述
查看训练集,观察内部是否存在噪音
在这里插入图片描述
查找噪音点(数据点-均值超过该数据标准差的三倍视为噪音),并计算数量

异常点的标准是是看y值离不离群。

outliers=np.abs(all_df.loc[all_df.traintest=='train',["count"]]
                -all_df.loc[all_df.traintest=='train',["count"]].mean()) >
                    (3*all_df.loc[all_df.traintest=='train',["count"]].std()) # 3倍不是硬性规定,是需要根据箱图来去计算的
len(all_df.loc[all_df.traintest=='train'][outliers['count'].values])
109

同样,计算真实数据点个数

goodpoints=np.abs(all_df.loc[all_df.traintest=='train',["count"]]-all_df.loc[all_df.traintest=='train',["count"]].mean()) <=(3*all_df.loc[all_df.traintest=='train',["count"]].std())

len(all_df.loc[all_df.traintest=='train'][goodpoints['count'].values])
8055

只保留训练集中正常样本点和测试集数据,重新构建all_df

查看处理后的数据样本量

在这里插入图片描述



四、数据分析

corr()相关系数,列举出重要特征之间的相关性,判断是否进行删除

一般大于0.6才进行删除

corrMatt = all_df.loc[all_df.traintest=='train',["temp","atemp","casual","registered","humidity","windspeed","count"]].corr()
mask = np.array(corrMatt)
mask[np.tril_indices_from(mask)] = False # 通过索引找数据方式
fig,ax= plt.subplots()
fig.set_size_inches(15,8)
# mask 缺少数值的单元格自动隐藏 , vmax 最大值为80%, square = True 所有单元格为方形, annot=True 注释热图
sn.heatmap(corrMatt, mask=mask,vmax=.8, square=True,annot=True) # matplotlib的高级封装

在这里插入图片描述
分析季节对用车辆的影响

在这里插入图片描述

分析时间对用车辆的影响

在这里插入图片描述

查看小时和季节对用车的影响

在这里插入图片描述

查看星期对用车的影响

在这里插入图片描述

能够看出,周末的用车时段和工作日不同,所以在这里进行数据值修改

def hour_section(hour,weekday):
    if weekday not in ['Saturday','Sunday']:
        if hour>=0 and hour<=6:
            return 0
        elif hour>=7 and hour<=10:
            return 1
        elif hour>=11 and hour<=15:
            return 2
        elif hour>=16 and hour<=20:
            return 3
        else :return 4
    else: # 周六日用车分析
        if hour>=0 and hour <=8 :
            return 5
        elif hour >=9 and hour <=20:
            return 6
        else: return 7

添加一项周末对用车的影响

all_df['hour_week_section']=all_df.apply(lambda row: hour_section(row['hour'], row['weekday']), axis=1)
all_df.info()

查看假期占比

在这里插入图片描述

在这里插入图片描述



五、特征工程

做完这些分析,准备做特征工程,比如归一化,缺失值填充,构造新的特征等。

all_df.info()

在这里插入图片描述
使用pandas做onehot操作 需要让大家进行优化处理,修改为使用sklearn.preprocision 中的OneHotEncoder进行处理

all_df=pd.get_dummies(all_df,columns=['season'])
all_df=pd.get_dummies(all_df,columns=['weather'])

数据标准化处理

import sklearn.preprocessing as preprocessing
scaler = preprocessing.StandardScaler()
temp_scale_param = scaler.fit(all_df[['temp']]) # 温度
all_df['temp_scaled'] = scaler.fit_transform(all_df[['temp']], temp_scale_param)
scaler = preprocessing.StandardScaler() 
atemp_scale_param = scaler.fit(all_df[['atemp']]) # 体感温度
all_df['atemp_scaled'] = scaler.fit_transform(all_df[['atemp']], atemp_scale_param)
scaler = preprocessing.StandardScaler()
humidity_scale_param = scaler.fit(all_df[['humidity']]) # 湿度
all_df['humidity_scaled'] = scaler.fit_transform(all_df[['humidity']], atemp_scale_param)
scaler = preprocessing.StandardScaler()
humidity_scale_param = scaler.fit(all_df[['windspeed']]) # 风速
all_df['windspeed_scaled'] = scaler.fit_transform(all_df[['windspeed']], atemp_scale_param)

对时间进行处理分析

import datetime
def date_diff(date):
    first_new_year=str(date[0:4])+"-01-01 00:00:00"
    next_new_year=str(int(date[0:4])+1)+"-01-01 00:00:00" 
    date = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S')
    first_new_year = datetime.datetime.strptime(first_new_year, '%Y-%m-%d %H:%M:%S')
    next_new_year = datetime.datetime.strptime(next_new_year, '%Y-%m-%d %H:%M:%S')
    if (abs((date-first_new_year).days)) > (abs ((date-next_new_year).days)):
        return (abs((date-next_new_year).days))
    else:return  (abs((date-first_new_year).days))
all_df['date_newyear_num']=all_df.datetime.apply(date_diff)
scaler = preprocessing.StandardScaler()
date_newyear_num_scale_param = scaler.fit(all_df[['date_newyear_num']])
all_df['date_newyear_num_scaled'] = scaler.fit_transform(all_df[['date_newyear_num']], date_newyear_num_scale_param)

独热编码处理

all_df=pd.get_dummies(all_df,columns=['month'])
all_df=pd.get_dummies(all_df,columns=['hour'])
all_df=pd.get_dummies(all_df,columns=['weekday'])
all_df=pd.get_dummies(all_df,columns=['hour_workingday'])
all_df=pd.get_dummies(all_df,columns=['hour_week_section'])
all_df.columns.values
array(['datetime', 'holiday', 'workingday', 'temp', 'atemp', 'humidity',
       'windspeed', 'casual', 'registered', 'count', 'traintest', 'date',
       'monthnum', 'daynum', 'hour_section', 'temp_int', 'season_1',
       'season_2', 'season_3', 'season_4', 'weather_1', 'weather_2',
       'weather_3', 'weather_4', 'temp_scaled', 'atemp_scaled',
       'humidity_scaled', 'windspeed_scaled', 'date_newyear_num',
       'date_newyear_num_scaled', 'month_April', 'month_August',
       'month_December', 'month_February', 'month_January', 'month_July',
       'month_June', 'month_March', 'month_May', 'month_November',
       'month_October', 'month_September', 'hour_0', 'hour_1', 'hour_2',
       'hour_3', 'hour_4', 'hour_5', 'hour_6', 'hour_7', 'hour_8',
       'hour_9', 'hour_10', 'hour_11', 'hour_12', 'hour_13', 'hour_14',
       'hour_15', 'hour_16', 'hour_17', 'hour_18', 'hour_19', 'hour_20',
       'hour_21', 'hour_22', 'hour_23', 'weekday_Friday',
       'weekday_Monday', 'weekday_Saturday', 'weekday_Sunday',
       'weekday_Thursday', 'weekday_Tuesday', 'weekday_Wednesday',
       'hour_workingday_0_0', 'hour_workingday_0_1',
       'hour_workingday_10_0', 'hour_workingday_10_1',
       'hour_workingday_11_0', 'hour_workingday_11_1',
       'hour_workingday_12_0', 'hour_workingday_12_1',
       'hour_workingday_13_0', 'hour_workingday_13_1',
       'hour_workingday_14_0', 'hour_workingday_14_1',
       'hour_workingday_15_0', 'hour_workingday_15_1',
       'hour_workingday_16_0', 'hour_workingday_16_1',
       'hour_workingday_17_0', 'hour_workingday_17_1',
       'hour_workingday_18_0', 'hour_workingday_18_1',
       'hour_workingday_19_0', 'hour_workingday_19_1',
       'hour_workingday_1_0', 'hour_workingday_1_1',
       'hour_workingday_20_0', 'hour_workingday_20_1',
       'hour_workingday_21_0', 'hour_workingday_21_1',
       'hour_workingday_22_0', 'hour_workingday_22_1',
       'hour_workingday_23_0', 'hour_workingday_23_1',
       'hour_workingday_2_0', 'hour_workingday_2_1',
       'hour_workingday_3_0', 'hour_workingday_3_1',
       'hour_workingday_4_0', 'hour_workingday_4_1',
       'hour_workingday_5_0', 'hour_workingday_5_1',
       'hour_workingday_6_0', 'hour_workingday_6_1',
       'hour_workingday_7_0', 'hour_workingday_7_1',
       'hour_workingday_8_0', 'hour_workingday_8_1',
       'hour_workingday_9_0', 'hour_workingday_9_1',
       'hour_week_section_0', 'hour_week_section_1',
       'hour_week_section_2', 'hour_week_section_3',
       'hour_week_section_4', 'hour_week_section_5',
       'hour_week_section_6', 'hour_week_section_7'], dtype=object)
all_df.to_csv("feature_engine.csv")

数据集划分,方便以后查看信息

X=all_df.loc[all_df.traintest=='train',feature_columns].values  # 训练集特征
y_casual=all_df.loc[all_df.traintest=='train'].casual.apply(lambda x: np.log1p(x)).values
y_regstered=all_df.loc[all_df.traintest=='train'].registered.apply(lambda x: np.log1p(x)).values
y_all=all_df.loc[all_df.traintest=='train','count'].values # 训练集标签
X_test=all_df.loc[all_df.traintest=='test',feature_columns].values # 测试集特征
X_date=all_df.loc[all_df.traintest=='test','datetime'].values
y_test=all_df.loc[all_df.traintest=='test','count'].values # 测试集标签

将数据信息进行保存处理

all_df.loc[all_df.traintest=='train',feature_columns].to_csv("X.csv")
all_df.loc[all_df.traintest=='train'].casual.apply(lambda x: np.log1p(x)).to_csv("y_casual.csv")
all_df.loc[all_df.traintest=='train'].registered.apply(lambda x: np.log1p(x)).to_csv("y_regstered.csv")
all_df.loc[all_df.traintest=='train','count'].apply(lambda x: np.log1p(x)).to_csv("y_all.csv")
all_df.loc[all_df.traintest=='test',feature_columns].to_csv("X_test.csv")
all_df.loc[all_df.traintest=='test','datetime'].to_csv("X_date.csv")



六、模型创建及评测

L1正则化, alpha正则项系数选择是最小值,证明L1正则化方式不适合该模型进行精度计算

from sklearn.linear_model import Lasso, Ridge
lasso = Lasso()
param_grid = {'alpha': [1, 0.5, 0.1, 0.01, 0.0001]}
model1 = GridSearchCV(lasso, param_grid=param_grid, cv=10)
model1.fit(X, y_all)
print(model1.best_params_)
print(model1.score(X_test, y_test))
{'alpha': 0.0001}
0.7663301412909473

L2正则,线性回归整体效果不理想

ridge = Ridge()
param_grid = {'alpha': [1, 0.5, 0.1, 0.01, 0.0001]}
model2 = GridSearchCV(ridge, param_grid=param_grid, cv=10)
model2.fit(X, y_all)
print(model2.best_params_)
print(model2.score(X_test, y_test))
{'alpha': 1}
0.7660617181719144


使用集成学习处理算法

集成学习运算效率会非常慢 一个算法将近半小时的时间(4核八线程, 12G内存条)

随机森林效果明显很好

from sklearn.ensemble import RandomForestRegressor
rf = RandomForestRegressor()
param_grid = {
    'n_estimators': [50, 100, 200, 500],
#     'max_depth': [2, 3, 4],
}
model3 = GridSearchCV(rf, param_grid=param_grid, cv=10)
model3.fit(X, y_all)
print(model3.best_params_)
print(model3.score(X_test, y_test))
{'n_estimators': 500}
0.8537900349049669

GBDT 梯度提升树的处理, 效果也不错,不过还是可以继续调参

from sklearn.ensemble import GradientBoostingRegressor
gbdt = GradientBoostingRegressor()
param_grid = {
    'n_estimators': [50, 100, 200, 500],
#     'max_depth': [2, 3, 4]
}
model4 = GridSearchCV(gbdt, param_grid=param_grid, cv=10)
model4.fit(X, y_all)
print(model4.best_params_)
print(model4.score(X_test, y_test))
{'n_estimators': 500}
0.8407082355311021

Adaboost这种模型预测该算法不可行,精度不能达到实际要求

from sklearn.ensemble import AdaBoostRegressor
ab = AdaBoostRegressor()
param_grid = {
    'n_estimators': [50, 100, 200, 500],
#     'max_depth': [2, 3, 4]
}
model5 = GridSearchCV(ab, param_grid=param_grid, cv=10)
model5.fit(X, y_all)
print(model5.best_params_)
print(model5.score(X_test, y_test))
{'n_estimators': 100}
0.5593182730057247



总结,最后,会选择随机森林和梯度提升树进行比较,调参得到最好的模型进行处理



字段分析

人为分析每个字段的含义,之间存在的关联,在数据分析时可以更好的体现数据之间的关联性



字段处理(数据挖掘) 日期处理

本身字段没有使用价值,可以对字段进行拆分或者合并的方式,将字段拆解成更多有利于分析的信息



噪音处理

噪音肯定会影响最终的判断结果,需要将训练集的噪音进行去除,依据 样本-均值和标准差之间的关系



数据分析

分析每个字段和标签之间的关系,以及字段之间是否存在关联



数据预处理

离散值进行onehot处理,连续值进行标准化处理



应用处理

尽可能使用多种模型进行预算,同时使用网格搜索配合大量参数进行拟合,选出最优参数



代码在这呢!!

链接:https://pan.baidu.com/s/1z249OLF58lapp_aV2kCgUQ

提取码:lent

复制这段内容后打开百度网盘手机App,操作更方便哦



版权声明:本文为weixin_43673156原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。