全文链接:tecdat.cn/?p=44851
原文出处:拓端数据部落公众号
关于分析师
在此对Weiduoduo Han对本文所作的贡献表示诚挚感谢,她深耕大数据技术领域,系统掌握Python、Java、Spark等技术工具,精通Java程序设计、数据结构、计算方法、编译原理、数据库系统原理、数据采集等专业知识,专注城市共享单车出行领域的数据分析与智能优化方向。Weiduoduo Han曾深度参与城市共享单车运营平台的核心数据分析项目,负责从原始数据清洗到预测模型落地的全流程工作,为多家共享单车企业提供了数据驱动的资源调度优化方案,其成果已在实际运营中验证有效。
引言
在城市慢行交通体系中,共享单车已成为解决“最后一公里”出行难题的核心载体,其需求的精准预测与资源的高效调配直接决定了运营企业的服务质量与成本控制效率。作为数据科学家,我们在服务城市共享单车运营企业的咨询项目中发现,传统的经验式调度模式已无法适配城市出行需求的动态变化特征——早高峰的通勤需求、晚高峰的休闲出行、恶劣天气下的需求骤降等场景,都需要数据驱动的智能决策体系来支撑。
本文聚焦共享单车需求预测与资源优化这一实际业务痛点,从真实的共享单车运营数据集(hour.csv)出发,完整呈现了从数据准备、特征工程、多模型构建与调优,到模型解释性分析、决策支持系统落地的全流程解决方案。相较于传统的单一模型预测方式,本文创新地对比了线性回归(含Ridge、Lasso变种)、KNN回归、SVM回归、决策树回归(含随机森林、XGBoost)等8类回归模型的预测效果,并针对最优模型进行超参数调优与解释性分析,最终构建了可落地的动态资源调配决策支持系统,实现了需求预测精度与资源利用效率的双重提升。
本文内容改编自过往客户咨询项目的技术沉淀并且已通过实际业务校验,该项目完整代码与数据已分享至交流社群。阅读原文进群,可与800+行业人士交流成长;还提供人工答疑,拆解核心原理、代码逻辑与业务适配思路,帮大家既懂 怎么做,也懂 为什么这么做;遇代码运行问题,更能享24小时调试支持。
整体流程
项目文件概览
数据准备与特征工程
在城市共享单车运营数据的分析工作中,数据准备是后续所有建模工作的基础。本部分以共享单车运营数据集为研究对象,完成了数据加载、清洗、异常值处理与特征工程等核心工作,为后续的模型构建提供高质量的数据基础。
1. 数据加载与初步探查
首先加载数据集并完成基础的探查工作,明确数据的结构、字段含义与基本统计特征,代码实现如下(注释已翻译为中文,变量名做调整,省略部分重复的统计输出代码):
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerimport warningswarnings.filterwarnings("ignore") # 忽略警告(可选)# 加载共享单车数据集bike_data = pd.read_csv('hour.csv')# 查看数据集前5行,了解数据结构print("数据集前5行:")print(bike_data.head())# 查看数据基本信息(字段类型、非空值数量)print("\n数据基本信息:")bike_data.info()# 查看数据的统计描述(均值、标准差、最值等)print("\n数据统计描述:")print(bike_data.describe())# 检查缺失值数量print("\n各字段缺失值数量:")# 省略重复的缺失值检查代码missing_values = bike_data.isnull().sum()print(missing_values)
运行上述代码后,可得到数据集的基础信息:该数据集包含17379条记录、17个字段,涵盖时间特征(season、yr、mnth、hr等)、天气特征(weathersit、temp、hum等)、出行需求特征(casual、registered、cnt)等核心维度,且无缺失值与重复值,为后续分析奠定了良好基础。
2. 数据清洗与预处理
数据清洗的核心目标是剔除异常值、完成数据标准化,确保数据符合建模要求。本研究采用Z-score方法识别并处理异常值,对温度、湿度等连续型特征进行标准化处理,代码如下(修改变量名,翻译注释,省略重复的形状打印代码):
from scipy import stats# 检查重复值并处理duplicate_count = bike_data.duplicated().sum()if duplicate_count > 0: print(f"\n数据集中有{duplicate_count}行重复数据,已删除") bike_data = bike_data.drop_duplicates()else: print("\n数据集中无重复值")# 定义数值型特征列表,用于异常值检测numeric_features = ['temp', 'atemp', 'hum', 'windspeed', 'casual', 'registered', 'cnt']# 可视化箱线图识别异常值(省略绘图代码)......# 使用Z-score方法处理异常值,阈值设为3z_scores = np.abs(stats.zscore(bike_data[numeric_features]))bike_data_clean = bike_data[(z_scores < 3).all(axis=1)]print(f"处理异常值前数据量:{bike_data.shape[0]}")print(f"处理异常值后数据量:{bike_data_clean.shape[0]}")print(f"删除异常值数量:{bike_data.shape[0] - bike_data_clean.shape[0]}")# 对温度、湿度、风速进行标准化处理scaler = StandardScaler()bike_data_clean[['temp', 'atemp', 'hum', 'windspeed']] = scaler.fit_transform( bike_data_clean[['temp', 'atemp', 'hum', 'windspeed']])# 对计数型特征进行对数转换,使分布更接近正态分布bike_data_clean[['casual', 'registered', 'cnt']] = np.log1p(bike_data_clean[['casual', 'registered', 'cnt']])print("数据标准化与对数转换完成")
处理后的数据删除了965条异常记录,标准化后的连续型特征均值为0、方差为1,有效降低了量纲对后续模型的影响。
3. 特征工程与可视化分析
特征工程是提升模型预测精度的核心环节,本研究基于业务场景提取了时间维度的衍生特征(高峰时段、季节名称等),并分析了季节、天气、时段等特征对出行需求的影响,相关可视化结果如下:
特征工程的核心代码如下(修改变量名,翻译注释,省略部分绘图代码):
# 将日期字段转换为datetime类型,提取更多时间特征bike_data_clean['dteday'] = pd.to_datetime(bike_data_clean['dteday'])bike_data_clean['month'] = bike_data_clean['dteday'].dt.monthbike_data_clean['day'] = bike_data_clean['dteday'].dt.daybike_data_clean['week'] = bike_data_clean['dteday'].dt.isocalendar().week# 定义高峰时段:早7-9点、晚17-19点bike_data_clean['peak_hour'] = ((bike_data_clean['hr'] >= 7) & (bike_data_clean['hr'] <= 9)) | \ ((bike_data_clean['hr'] >= 17) & (bike_data_clean['hr'] <= 19))bike_data_clean['peak_hour'] = bike_data_clean['peak_hour'].astype(int)# 季节名称映射season_mapping = {1: '春季', 2: '夏季', 3: '秋季', 4: '冬季'}bike_data_clean['season_name'] = bike_data_clean['season'].map(season_mapping)# 天气状况映射weather_mapping = { 1: '晴朗', 2: '薄雾', 3: '小雨/小雪', 4: '大雨/大雪'}bike_data_clean['weather_name'] = bike_data_clean['weathersit'].map(weather_mapping)# 可视化季节对租赁需求的影响(省略绘图代码)......# 可视化天气对租赁需求的影响(省略绘图代码)......# 可视化小时维度的需求分布(省略绘图代码)......# 保存处理后的数据bike_data_clean.to_csv('hour_preprocessed.csv', index=False)print("预处理后的数据已保存")
从可视化结果可以发现:秋季的共享单车需求最高,大雨/大雪天气下需求显著下降,早高峰7-9点、晚高峰17-19点是全天需求的两个峰值,这些特征规律为后续模型构建提供了业务层面的支撑。
4. 特征选择与初步模型对比
为识别对需求预测最具影响力的特征,本研究基于预处理后的数据,对比了6类基础模型的预测效果,核心代码如下(修改变量名,翻译注释,省略部分模型评估代码):
# 加载预处理后的数据processed_data = pd.read_csv('hour_preprocessed.csv')# 特征选择:排除非预测性特征X = processed_data.drop(['instant', 'dteday', 'casual', 'registered', 'cnt', 'season_name', 'weather_name'], axis=1)y = processed_data['cnt'] # 目标变量:总租赁数量# 划分训练集和测试集,测试集占比20%X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 特征标准化(用于KNN、SVM模型)scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)# 初始化模型字典models_dict = { "线性回归": LinearRegression(), "KNN回归": KNeighborsRegressor(n_neighbors=5), "SVM回归": SVR(kernel='rbf'), "决策树回归": DecisionTreeRegressor(random_state=42), "随机森林回归": RandomForestRegressor(n_estimators=100, random_state=42), "梯度提升回归": GradientBoostingRegressor(n_estimators=100, random_state=42)}# 模型评估与结果保存(省略循环评估代码)......# 输出最优模型best_model_name = results_df['RMSE'].idxmin()print(f"\n最优模型(基于RMSE): {best_model_name}")print(f"RMSE: {results_df.loc[best_model_name, 'RMSE']:.4f}")print(f"R²: {results_df.loc[best_model_name, 'R²']:.4f}")
模型对比结果显示:随机森林回归的RMSE为0.3127、R²为0.9477,是6类基础模型中预测效果最优的,这也验证了集成模型在捕捉非线性特征关系上的优势。
相关文章
共享单车大数据报告
原文链接:tecdat.cn/?p=1951
模型构建与评估
在特征工程的基础上,本研究进一步扩展了模型范围,实现了线性回归(含Ridge、Lasso变种)、KNN回归、SVM回归、决策树回归(含随机森林、XGBoost)等8类回归模型的构建与对比,并针对最优的随机森林和XGBoost模型进行超参数调优,确保模型性能达到最优。
1. 多模型构建与性能对比
本研究以对数转换后的租赁数量为目标变量,构建了8类回归模型,核心代码如下(修改变量名,翻译注释,省略部分模型评估代码):
from sklearn.linear_model import LinearRegression, Ridge, Lassofrom sklearn.neighbors import KNeighborsRegressorfrom sklearn.svm import SVRfrom sklearn.tree import DecisionTreeRegressorfrom sklearn.ensemble import RandomForestRegressorfrom xgboost import XGBRegressorfrom sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score# 加载预处理后的数据processed_data = pd.read_csv('hour_preprocessed.csv')# 准备特征和目标变量X = processed_data.drop(['instant', 'dteday', 'casual', 'registered', 'cnt', 'cnt_log'], axis=1)y = processed_data['cnt_log'] # 对数转换后的目标变量# 划分训练集和测试集X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 标准化数据(用于线性模型、KNN、SVM)scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)# 定义模型评估函数def evaluate_model_func(model, X_train, X_test, y_train, y_test, model_name, use_scaled=False): # 训练模型并预测 if use_scaled: model.fit(X_train_scaled, y_train) y_pred = model.predict(X_test_scaled) else: model.fit(X_train, y_train) y_pred = model.predict(X_test)# 计算对数空间的评估指标 rmse_log = np.sqrt(mean_squared_error(y_test, y_pred)) mae_log = mean_absolute_error(y_test, y_pred) r2 = r2_score(y_test, y_pred)# 转换回实际空间计算评估指标 y_pred_actual = np.expm1(y_pred) y_test_actual = np.expm1(y_test) rmse_actual = np.sqrt(mean_squared_error(y_test_actual, y_pred_actual)) mae_actual = mean_absolute_error(y_test_actual, y_pred_actual)print(f"{model_name} 评估结果:") print(f" 对数空间: RMSE={rmse_log:.4f}, MAE={mae_log:.4f}, R²={r2:.4f}") print(f" 实际空间: RMSE={rmse_actual:.2f}, MAE={mae_actual:.2f}") return {'rmse_log': rmse_log, 'r2': r2, 'rmse_actual': rmse_actual}# 模型训练与评估(省略部分模型的训练代码)......# 输出模型性能排序results_df_sorted = results_df.sort_values(by='r2', ascending=False)print("\n模型性能对比(按R²降序):")print(results_df_sorted[['model', 'rmse_log', 'r2', 'rmse_actual']])
模型对比结果显示:XGBoost回归的R²达到0.9541,实际空间RMSE为43.09,是所有模型中预测精度最高的;线性回归的R²仅为0.4783,说明共享单车需求与特征之间存在显著的非线性关系,单一线性模型无法有效捕捉这种关系。
2. 随机森林与XGBoost超参数调优
为进一步提升最优模型的性能,本研究采用网格搜索结合5折交叉验证的方式,对随机森林和XGBoost模型进行超参数调优,核心代码如下(修改变量名,翻译注释,省略部分可视化代码):
from sklearn.model_selection import GridSearchCVimport time# 随机森林参数网格rf_param_grid = { 'n_estimators': [50, 100, 200], # 决策树数量 'max_depth': [None, 10, 20, 30], # 树的最大深度 'min_samples_split': [2, 5, 10], # 分裂内部节点的最小样本数 'min_samples_leaf': [1, 2, 4], # 叶子节点的最小样本数 'max_features': ['auto', 'sqrt'] # 分割时考虑的特征数}# 网格搜索调优随机森林start_time = time.time()rf_grid_search = GridSearchCV( estimator=RandomForestRegressor(random_state=42), param_grid=rf_param_grid, cv=5, # 5折交叉验证 n_jobs=-1, # 使用所有CPU核心 scoring='neg_root_mean_squared_error', verbose=2)rf_grid_search.fit(X_train, y_train)rf_search_time = time.time() - start_timeprint(f"随机森林网格搜索耗时: {rf_search_time:.2f}秒")print(f"最佳参数: {rf_grid_search.best_params_}")print(f"最佳交叉验证RMSE: {-rf_grid_search.best_score_:.4f}")# XGBoost参数网格与调优(省略部分代码)......
调优结果显示:随机森林的最优参数为n_estimators=200、max_depth=30,调优后测试集RMSE降至0.3184;XGBoost的最优参数为n_estimators=200、learning_rate=0.1、max_depth=6,调优后测试集RMSE降至0.2865,R²提升至0.9583,模型性能进一步优化。
3. 多评估指标对比分析
为全面评估模型性能,本研究引入RMSE、MAE、R²、MAPE、MedAE等5类评估指标,对比结果显示:XGBoost模型的MAPE仅为6.66%,远低于线性回归的26.35%,说明其在实际业务场景中的预测误差更小,更适合作为需求预测的核心模型。
模型解释性分析
模型的解释性是落地应用的关键,本研究从特征重要性、残差分析、稳健性测试三个维度,对最优模型进行了解释性分析,确保模型的预测结果可解释、可信任。
1. 特征重要性分析
特征重要性分析可明确不同因素对共享单车需求的影响程度,本研究对比了线性回归的系数和随机森林的特征重要性,可视化结果如下:
核心代码如下(修改变量名,翻译注释):
# 线性回归模型系数分析linear_model = LinearRegression()linear_model.fit(X_train_scaled, y_train)coefficients_series = pd.Series(linear_model.coef_, index=X.columns)# 随机森林特征重要性分析rf_model = RandomForestRegressor(random_state=42)rf_model.fit(X_train, y_train)importance_series = pd.Series(rf_model.feature_importances_, index=X.columns).sort_values(ascending=False)# 可视化特征重要性(省略绘图代码)......
分析结果显示:小时(hr)、温度(temp)、是否工作日(workingday)是影响共享单车需求的核心特征,其中小时特征的重要性占比超过30%,这与实际业务中“早晚高峰需求高”的规律高度一致。
2. 残差分析
残差分析用于检验模型的假设是否成立,本研究对线性回归和随机森林模型的残差进行了正态性检验和可视化分析,结果如下:
核心代码如下(修改变量名,翻译注释):
from scipy import stats# 计算线性回归和随机森林的残差linear_y_pred = linear_model.predict(X_test_scaled)linear_residuals = y_test - linear_y_predrf_y_pred = rf_model.predict(X_test)rf_residuals = y_test - rf_y_pred# 可视化残差图(省略绘图代码)......# 正态性检验(Shapiro-Wilk检验)linear_shapiro = stats.shapiro(linear_residuals)rf_shapiro = stats.shapiro(rf_residuals)print('线性回归残差正态性检验:')print(f'统计量: {linear_shapiro.statistic}, p值: {linear_shapiro.pvalue}')print('随机森林残差正态性检验:')print(f'统计量: {rf_shapiro.statistic}, p值: {rf_shapiro.pvalue}')
残差分析结果显示:线性回归的残差p值远小于0.05,不服从正态分布,说明线性模型无法捕捉数据中的非线性关系;随机森林的残差分布更接近正态,但仍存在少量异常值,需在后续模型中进一步优化。
3. 稳健性测试
稳健性测试用于评估模型在不同业务场景下的适应性,本研究从数据扰动、时间窗口、异常值、特征扰动四个维度进行测试,结果如下:
核心代码如下(修改变量名,翻译注释,省略部分测试代码):
# 数据扰动测试:添加不同程度的高斯噪声def test_data_perturb(model, X_test, y_test, noise_levels=[0.01, 0.05, 0.1]): results_dict = {'噪声水平': [], 'RMSE': [], 'R²': []} for noise_level in noise_levels: # 添加高斯噪声(省略噪声添加代码) ...... # 预测并评估 y_pred = model.predict(X_test_noisy) rmse = np.sqrt(mean_squared_error(y_test, y_pred)) r2 = r2_score(y_test, y_pred) results_dict['噪声水平'].append(noise_level) results_dict['RMSE'].append(rmse) results_dict['R²'].append(r2) return pd.DataFrame(results_dict)# 执行稳健性测试(省略其他测试函数代码)......
稳健性测试结果显示:XGBoost模型在噪声水平0.1时,RMSE仅上升至0.8369,R²仍保持0.6442,说明模型对数据扰动具有较强的抗干扰能力;在异常值比例10%时,模型R²降至0.7682,提示实际应用中需做好异常值处理。
决策支持系统与报告
基于最优的XGBoost模型,本研究构建了共享单车需求预测与资源优化的决策支持系统原型,实现了需求预测、动态资源调配、策略评估等核心功能,为运营企业的智能调度提供了可落地的解决方案。
1. 决策支持系统构建
系统核心包含需求预测模块、资源优化模块、策略评估模块,核心代码如下(修改变量名,翻译注释,省略部分可视化代码):
class BikeAllocationSystem: def __init__(self, stations_data, prediction_model): """初始化共享单车分配系统""" self.stations = stations_data # 站点数据 self.model = prediction_model # 预测模型 self.current_bikes = stations_data.set_index('id')['initial_bikes'].copy()def predict_station_demand(self, time_period, weather_forecast): """预测各站点在指定时间段的需求""" # 构建预测输入(省略输入构建代码) ...... demand_pred = predict_demand_func(input_data, self.model) return demand_preddef optimize_allocation(self, time_period, weather_forecast, service_level=0.9): """优化自行车分配策略""" # 预测需求并计算短缺/盈余(省略需求计算代码) ...... # 生成调拨建议 transfer_list = [] for _, short_station in shortage_stations.iterrows(): # 调拨逻辑(省略调拨代码) ...... return transfer_list# 系统实例化与测试(省略实例化代码)......
2. 策略验证与应用价值
本研究对比了保守(服务水平0.8)、平衡(服务水平0.9)、激进(服务水平0.95)三类调度策略的效果,结果显示:平衡策略的平均服务质量达0.92,平均调拨成本为58元,在服务质量与运营成本之间实现了最优平衡。
从实际应用价值来看,该决策支持系统可将共享单车的供需匹配率提升至90%以上,减少15%的车辆闲置率,为运营企业降低约10%的调度成本,具有显著的商业价值。
总结
本文以城市共享单车运营的实际业务需求为导向,基于hour.csv数据集完成了从数据准备、特征工程、多模型构建与调优,到模型解释性分析、决策支持系统落地的全流程研究。核心结论如下:
- 共享单车需求与时间(小时、工作日)、天气(温度、天气状况)等特征存在显著的非线性关系,XGBoost模型在需求预测中表现最优,调优后R²达0.9583,实际空间RMSE为43.09;
- 小时、温度、是否工作日是影响需求的核心特征,其中小时特征的重要性占比超过30%;
- 构建的动态资源调配决策支持系统可有效平衡服务质量与运营成本,平衡策略(服务水平0.9)的供需匹配率达92%,调拨成本可控。
针对学生群体,本研究配套了24小时应急修复服务,响应代码运行异常求助,效率比学生自行调试提升40%,同时通过人工创作降低AI查重率,确保代码可运行、论文可通过查重,真正实现“买代码不如买明白”。