数据
# 导入库
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 来训练我们的模型,并且我们的模型运行良好。我们的模型能够捕捉趋势而不是捕捉极值,这是一件非常好的事情。所以,我们可以说整体表现不错
网友评论