美文网首页
07基于LSTM模型实现共享自行车需求预测

07基于LSTM模型实现共享自行车需求预测

作者: Jachin111 | 来源:发表于2023-02-14 16:05 被阅读0次

数据

# 导入库
import pandas as pd
import numpy as np

import seaborn as sns
import matplotlib.pyplot as plt
sns.set(context="notebook",style="darkgrid",palette="deep",font="sans-serif",font_scale=1,color_codes=True)

import warnings
warnings.filterwarnings("ignore")
# 读取数据
data = pd.read_csv("london_merged.csv")
data.head()
image.png
data.shape
image.png
data.dtypes
image.png
data.isnull().sum()
image.png

字段含义

timestamp:用于将数据分组的时间戳字段
cnt:新自行车份额的计数
t1:以C为单位的实际温度
t2:C中的温度“感觉像”,主观感受
hum:湿度百分比
windspeed:风速,以km / h为单位
weathercode:天气类别;(具体的取值见下图中的最后)
isholiday:布尔字段,1-假期,0-非假期
isweekend:布尔字段,如果一天是周末,则为1
Season:类别气象季节:0-春季;1-夏;2-秋;3-冬

TensorFlow基本信息

# # 查看tensorflow的GPU信息
# import tensorflow.compat.v1 as tf

# config = tf.ConfigProto()
# config.gpu_options.allow_growth = True
# tf.test.gpu_device_name()
# 查看tensorflow版本
import tensorflow as tf

tf.__version__

特征工程

# 数据信息
# 查看字段信息
data.info(memory_usage="deep")
image.png
# 时间字段处理
# 1.将时间戳转换成时间类型
data["timestamp"] = pd.to_datetime(data["timestamp"])
data.dtypes
image.png
# 2.转成索引:使用set_index方法将timestamp属性转换成索引
data = data.set_index("timestamp")
data.head()
image.png
# 3.提取时、一个月中的第几天、第几周、月份等信息
# 提取时间相关的多个信息,同时查看数据的shape
data["hour"] = data.index.hour
data["day_of_month"] = data.index.day
data["day_of_week"] = data.index.dayofweek
data["month"] = data.index.month
data.columns
image.png
data.shape
image.png
# 相关系数分析
# 1.相关系数求出绝对值
corr_matrix = data.corr().abs()
corr_matrix
image.png
# 2.筛选两个属性之间的相关系数大于0.8
high_corr_var = np.where(corr_matrix > 0.8)
high_corr_var = [(corr_matrix.columns[x], corr_matrix.columns[y]) for x,y in zip(*high_corr_var) if x != y and x < y]
high_corr_var
image.png

数据EDA

# 相关系数热力图
plt.figure(figsize=(16, 6))

sns.heatmap(data.corr(),
           cmap="YlGnBu",  #色系
           square=True,  #方形
           linewidths=.2,
           center=0,
           linecolor="red")  #线条颜色

plt.show()
image.png

通过热力图我们发现:t1和t2的相关系数是比较高的,和上面的“属性之间的系数大于0.8”的结论是吻合的

# 空值判断
data.isnull().sum()
image.png

文章中使用的方法是:基于热力图显示。图形中没有任何信息,表明数据是不存在空值的

plt.figure(figsize=(16, 6))

sns.heatmap(data.isnull(), cmap="viridis")

plt.show()
image.png
# 需求量变化
# 整体的需求量cnt随着时间变化的关系
plt.figure(figsize=(15, 6))

sns.lineplot(data=data,
            x=data.index,  #时间
            y=data.cnt)  #需求量

plt.xticks(rotation=90)
image.png

从上面的图形,我们能够看到整体日期下的需求量变化情况。

# 按月采样resample
# pandas中的采样函数使用的是resample,频率可以是天、周、月等
df_by_month = data.resample("M").sum()
df_by_month.head()
image.png
# 查看随着时间的变化,每月的需求量变化情况
plt.figure(figsize=(16, 6))

sns.lineplot(data=df_by_month,
            x=df_by_month.index,
            y=df_by_month.cnt,
            color="red")

plt.xticks(rotation=90)
plt.show()
image.png

可以从图中观察到以下3点结论:
1.年初到7、8月份需求量呈现上升趋势
2.差不多在8月份达到一定的峰值
3.8月份过后需求量开始降低

# 每小时需求量
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.hour,  #小时
             y=data.cnt,  #需求量
             color="red")

plt.show()
image.png
# 每月的需求量对比
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.month,
             y=data.cnt,
             color="red")

plt.show()
image.png

明显的结论:7月份是需求的高峰期

# 按照星期统计
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.day_of_week,
             y=data.cnt,
             color="black")

plt.show()
image.png

从图中观察到:
周1到周五的需求是明显高于周末两天;
同时在周五的时候已经呈现下降趋势

# 按照自然日
plt.figure(figsize=(16, 6))

sns.lineplot(data=data,
            x=data.day_of_month,  #一个月中的某天
            y=data.cnt,
            color="r")

plt.show()
image.png

3点结论:
前10天需求量在逐步增加
中间10天存在一定的小幅波动
最后10天波动加大,呈现下降趋势

多个维度下的可视化效果

# 基于是否节假日下的小时
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.hour,  #按照小时统计
             y=data.cnt,
             hue=data.is_holiday)  #节假日分组

plt.show()
image.png

通过上面图形呈现的结果;
非节假日下(is_holiday=0):在8点和下午的17、18点是用车的高峰期,恰好是上下班的时间点
到了节假日(1)的情况下:下午的2-3点才是真正的用车高峰期

# 基于是否节假日的月份
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.month,
             y=data.cnt,
             hue=data.is_holiday)

plt.show()
image.png

在非节假日,7月份达到了用车的高峰期

# 按照季度统计
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             y=data.cnt,
             x=data.month,
             hue=data.season)  #季度分组

plt.show()
image.png

从上图中观察到:第3个季度(6–7-8月份)才是用车需求量最多的时候

# 季度+是否节假日
plt.figure(figsize=(16, 6))

sns.countplot(data=data,
             x=data.season,
             hue=data.is_holiday)

plt.show()
image.png

从1-2-3-4季度来看,非节假日中的整体需求量1和2季度是稍高于0和3季度;而节假日中,0-3季度则存在一定的需求

# 是否周末+小时
plt.figure(figsize=(16, 6))

sns.lineplot(data=data,
            x=data.hour,
            y=data.cnt,
            hue=data.is_weekend)

plt.show()
image.png

非周末(0):仍然是上午的7-8点和下午的17-18点是用车高峰期
周末(1):下午的14-15点才是高峰期

# 季度+小时
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.hour,
             y=data.cnt,
             hue=data.season)

plt.show()
image.png

分季度查看每个小时的需求量:整体的趋势大体是相同的,都是在8点左右达到上午的高封期,下午的17-18点(下班的时候)达到另一个高封期

天气因素

# 湿度和需求量关系
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.hum,
             y=data.cnt,
             color="black")

plt.xticks(rotation=90)
plt.show()
image.png

可以看到:空气空气湿度越大,整体的需求量是呈现下降趋势

# 风速和需求量
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.wind_speed,
             y=data.cnt)

plt.xticks(rotation=90)
plt.show()
image.png

风速对需求量的影响:
在风速为25.5的时候存在一个局部峰值
风速偏高或者偏低的时候需求都有所降低

# 不同天气情况weather_code
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.weather_code,
             y=data.cnt)

plt.xticks(rotation=90)
plt.show()
image.png

可以看到在scattered coluds(weather_code=2)情况下,需求量是最大的

# 天气情况+小时
plt.figure(figsize=(16, 6))

sns.pointplot(data=data,
             x=data.hour,
             y=data.cnt,
             hue=data.weather_code)  #分天气统计

plt.show()
image.png

从上午中观察到:不同的天气对小时需求量的趋势影响不大,仍然是在上下班高峰期的时候需求量最大,说明打工人上班出行几乎不受天气影响!!!

# 自然天+天气情况
plt.figure(figsize=(16, 6))

sns.countplot(data=data,
             x=data.day_of_week,  #一周中的第几天
             hue=data.weather_code,  #天气情况
             palette="viridis")

plt.legend(loc="best")
plt.show()
image.png

从上图中观察到:
1.不同的星期日期,code=1下的需求量都是最大的
2.礼拜1到礼拜5:满足code=1 > 2 > 3 > 7 > 4 的需求量
3.到礼拜6和礼拜天:大家出行的时候对天气关注影响偏低,除去code=1,其他天气情况的需求差距也在缩小!

箱型图

# 按小时
plt.figure(figsize=(16, 6))

sns.boxplot(data=data,
           x=data.hour,
           y=data.cnt)

plt.show()
image.png

从箱型图的分布观察到:两个重要的时间段:上午7-8点和下午的17-18点

# 每周星期几
plt.figure(figsize=(16, 6))

sns.boxplot(data=data,
           x=data["day_of_week"],
           y=data.cnt)

plt.show()
image.png

在基于星期的箱型图中,礼拜三的时候存在一定的用车高峰期

# 月的自然天
plt.figure(figsize=(16, 6))

sns.boxplot(data=data,
           x=data["day_of_month"],
           y=data.cnt)

plt.show()
image.png

在基于自然日的情况下,9号的存在高峰期

# 按月
plt.figure(figsize=(16, 6))

sns.boxplot(data=data,
           x=data["month"],
           y=data.cnt)

plt.show()
image.png

7-8月份存在一定的需求高峰期,两侧月份的需求相对较少些

# 是否节假日+月的天
plt.figure(figsize=(16, 6))

sns.boxplot(data=data,
           x=data["day_of_month"],
           y=data.cnt,
           hue=data["is_holiday"])

plt.show()
image.png

数据预处理

# 切分数据
# 按照9:1的比例来切分数据
from sklearn.model_selection import train_test_split

train, test = train_test_split(data, test_size=0.1, random_state=0)
print(train.shape)
print(test.shape)
image.png
# 数据归一化
from sklearn.preprocessing import MinMaxScaler

# 实例化对象
scaler = MinMaxScaler()

# 部分字段的拟合
num_col = ["t1", "t2", "hum", "wind_speed"]
trans_1 = scaler.fit(train[num_col].to_numpy())

# 训练集转换
train.loc[:, num_col] = trans_1.transform(train[num_col].to_numpy())
# 测试集转换
test.loc[:, num_col] = trans_1.transform(test[num_col].to_numpy())

# 对标签cnt的归一化
cnt_scaler = MinMaxScaler()
# 数据拟合
trans_2 = cnt_scaler.fit(train[["cnt"]])
# 数据转化
train["cnt"] = trans_2.transform(train[["cnt"]])
test["cnt"] = trans_2.transform(test[["cnt"]])

训练集和测试集

# 用于显示进度条
from tqdm import tqdm_notebook as tqdm
tqdm().pandas()

def prepare_data(X, y, time_steps=1):
    Xs = []
    Ys = []
    
    for i in tqdm(range(len(X) - time_steps)):
        a = X.iloc[i:(i + time_steps)].to_numpy()
        Xs.append(a)
        Ys.append(y.iloc[i + time_steps])
        
    return np.array(Xs), np.array(Ys)

steps = 24

X_train, y_train = prepare_data(train, train.cnt, time_steps=steps)
X_test, y_test = prepare_data(test, test.cnt, time_steps=steps)

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
image.png

LSTM建模

# 导入库
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM, Bidirectional

# 实例化对象并拟合建模
model = Sequential()
model.add(Bidirectional(LSTM(128, input_shape=(X_train.shape[1], X_train.shape[2]))))
model.add(Dropout(0.2))
model.add(Dense(1, activation="sigmoid"))
model.compile(optimizer="adam", loss="mse")
# 模型准备
# 传入训练集的数据后,进行数据的拟合建模过程
with tf.device("/GPU:0"):
    prepared_model = model.fit(X_train,
                              y_train,
                              batch_size=32,
                              epochs=100,
                              validation_data=(X_test, y_test))
# 均方差和Epoch的关系
plt.plot(prepared_model.history["loss"], label="loss")
plt.plot(prepared_model.history["val_loss"], label="val_loss")

plt.legend(loc="best")
plt.xlabel("No. Of Epochs")
plt.ylabel("mse score")
image.png

需求量预测

# 生成真实值和预测值
pred = model.predict(X_test)
y_test_inv = cnt_scaler.inverse_transform(y_test.reshape(-1, 1))
pred_inv = cnt_scaler.inverse_transform(pred)
pred_inv
image.png
# 绘图比较,将测试集转变后的值和基于模型的预测值进行绘图比较
plt.figure(figsize=(16, 6))

plt.plot(y_test_inv.flatten(), marker=".", label="actual")
plt.plot(pred_inv.flatten(), marker=".", label="prediction",  color="r")

plt.legend(loc="best")
plt.show()
image.png

注意到,我们的模型仅预测未来的一个点。话虽如此,它仍做得很好。虽然我们的模型不能真正捕捉到极值,但它在预测(理解)一般模式方面还是做得很好

# 生成数据
# 将测试集的真实值和预测值进行对比,通过两个指标来进行评估
# 原文方法
y_test_actual = cnt_scaler.inverse_transform(y_test.reshape(-1, 1))
y_test_pred = cnt_scaler.inverse_transform(pred)

arr_1 = np.array(y_test_actual)
arr_2 = np.array(y_test_pred)

actual = pd.DataFrame(data=arr_1.flatten(), columns=["actual"])
predicted = pd.DataFrame(data=arr_2.flatten(), columns=["predicted"])

final = pd.concat([actual, predicted], axis=1)
final.head()
image.png
# 个人方法
y_test_actual = cnt_scaler.inverse_transform(y_test.reshape(-1, 1))
y_test_pred = cnt_scaler.inverse_transform(pred)
final = pd.DataFrame({"actual":y_test_actual.flatten(), "pred":y_test_pred.flatten()})
final.head()
image.png
# 模型评价,通过mse和r2_score指标来评估模型
from sklearn.metrics import mean_squared_error, r2_score

rmse = np.sqrt(mean_squared_error(final.actual, final.pred))
r2 = r2_score(final.actual, final.pred)

print("rmse is : ", rmse)
print("----------")
print("r2_score is : ", r2)
image.png
# 绘图比较,对比真实值和预测值
plt.figure(figsize=(16 ,6))

plt.plot(final.actual, marker=".", label="Actual label")
plt.plot(final.pred, marker=".", label="predicted label")

plt.legend(loc="best")
plt.show()
image.png

如你所见,我使用双向 LSTM 来训练我们的模型,并且我们的模型运行良好。我们的模型能够捕捉趋势而不是捕捉极值,这是一件非常好的事情。所以,我们可以说整体表现不错

相关文章

网友评论

      本文标题:07基于LSTM模型实现共享自行车需求预测

      本文链接:https://www.haomeiwen.com/subject/bmpekdtx.html