2023 年第二届钉钉杯大学生大数据挑战赛 初赛 B:美国纽约公共自行车使用量预测分析 问题二
相关链接
【2023 年第二届钉钉杯大学生大数据挑战赛】 初赛 B:美国纽约公共自行车使用量预测分析 问题一Python代码分析
【2023 年第二届钉钉杯大学生大数据挑战赛】 初赛 B:美国纽约公共自行车使用量预测分析 问题二Python代码分析
【2023 年第二届钉钉杯大学生大数据挑战赛】 初赛 B:美国纽约公共自行车使用量预测分析 问题三时间序列预测Python代码分析
1 题目
Citi Bike是纽约市在2013年启动的一项自行车共享出行计划,由“花旗银行”(Citi Bank)赞助并取名为“花旗单车”(Citi Bike)。在曼哈顿,布鲁克林,皇后区和泽西市有8,000辆自行车和500个车站。为纽约的居民和游客提供一种 方便快捷,并且省钱的自行车出行方式。人们随处都能借到Citi Bank,并在他们的目的地归还。本案例的数据有两部分:第一部分是纽约市公共自行车的借还交易流水表。Citi Bik自行车与共享单车不同,不能使用手机扫码在任意地点借还车,而需要使用固定的自行车桩借还车,数据集包含2013年7月1日至2016年8 月31日共38个月(1158天)的数据,每个月一个文件。其中2013年7月到2014年8 月的数据格式与其它年月的数据格式有所差别,具体体现在变量starttime和stoptime的存储格式不同。
第二部分是纽约市那段时间的天气数据,并存储在weather_data_NYC.csv文 件中,该文件包含2010年至2016年的小时级别的天气数据。
公共自行车数据字段表
变量编号 | 变量名 | 变量含义 | 变量取值及说明 |
---|---|---|---|
1 | trip duration | 旅行时长 | 骑行时间,数值型,秒 |
2 | start time | 出发时间 | 借车时间,字符串,m/d/YYY HH:MM:SS |
3 | stop time | 结束时间 | 还车时间,字符串,m/d/YYY HH:MM:SS |
4 | start station id | 借车站点编号 | 定性变量,站点唯一编号 |
5 | start station name | 借车站点名称 | 字符串 |
6 | start station latitude | 借车站点维度 | 数值型 |
7 | start station longtude | 借车站点经度 | 数值型 |
8 | end station id | 还车站点编号 | 定性变量,站点唯一编号 |
9 | end station name | 还车站点名称 | 字符串 |
10 | end station latitude | 还车站点纬度 | 数值型 |
11 | end station longitude | 还车站点经度 | 数值型 |
12 | bile id | 自行车编号 | 定性变量,自行车唯一编号 |
13 | Use type | 用户类型 | Subscriber:年度用户; Customer:24小时或者7天的临时用户 |
14 | birth year | 出生年份 | 仅此列存在缺失值 |
15 | gender | 性别 | 0:未知 1:男性 2:女性 |
天气数据字段简介表
变量编号 | 变量名 | 变量含义 | 变量取值及说明 |
---|---|---|---|
1 | date | 日期 | 字符串 |
2 | time | 时间 | EDT(Eastern Daylight Timing)指美国东部夏令单位 |
3 | temperature | 气温 | 单位:℃ |
4 | dew_poit | 露点 | 单位:℃ |
5 | humidity | 湿度 | 百分数 |
6 | pressure | 海平面气压 | 单位:百帕 |
7 | visibility | 能见度 | 单位:千米 |
8 | wind_direction | 风向 | 离散型,类别包括west,calm等 |
9 | wind_speed | 风速 | 单位:千米每小时 |
10 | moment_wind_speed | 瞬间风速 | 单位:千米每小时 |
11 | precipitation | 降水量 | 单位:毫米,存在缺失值 |
12 | activity | 活动 | 离散型,类别包括snow等 |
13 | conditions | 状态 | 离散型,类别包括overcast,light snow等 |
14 | WindDirDegrees | 风向角 | 连续型,取值为0~359 |
15 | DateUTC | 格林尼治时间 | YYY/m/d HH:MM |
二、解决问题
- 自行车借还情况功能实现:
实现各个站点在一天的自行车借还情况网络图,该网络图是有向图,箭头从借车站点指向还车站点(很多站点之间同时有借还记录,所以大部分站点两两之间是双向连接)。
(一)以2014年8月3日为例进行网络分析,实现自行车借还网络图,计算网络图的节点数,边数,网络密度(表示边的个数占所有可能的连接比例数),给出计算过程和画图结果。
(二)使用上述的网络分析图,对经度位于40.695~40.72,纬度位于- 74.023~-73.973之间的局域网区域进行分析,计算出平均最短路径长度(所有点 两两之间的最短路径长度进行算数平均)和网络直径(被定义网络中最短路径的 最大值)。
- 聚类分析
对于2013年7月1日至2015年8月31日数据集的自行车数据进行聚类分析,选 择合适的聚类数量K值,至少选择两种聚类算法进行聚类,并且比较不同的聚类 方法以及分析聚类结果。
- 站点借车量的预测分析:
对所有站点公共自行车的借车量预测,预测出未来的单日借车量。将2013年 7月-2015年7月数据作为训练集,2015年8月1-31日的数据作为测试集,预测2015 年8月1-31日每天的自行车单日借车量。给出每个站点预测结果的MAPE,并且给 出模型的参数数量,最后算出所有站点的MAPE的均值(注:测试集不能参与到训 练和验证中,否则作违规处理)。
$$MAPE = \frac{1}{n} \sum{|\frac{y_i-\hat{y_i}}{y_i}|} \times 100\%$$
data.csv是纽约市公共自行车的借还交易流水信息,格式如下表,请使用python对数据预处理和特征工程后,聚类分析:
公共自行车数据字段表
变量编号 | 变量名 | 变量含义 | 变量取值及说明 |
---|---|---|---|
1 | trip duration | 旅行时长 | 骑行时间,数值型,秒 |
2 | start time | 出发时间 | 借车时间,字符串,m/d/YYY HH:MM:SS |
3 | stop time | 结束时间 | 还车时间,字符串,m/d/YYY HH:MM:SS |
4 | start station id | 借车站点编号 | 定性变量,站点唯一编号 |
5 | start station name | 借车站点名称 | 字符串 |
6 | start station latitude | 借车站点维度 | 数值型 |
7 | start station longtude | 借车站点经度 | 数值型 |
8 | end station id | 还车站点编号 | 定性变量,站点唯一编号 |
9 | end station name | 还车站点名称 | 字符串 |
10 | end station latitude | 还车站点纬度 | 数值型 |
11 | end station longitude | 还车站点经度 | 数值型 |
12 | bile id | 自行车编号 | 定性变量,自行车唯一编号 |
13 | Use type | 用户类型 | Subscriber:年度用户; Customer:24小时或者7天的临时用户 |
14 | birth year | 出生年份 | 仅此列存在缺失值 |
15 | gender | 性别 | 0:未知 1:男性 2:女性 |
2 问题分析
2.1 问题一
- 绘制有向图
a. 读入数据并分别提取“起始站点编号”和“结束站点编号”两列数据,构建自行车借还网络图。
b. 对于第一步构建的网络图,我们需要计算网络图的节点数,边数,网络密度。节点数即为站点数,边数为借还次数。网络密度为边的数量占所有可能的连接比例。
c. 画出自行车借还网络图。
e. 计算平均最短路径长度和网络直径
首先选出符合条件(经度位于40.695~40.72,纬度位于- 74.023~-73.973之间)的借车站点和还车站点,并以它们为节点构建一个子图进行分析。然后可以直接使用networkx库中的函数来计算平均最短路径长度和网络直径。
2.2 问题二
数据预处理:对进行数据清洗和特征提取。可以使用PCA、LDA算法进行降维,减小计算复杂度。
聚类算法:
a. K-means: 进行数据聚类时,选择不同的K值进行多次试验,选取最优的聚类结果。可以使用轮廓系数、Calinski-Harabaz指数等评价指标进行比较和选择。
b. DBSCAN: 利用密度对数据点进行聚类,不需要预先指定聚类的数量。使用基于密度的聚类算法时,可以通过调整半径参数和密度参数来得到不同聚类效果。
c. 层次聚类:可分为自顶向下和自底向上两种方式。通过迭代计算每个数据点之间的相似度,将数据点逐渐合并,最后得到聚类结果。d.改进的聚类算法
e. 深度聚类算法
聚类结果分析:选择最优的聚类结果后,对不同类别骑车的用户进行画像。分析每个类别的用户行为特征。
2.3 问题三
- 导入数据并进行数据预处理,整合以站点为单位的借车数据。
- 对数据进行时间序列分析,使用ARIMA模型进行单日借车量预测。
- 使用时间序列交叉验证方法进行模型评估,计算每个站点预测结果的MAPE。
- 计算所有站点的MAPE的均值,给出模型的参数数量。
3 Python代码实现
3.1 问题一
【2023 年第二届钉钉杯大学生大数据挑战赛】 初赛 B:美国纽约公共自行车使用量预测分析 问题一Python代码分析
3.2 问题二
3.2.1 读取数据
导入包
import pandas as pd
from sklearn.cluster import Birch
from sklearn.cluster import AgglomerativeClustering
from sklearn.decomposition import PCA
import time
from sklearn import metrics
import os
from sklearn.cluster import MeanShift
from tqdm import tqdm
import numpy as np
import warnings
warnings.filterwarnings("ignore")
tqdm.pandas()
# 合并数据
folder_path = '初赛数据集/2013_2015'
dfs = []
for filename in os.listdir(folder_path):
if filename.endswith('.csv'):
csv_path = os.path.join(folder_path, filename)
tempdf = pd.read_csv(csv_path)
dfs.append(tempdf)
data = pd.concat(dfs,axis=0)
# 根据数据表的字段说明,删除与研究无关的列,例如自行车编号和出生年份等信息,并在必要时删除带有缺失值的行。
data.drop(['bikeid', 'birth year'], axis=1, inplace=True)
data.dropna(inplace=True) # 删除带有缺失值的行
data.shape
3.2.3 特征工程
创建新特征,包括:出发时间和结束时间之间的差值,以及出发站点和结束站点之间的距离(通过经纬度计算)等
from math import radians, sin, cos, acos
from datetime import datetime
data['starttime'] = pd.to_datetime(data['starttime']) # 将时间格式转换为datetime
data['stoptime'] = pd.to_datetime(data['stoptime'])
# 计算时间差值和路程距离
data['duration'] = data['stoptime'] - data['starttime']
data['duration'] = data['duration'] / pd.Timedelta(seconds=1) # 将时间差值转换为秒数
def get_distance(lat1, lng1, lat2, lng2):
"""
根据两点经纬度计算路程距离,单位为米
"""
。。。略
return distance * 1000
data['distance'] = data.apply(
lambda row: get_distance(row['start station latitude'], row['start station longitude'],
row['end station latitude'], row['end station longitude']), axis=1)
features = ['tripduration', 'start station latitude', 'start station longitude',
'end station latitude', 'end station longitude', 'duration', 'distance']
clear_data =data[features]
clear_data.to_excel('初赛数据集/特征工程后的数据.xlsx',index=False)
3.2.3 聚类分析
K 值分析,采用手肘法
start = time.time()
trainingData = weight
SSE = [] # 存放每次结果的误差平方和
k1 = 2
k2 = 10
trainingData =weight
for k in range(k1, k2):
estimator = KMeans(n_clusters=k, max_iter=10000, init="k-means++", tol=1e-6)
estimator.fit(trainingData)
SSE.append(estimator.inertia_) # estimator.inertia_获取聚类准则的总和
end = time.time()
print(f'耗时:{end-start}s')
X = range(k1,k2)
plt.figure(figsize=(8,6))
plt.xlabel('k',fontsize=20)
plt.ylabel('SSE',fontsize=20)
plt.plot(X, SSE, 'o-')
plt.savefig('img/手肘法.png',dpi=300)
plt.show()
3.2.4 Kmeas聚类
from sklearn.cluster import KMeans
start = time.time()
trainingData = weight
clf = KMeans(n_clusters=4,max_iter=10000, init="k-means++", tol=1e-6)
result = clf.fit(trainingData)
source = list(clf.predict(trainingData))
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
import matplotlib.pyplot as plt
import seaborn as sns
# 使用PCA将样本点投影到二维平面上
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(weight)
source = list(clf.predict(trainingData))
# 绘制每个样本点与其对应的簇标签
plt.figure(figsize=(8, 6))
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=source, palette='bright')
plt.savefig('img/kmeans.png',dpi=300)
plt.show()
3.2.5 AGG层次聚类
start = time.time()
trainingData = weight
# 使用层次聚类
clf = AgglomerativeClustering(n_clusters=4, linkage='ward', affinity='euclidean')
result = clf.fit(trainingData)
source = list(clf.labels_)
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
import matplotlib.pyplot as plt
import seaborn as sns
# 使用PCA将样本点投影到二维平面上
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(weight)
source = list(clf.predict(trainingData))
# 绘制每个样本点与其对应的簇标签
plt.figure(figsize=(8, 6))
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=source, palette='bright')
plt.savefig('img/agg聚类.png',dpi=300)
plt.show()
3.2.6 DBSCAN聚类
from sklearn.cluster import DBSCAN
from sklearn.decomposition import PCA
import time
from sklearn import metrics
start = time.time()
trainingData = weight
clf = DBSCAN(eps=0.08, min_samples=7)
result = clf.fit(trainingData)
source = list(clf.fit_predict(trainingData))
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
import matplotlib.pyplot as plt
import seaborn as sns
# 使用PCA将样本点投影到二维平面上
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(weight)
source = list(clf.predict(trainingData))
# 绘制每个样本点与其对应的簇标签
plt.figure (figsize=(8, 6))
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=source, palette='bright')
plt.savefig('img/dbscan.png',dpi=300)
plt.show()
3.2.7 Birch聚类
trainingData = weight
clf = Birch(n_clusters=5, branching_factor=10, threshold=0.01)
start = time.time()
result = clf.fit(trainingData)
source = list(clf.predict(trainingData))
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
import matplotlib.pyplot as plt
import seaborn as sns
# 使用PCA将样本点投影到二维平面上
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(weight)
source = list(clf.predict(trainingData))
# 绘制每个样本点与其对应的簇标签
plt.figure(figsize=(8, 6))
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=source, palette='bright')
plt.savefig('img/Birch聚类.png',dpi=300)
plt.show()
4 完整代码下载
见知乎文章底部链接,包括所有问题的全部代码
zhuanlan.zhihu.com/p/643865954