基于机器学习的共享单车需求量影响因素的数据可视化分析


前言

本项目分析了影响单车租赁数的众多因素:年月日期时间、温度、湿度、季节、天气、风速,并对华盛顿地区的单车租赁需求进行预测。
本项目使用的模块:numpy、pandas、matplotlib、seaborn、datatime


一、数据采集与处理

1、数据来源

数据平台:Kaggle,一个数据建模和数据分析竞赛平台。
数据链接:https://www.kaggle.com/c/bike-sharing-demand
数据内容:美国华盛顿共享单车租赁数据,数据提供了跨越两年的每天不同时刻单车租赁数据,包含天气信息和日期信息,训练集(train.csv)由每月前19天的数据组成,测试集(test.csv)是每月第20天到当月底的数据。

data_train = pd.read_csv('train.csv')
data_test  = pd.read_csv('test.csv')

在这里插入图片描述

2、数据预处理

(1)缺失值检查

#缺失值检查
data_train.info()
print('-'*40)
data_test.info()

运行结果:
在这里插入图片描述
表明数据集没有内容缺失
(2)异常值处理
第一,绘制直方图,观察数据分布情况

# 查看是否符合高斯分布
fig,axes = plt.subplots(1,3)
# 设置图形的尺寸,单位为英寸。1英寸等于2.54cm
fig.set_size_inches(18,5)

sns.distplot(data_train['count'],bins=100,ax=axes[0])
sns.distplot(data_train['casual'],bins=100,ax=axes[1])
sns.distplot(data_train['registered'],bins=100,ax=axes[2])

在这里插入图片描述

第二,数据统计信息描述

在这里插入图片描述
第三、箱线图(sns.boxplot函数)观察数据分布情况

fig,axes = plt.subplots(1,3)
fig.set_size_inches(12,6)

sns.boxplot(data = data_train['count'],ax=axes[0])
axes[0].set(xlabel='count')
sns.boxplot(data = data_train['casual'], ax=axes[1])
axes[1].set(xlabel='casual')
sns.boxplot(data = data_train['registered'], ax=axes[2])
axes[2].set(xlabel='registered')

在这里插入图片描述
第四,删除异常值(长尾,大值),并做对数log处理

# 去除异常值 将大于μ+3σ的数据值作为异常值
def drop_outlier(data,col):
    mask = np.abs(data[col]-data[col].mean())<(3*data[col].std())
    data = data.loc[mask]
    # 可视化剔除异常值后的col和col_log
    data[col+'_log'] = np.log1p(data[col])
    f, [ax1, ax2] = plt.subplots(1,2, figsize=(15,6))

    sns.distplot(data[col], ax=ax1)
    ax1.set_title(col+'分布')

    sns.distplot(data[col+'_log'], ax=ax2)
    ax2.set_title(col+'_log分布')
    return data

data_train = drop_outlier(data_train,'count')
data_train = drop_outlier(data_train,'casual')
data_train = drop_outlier(data_train,'registered')

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

二、可视化分析(统计学)

1.特征分解

将datetime特征拆分为日期、星期、年、月、日、小时

#特征分解
def split_datetime(data):
    data['date'] = data['datetime'].apply(lambda x:x.split()[0])
    data['weekday'] =data['date'].apply(lambda x:datetime.strptime(x,'%Y-%m-%d').isoweekday())
    data['year'] = data['date'].apply(lambda x:x.split('-')[0]).astype('int')
    data['month'] = data['date'].apply(lambda x:x.split('-')[1]).astype('int')
    data['day'] = data['date'].apply(lambda x:x.split('-')[2]).astype('int')
    data['hour'] = data['datetime'].apply(lambda x:x.split()[1].split(':')[0]).astype('int')
    return data

data_train = split_datetime(data_train)
data_train.head()
data_train.info()

在这里插入图片描述

2.整体关系图(pairplot)

cols =['season','holiday','workingday','weekday','weather','temp',
       'atemp','humidity','windspeed','hour']

sns.pairplot(data_train ,x_vars=cols,
             y_vars=['casual','registered','count'], 
             plot_kws={'alpha': 0.2})

在这里插入图片描述
可以观察到:
一季度出行人数总体偏少
非假日借车总数比假日借车总数要高
会员在工作日出行多,节假日出行少,临时用户则相反
租赁数量随天气等级上升而减少
温度、湿度对非会员影响较大,对会员影响较小
小时数对租赁情况影响明显,会员呈现两个高峰,非会员呈正态分布

3.相关性分析(heatmap)

#查看各特征与count的相关性
corr = data_train.corr()
plt.subplots(figsize=(14,14))
sns.heatmap(corr,annot=True,vmax=1,cmap='YlGnBu')

在这里插入图片描述

# 降序
np.abs(corr['count']).sort_values(ascending=False)

运行结果:

count             1.000000
registered        0.977642
count_log         0.845294
registered_log    0.839080
casual_log        0.758863
casual            0.711105
hour              0.442659
temp              0.376656
atemp             0.372705
humidity          0.307362
month             0.176115
season            0.173657
year              0.171519
weather           0.123578
windspeed         0.116767
workingday        0.046246
weekday           0.022100
day               0.015243
holiday           0.002421
Name: count, dtype: float64

可以看出特征对count的影响力度分别为:
hour(时段)>temp(温度)>atemp(体感温度)>humidity(湿度)>month(月份)>season(季节)>year(年份)>weather(天气等级)>windspeed(风速)>workingday(工作日)>weekday(星期几)>day(天数)>(holiday)节假日

4.参看各因素与count的关系

(1)hour

# hour总体变化趋势
date = data_train.groupby(['hour'], as_index=False).agg({'count':'mean',
                                                   'registered':'mean',  
                                                   'casual':'mean'})
fig = plt.figure(figsize=(18,6))
ax = fig.add_subplot(1,1,1)
# 使用总量
plt.plot(date['hour'], date['count'], linewidth=1.3)
# 会员使用量
plt.plot(date['hour'], date['registered'], linewidth=1.3)
# 非会员使用量
plt.plot(date['hour'], date['casual'], linewidth=1.3)
plt.legend()

在这里插入图片描述

# 工作日与非工作日下,hour与count的关系
date = data_train.groupby(['workingday','hour'], as_index=False).agg({'count':'mean',
                                                   'registered':'mean',  
                                                   'casual':'mean'})

mask = date['workingday'] == 1

workingday_date= date[mask].drop(['workingday','hour'],axis=1).reset_index(drop=True)
nworkingday_date = date[~mask].drop(['workingday','hour'],axis=1).reset_index(drop=True)

fig, axes = plt.subplots(1,2,sharey = True)
workingday_date.plot(figsize=(15,5),title ='working day',ax=axes[0])
axes[0].set(xlabel='hour')
nworkingday_date.plot(figsize=(15,5),title ='nonworkdays',ax=axes[1])
axes[1].set(xlabel='hour')

在这里插入图片描述
可以看出:
*工作日
会员用户(registered)上下班时间是两个用车高峰,而中午也会有一个小高峰,猜测可能是外出午餐的人。
临时用户(casual)起伏比较平缓,高峰期在17点左右。
会员用户(registered)的用车数量远超临时用户(casual)。
*非工作日
租赁数量(count)随时间呈现一个正态分布,高峰在12点左右,低谷在4点左右,且分布比较均匀。

(2)temp

# 按温度取平均值
temp = data_train.groupby(['temp'], as_index=True).agg({'count':'mean',
                                                   'registered':'mean',  
                                                   'casual':'mean'})
temp.plot(figsize=(10,5),title='温度与count的变化趋势')


在这里插入图片描述
(3)humidity

# 湿度
humidity = data_train.groupby(['humidity'], as_index=True).agg({'count':'mean',
                                                   'registered':'mean',  
                                                   'casual':'mean'})
humidity.plot(figsize=(10,5),title='湿度与count的变化趋势')

在这里插入图片描述
(4)month

# 月使用量变化趋势
date = data_train.groupby(['month'], as_index=False).agg({'count':'mean',
                                                   'registered':'mean',  
                                                   'casual':'mean'})

fig = plt.figure(figsize=(18,6))
ax = fig.add_subplot(1,1,1)
plt.plot(date['month'], date['count'] , linewidth=1.3 , label = '使用总量' )
plt.plot(date['month'], date['registered'] , linewidth=1.3 , label = '会员使用量' )
plt.plot(date['month'], date['casual'] , linewidth=1.3 , label = '非会员使用量' )
plt.legend()

在这里插入图片描述
(5)season

day_df=data_train.groupby('date').agg({'year':'mean','season':'mean',
                                      'casual':'sum', 'registered':'sum'
                                      ,'count':'sum','temp':'mean',
                                      'atemp':'mean'})
season_df = day_df.groupby(['year','season'], as_index=True).agg({'casual':'mean', 
                                                                  'registered':'mean',
                                                                  'count':'mean'})
temp_df = day_df.groupby(['year','season'], as_index=True).agg({'temp':'mean', 
                                                                'atemp':'mean'})

fig = plt.figure(figsize=(10,10))
xlables = season_df.index.map(lambda x:str(x))

ax1 = fig.add_subplot(2,1,1)
ax1.set_title('这两年count随季节的总体趋势')
plt.plot(xlables,season_df)
plt.legend(['casual','registered','count'])

ax2 = fig.add_subplot(2,1,2)
ax2.set_title('这两年count随季节的总体趋势')
plt.plot(xlables,temp_df)

plt.legend(['temp','atemp'])

在这里插入图片描述

无论是临时用户还是会员用户用车的数量都在秋季迎来高峰,而春季度用户数量最低

(6)weather

weather_df = data_train.groupby('weather',as_index=True).agg({'casual':'mean',
                                                              'registered':'mean'})
weather_df.plot.bar(stacked=True)

在这里插入图片描述

(7)windspeed

# 风速 
# 化为整数型数据
data_train['windspeed'] = data_train['windspeed'].astype(int)
windspeed = data_train.groupby(['windspeed'], as_index=True).agg({'count':'mean',
                                                                  'registered':'mean',  
                                                                  'casual':'mean'})
windspeed.plot(figsize=(10,8))

在这里插入图片描述
(8)日期(直方图)
在这里插入图片描述
在这里插入图片描述

三、基于机器学习的模型预测

1.特征工程

首先,数据异常值处理,并取对数

train = pd.read_csv('train.csv')
test  = pd.read_csv('test.csv')

#训练集去除3倍方差以外数据
train_std = train[np.abs(train['count']-train['count'].mean())<=(3*train['count'].std())]

train_std.reset_index(drop=True,inplace=True)
train_std.shape
test.shape
#对数据进行对数变换后的分布,特别注意后面预测数据时需要取指数变换
ylabels = train_std['count']
ylabels_log = np.log(ylabels)
sns.distplot(ylabels_log)

其次,多类别型数据转为二分型类别(one-hot)
机器学习算法无法直接用于数据分类。数据分类必须转换为数字二进制才能进一步进行。one-hot编码是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值。然后,每个整数值被表示为二进制向量,除了整数的索引之外,它都是零值,它被标记为1。get_dummies 是利用pandas实现one hot encode的方式。

#利用one-hot将多类别型数据转为二分型类别
combine_feature = combine_train_test[['temp','humidity','weather','season','year','weather',
                                      'month','weekday','hour','workingday','windspeed','count']]

combine_feature.info()
cols = ['month','season','weather','year']
combine_feature = pd.get_dummies(combine_feature,columns=cols,prefix_sep='_')

combine_feature.info()

最后,划分数据集
原始数据:
在这里插入图片描述
利用train_test_split随机分配train_data为新的训练集和数据集

在这里插入图片描述
在这里插入图片描述

train_X, test_X, train_y, test_y = train_test_split(source_X,
                                                    source_y,
                                                    train_size = 0.80)

2.随机森林算法

#模型一随机森林
from sklearn.ensemble import RandomForestRegressor
# 模型参数
forest_parmas = {'n_estimators':[1300,1500,1700], 'max_depth':range(20,30,4)}

Model = RandomForestRegressor(oob_score=True,n_jobs=-1,random_state = 42)

Model = get_best_model_and_accuracy(Model,forest_parmas ,train_X, train_y)

Model=Model.best_estimator_  
RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=24,
           max_features='auto', max_leaf_nodes=None,
           min_impurity_decrease=0.0, min_impurity_split=None,
           min_samples_leaf=1, min_samples_split=2,
           min_weight_fraction_leaf=0.0, n_estimators=1500, n_jobs=-1,
           oob_score=True, random_state=42, verbose=0, warm_start=False) 
# 分类问题,score得到的是模型的正确率
Model.score(test_X,test_y)
# 袋外分数
Model.oob_score_ 
# 模型保存
from sklearn.externals import joblib
joblib.dump(Model, "rf.pkl", compress=9)#将模型保存为rfpkl
from sklearn.metrics import mean_absolute_error
pre_y = Model.predict(test_X.values)#预测
pre_y = np.exp(pre_y)#这里格外注意,因为特征工程中将count值取对数,所以这里指数变换
test_y = np.exp(test_y)#这里格外注意,因为特征工程中将count值取对数,所以这里指数变换,测试集真实值

构建模型
模型训练,优化参数
在这里插入图片描述
模型预测
在这里插入图片描述

时间:电脑程序运行时间5min
正确率:

# 分类问题,score得到的是模型的正确率
Model.score(test_X,test_y)

0.9544882922113979

3.xgboost模型预测

#模型二xgboost
import sys
sys.path.append("D:/python/Anaconda3/Lib/site-packages")#添加python搜索路径
import xgboost
import xgboost as xg
# 模型参数  subsample:对于每棵树,随机采样的比例
xg_parmas = {'subsample':[i/10.0 for i in range(6,10)],
            'colsample_bytree':[i/10.0 for i in range(6,10)]} # 控制每棵随机采样的列数的占比

xg_model = xg.XGBRegressor(max_depth=8,min_child_weight=6,gamma=0.4)
xg_model = get_best_model_and_accuracy(xg_model,xg_parmas,train_X.values, train_y.values)
xg_model=xg_model.best_estimator_
xg_model
xg.XGBRegressor(base_score=0.5, booster=None, colsample_bylevel=1,
       colsample_bynode=1, colsample_bytree=0.9, gamma=0.4, gpu_id=-1,
       importance_type='gain', interaction_constraints=None,
       learning_rate=0.300000012, max_delta_step=0, max_depth=8,
       min_child_weight=6, missing=None, monotone_constraints=None,
       n_estimators=100, n_jobs=0, num_parallel_tree=1,
       objective='reg:squarederror', random_state=0, reg_alpha=0,
       reg_lambda=1, scale_pos_weight=1, subsample=0.9, tree_method=None,
       validate_parameters=False, verbosity=None)
from sklearn.metrics import mean_absolute_error
pre_y = xg_model.predict(test_X.values)#预测
pre_y = np.exp(pre_y)#这里格外注意,因为特征工程中将count值取对数,所以这里指数变换

在这里插入图片描述

Best Accuracy: 0.9519465121003385
Best Parameters: {‘colsample_bytree’: 0.9, ‘subsample’: 0.9}
Average Time to Fit (s): 1.136
Average Time to Score (s): 0.019

**对比发现,随机森林正确率更高。

四、GUI界面设计

# 创建GUI窗口打开图像 并显示在窗口中

from PIL import Image, ImageTk # 导入图像处理函数库
import tkinter as tk           # 导入GUI界面函数库
import time

# 创建窗口 设定大小并命名
window = tk.Tk()
window.title('共享单车租赁数量影响因数可视化分析系统')
window.geometry('600x900')
global img_png6 
global img_png1
global img_png2
global img_png3
global img_png4
global img_png5
var = tk.StringVar()    # 这时文字变量储存器
global img_png7
# 创建打开图像和显示图像函数
def Open_Img():
    global img_png1
    global img_png2
    global img_png3
    global img_png4
    global img_png5
    var.set('已打开')
    Img1 = Image.open('humidity.gif')
    img_png1 = ImageTk.PhotoImage(Img1)
    Img2 = Image.open('temp.gif')
    img_png2 = ImageTk.PhotoImage(Img2)
    Img3 = Image.open('weather.gif')
    img_png3 = ImageTk.PhotoImage(Img3)
    Img4 = Image.open('weekday.gif')
    img_png4 = ImageTk.PhotoImage(Img4)
    Img5 = Image.open('working_day.gif')
    img_png5 = ImageTk.PhotoImage(Img5)

def Show_Img():
    global img_png1
    global img_png2
    global img_png3
    global img_png4
    global img_png5
    var.set('已显示')   # 设置标签的文字为 'you hit me'
    label_Img = tk.Label(window, image=img_png1)
    label_Img.place(x=150,y=190,height=100,width=300)
    label_Img = tk.Label(window, image=img_png2)
    label_Img.place(x=150,y=310,height=100,width=300)
    label_Img = tk.Label(window, image=img_png3)
    label_Img.place(x=150,y=430,height=100,width=300)
    label_Img = tk.Label(window, image=img_png4)
    label_Img.place(x=150,y=550,height=100,width=300)
    label_Img = tk.Label(window, image=img_png5)
    label_Img.place(x=150,y=670,height=200,width=300)
# 创建文本窗口,显示当前操作状态
Label_Show = tk.Label(window,
    textvariable=var,   # 使用 textvariable 替换 text, 因为这个可以变化
    bg='blue', font=('Arial', 12), width=15, height=2)
Label_Show.pack()
# 创建打开图像按钮
btn_Open = tk.Button(window,
    text='打开图像',      # 显示在按钮上的文字
    width=15, height=2,
    command=Open_Img)     # 点击按钮式执行的命令
btn_Open.pack()    # 按钮位置
# 创建显示图像按钮
btn_Show = tk.Button(window,
    text='显示图像',      # 显示在按钮上的文字
    width=15, height=2,
    command=Show_Img)     # 点击按钮式执行的命令
btn_Show.pack()    # 按钮位置

    
    
# 运行整体窗口
window.mainloop()

在这里插入图片描述
在这里插入图片描述

总结

本项目主要对华盛顿共享单车使用量影响因素进行可视化分析,并利用机器学习对使用量进行预测。

  • 14
    点赞
  • 135
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

远方上&肖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值