
简介共享单车租赁数量预测是交通出行类数据挖掘的典型场景项目使用Python完成数据分析和可视化并以随机森林、支持向量机两种模型进行租赁量回归预测。代码约300行既涵盖Pandas、NumPy的数据清洗与特征预处理也包含Matplotlib、Seaborn的可视化探索适合课程设计、竞赛练习以及希望系统学习数据挖掘全流程的初学者。压缩包共8个文件、约37.41MB以两个可运行ipynb代码文件为主体每个notebook都带有输出步骤配套csv原始数据、模型预测结果png图、特征重要性png图和一个html预览版便于快速查看图表效果同时附送常用包及安装方法说明可降低环境配置门槛。目前已有454人学习学习者既能对比随机森林与SVM在同一任务上的表现也能从数据预处理、特征工程到模型评估走通完整项目。具体可掌握缺失值填充、时间特征转换、标准化/归一化、K折交叉验证、MSE/MAE/R2评估以及随机森林特征重要性排序、SVM核参数选择等关键技能稍加改造即可迁移到其他租赁或销量预测场景。1. 共享单车租赁预测先用 300 行代码看清数据挖掘的完整漏斗共享单车的租赁数量预测本质上是一道回归题给定天气、时间、季节等条件预测某个小时的租车量。这个项目的价值不在于算法多新而在于它把一条完整的数据挖掘链路压缩到了约 300 行代码里——从 pandas 加载与清洗、日期特征分解到随机森林与支持向量机建模、交叉验证再到特征重要性和预测结果的可视化。很多做数据分析的同事日常处理的报表只是“看过去”而这一套流程解决的是“推算未来”两者之间差的就是特征工程和模型评估的功力。对于想系统梳理 pandas、matplotlib、sklearn 的从业者或者正在做课程设计、竞赛入门的人这个压缩包里的train.csv、两个.ipynb和特征重要性图恰好是一条清晰可走的路线先复现再改参最后替换成自己的数据。2. 数据清洗与特征工程日期列才是时间序列建模的富矿2.1 加载训练数据并处理缺失值在随机森林和 SVM 介入之前数据质量决定了模型上限。这个项目里train.csv通常是小时级别的租赁记录包含时间戳、天气状况、温度、体感温度、湿度、风速以及 Casual 和 Registered 两类用户数量。第一步自然是用 pandas 读入并做基础体检常见的做法是这样import pandas as pd import numpy as np df pd.read_csv(train.csv, parse_dates[datetime]) print(df.info()) print(df.isnull().sum()) # 时间序列缺失值处理策略线性插值优于均值填充 df[humidity] df[humidity].interpolate(methodlinear)parse_dates让 pandas 直接把 datetime 列解析成datetime64类型后续提取年、月、日、小时就不需要手动拼接。interpolate对连续型特征更为稳妥——均值填充会压低方差影响随机森林对特征阈值的搜索空间而线性插值保留了时间序列的局部趋势。实际处理时还应该看一眼df.describe()风速为 0 的极端值、体感温度与气温的倒挂都需要结合业务口径判断是保留还是修正而不是机械地删行。2.2 从时间戳中拆出可被模型消化的特征共享单车的租赁量和时段强相关早晚高峰、周末与工作日、季节交替都会改变需求曲线。原始时间戳字符串无法被 SVM 的距离公式直接计算所以特征工程的第一步就是把它拆成数值df[hour] df[datetime].dt.hour df[dayofweek] df[datetime].dt.dayofweek df[month] df[datetime].dt.month df[year] df[datetime].dt.year df[day] df[datetime].dt.day # 业务交叉特征是否上下班高峰 df[is_rush] ((df[hour] 7) (df[hour] 9) | (df[hour] 17) (df[hour] 19)).astype(int)dt.hour提取小时数dt.dayofweek返回 0-6 的整数用于区分工作日和周末。is_rush这个交叉特征在很多共享单车数据集中能显著提升模型表现因为它直接把业务规则编码成强信号。不要小看这一列随机森林可以自动学交互但显式给出业务含义明确的特征能让模型在小样本场景下少走弯路SVM 也能因为特征的线性可分性提升而更快收敛。2.3 离散特征编码与数值特征标准化SVM 对特征尺度极其敏感温度 0-40 和风速 0-60 不在同一量纲直接喂进去会导致距离计算被数值大的维度主导随机森林虽然不受单调变换影响但标准化对它并无坏处。分类特征如季节、天气则需要处理成数值形式# 方法一pandas 内置哑变量编码 df pd.get_dummies(df, columns[season, weather], drop_firstTrue) # 方法二手动映射有顺序的业务类别 df[weather_int] df[weather].map({1: 0, 2: 1, 3: 2, 4: 3}) # 标准化 from sklearn.preprocessing import StandardScaler scaler StandardScaler() feat_cols [temp, atemp, humidity, windspeed, hour, is_rush] df_scaled scaler.fit_transform(df[feat_cols])drop_firstTrue会去掉一个哑变量以避免多重共线性这对 SVM 这类基于距离的模型尤其关键。weather_int的映射保留了一级天气比四级更适宜骑行的语义在特征重要性分析中更容易识别出业务层面的信号。StandardScaler把连续特征转为均值 0、标准差 1 的分布能让 SVM 的 RBF 核函数在核宽度选择上更有效训练迭代次数也明显减少。这里有一个常用做法先 fit 在训练集上再用同一个scaler.transform应用在测试集上防止信息泄漏。3. 可视化探索从分布图到特征重要性图3.1 直方图与箱线图定位数据形态建模之前先看数据形态。Matplotlib 和 Seaborn 在这个项目中不是装饰品它们承担着两个任务验证特征是否符合常识、为特征选择提供定量依据。租车量通常是长尾分布——大部分时段租借量偏低少数时段爆量直接用原始值建模会被极端值拉偏评估指标。常见做法是取对数import matplotlib.pyplot as plt import seaborn as sns fig, axes plt.subplots(1, 2, figsize(12, 4)) sns.histplot(df[count], bins50, axaxes[0]) axes[0].set_title(Original Count Distribution) df[log_count] np.log1p(df[count]) sns.histplot(df[log_count], bins50, axaxes[1]) axes[1].set_title(Log-transformed Count) plt.tight_layout()np.log1p是log(x1)的数值稳定写法避免 x0 时出现负无穷。左侧长尾分布会让模型对峰值时段的预测误差被平方放大右侧对数化后分布接近正态MSE 才能反映真实的相对误差。箱线图则用来快速抓取离群点比如湿度为 0 但租赁量非零的记录这类矛盾点要么是传感器故障要么是数据录入错误标注出来早处理比留给模型硬扛要好。3.2 相关性矩阵与热力图定位冗余特征相关性分析能揭示特征与目标之间的线性关联强度同时暴露冗余特征。温度与体感温度往往高度相关两者都进模型不仅增加计算开销还会让随机森林的特征重要性在两者之间被稀释。热力图是这个阶段最高效的输出形式corr df[[temp, atemp, humidity, windspeed, hour, dayofweek, month, count]].corr() plt.figure(figsize(10, 8)) sns.heatmap(corr, annotTrue, cmapcoolwarm, fmt.2f) plt.title(Feature Correlation Matrix)对比temp和atemp对应的行会发现相关系数经常超过 0.98只保留一个即可。corr()计算的是 Pearson 系数只能捕捉线性关系随机森林里非线性组合可能仍然有价值所以热力图的作用是“优先怀疑、二次验证”——先删强冗余再用模型测试集对比验证删除后的结果而不是看到高相关就一刀切。模型跑完后输出feature_importances_和相关性矩阵对照观察能发现哪些特征线性相关弱但非线性贡献强。3.3 特征重要性可视化与模型输出的落地随机森林的feature_importances_是基于基尼不纯度减少量的归一化统计它回答的是“哪些因素对租赁量影响最大”这一业务问题。项目包里的特征重要性可视化.png正是这一段代码的产出importances rf_model.feature_importances_ indices np.argsort(importances)[::-1] plt.figure(figsize(10, 6)) plt.title(Feature Importances in Random Forest) plt.bar(range(len(importances)), importances[indices], aligncenter) plt.xticks(range(len(importances)), [feat_cols[i] for i in indices], rotation45) plt.tight_layout()柱子长度代表该特征在全部决策树分裂中带来的不纯度下降总和。小时特征排在首位几乎必然天气状况和温度紧随其后也符合业务直觉。值得注意的是特征重要性高不代表因果性强——如果数据里存在与目标间接相关的代理变量它也可能排前面。把这张图和相关性热力图放在同一个报告里比单贴一张图更能体现分析深度这也是这个项目在结构化思路上值得借鉴的地方。4. 随机森林与 SVM 建模参数选择、交叉验证与结果对比4.1 训练集与测试集的划分策略时间序列数据不能用随机打乱的train_test_split否则会发生数据泄漏——模型偷看了未来的数据验证结果虚高。应当按时间顺序切分比如用前 80% 的时间段训练后 20% 预测from sklearn.model_selection import train_test_split X df[feat_cols].values y df[log_count].values # 按时间顺序切分shuffleFalse 是关键 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, shuffleFalse, random_state42 )shuffleFalse让前面 80% 的时间段作为训练数据后面 20% 作为预测目标。如果使用默认的随机切分模型会利用未来信息去预测过去随机森林的集成投票机制会把这种泄漏信号当作强规律学习测试集上的 R² 会虚高到不真实。K 折交叉验证也要注意普通KFold不适用于时间序列TimeSeriesSplit才是常见做法始终用过去验证未来。4.2 随机森林从默认参数到网格搜索随机森林的核心超参数是n_estimators树的数量越大越稳定但计算量线性增长、max_depth限制单棵树深度防过拟合和min_samples_leaf叶子节点最少样本数。先跑一份基线from sklearn.ensemble import RandomForestRegressor rf_base RandomForestRegressor(n_estimators200, random_state42) rf_base.fit(X_train, y_train) y_pred_rf rf_base.predict(X_test)n_estimators200是实践中最常见的起点——超过 500 后边际收益递减而训练时间成倍增加。拟合后保存feature_importances_接下来用网格搜索定位更优的参数组合重点考察max_depth和min_samples_leaf的交叉影响from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [200, 300], max_depth: [10, 20, None], min_samples_leaf: [1, 3, 5] } rf_search GridSearchCV(RandomForestRegressor(random_state42), param_grid, cv3, scoringneg_mean_squared_error) rf_search.fit(X_train, y_train) print(rf_search.best_params_)GridSearchCV内部每次评估都保持相同的随机种子和交叉验证分割结果可以稳定复现。scoringneg_mean_squared_error表示得分越高误差越小网格搜索会自动挑分数最高的组合。调参后的随机森林预测值还需要做逆变换才能和真实租车量对比即np.expm1(rf_best.predict(X_test))否则 R² 再高也落不到业务口径上。4.3 支持向量机归一化之后才能发挥核函数威力随机森林对数据尺度不敏感但 SVM 的优化目标涉及特征向量的内积和距离计算必须标准化。RBF 核是处理非线性回归的默认选择其中C控制对误分类的惩罚力度gamma决定 RBF 核的宽度——gamma过大会让模型只关注临近点容易过拟合过小则会让决策边界过于平滑欠拟合。典型的三步走是from sklearn.svm import SVR from sklearn.pipeline import make_pipeline svr_pipe make_pipeline(StandardScaler(), SVR( kernelrbf, C100, gamma0.1, epsilon0.1 )) svr_pipe.fit(X_train, y_train) y_pred_svr svr_pipe.predict(X_test)make_pipeline把标准化器与 SVR 组装为流水线保证训练和测试时特征落在同一尺度。C100意味着模型会花较大代价去逼近训练数据点gamma0.1对应单个特征对距离的影响力集中在较小范围。epsilon是 SVR 独有参数表示回归管道的宽度——管道内部的点不计入损失这个值直接影响预测曲线的平滑程度。训练完成后输出关键结论指标from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score def evaluate(y_true, y_pred, model_name): print(f{model_name} RMSE: {np.sqrt(mean_squared_error(y_true, y_pred)):.4f}) print(f{model_name} MAE : {mean_absolute_error(y_true, y_pred):.4f}) print(f{model_name} R2 : {r2_score(y_true, y_pred):.4f}) evaluate(y_test, y_pred_rf, RandomForest)在多数共享单车数据流中随机森林的 RMSE 会比 RBF-SVM 低 5%-15%。原因并不复杂租赁量和小时、天气之间的关系呈现明显的分段特征决策树天然擅长这种分段模式而 SVM 需要精心调节gamma才能在非线性曲面上拟合同样的趋势。参数同等的计算时间下随机森林在调用feature_importances_之后的解释性也更贴合业务汇报需求。5. 实战对比与误差溯源特征、参数和数据泄漏的三重验证5.1 画预测值曲线看模型在哪个时段失准用 matplotlib 把真实值与随机森林、SVM 的预测值画在一起排名靠前的特征差异会立刻暴露。如下代码选取测试集最后 72 小时plt.figure(figsize(14, 5)) plt.plot(y_test[-72:], labelActual, linewidth2) plt.plot(y_pred_rf[-72:], labelRandom Forest, linestyle--) plt.plot(y_pred_svr[-72:], labelSVM, linestyle-.) plt.legend() plt.title(Predictions vs Actual, Last 72 Hours) plt.xlabel(Hour Index) plt.ylabel(Log-transformed Count)观察曲线能发现最常见的两种失准一是早高峰 7 点到 9 点被低估二是雨天极低租赁量被高估。早高峰低估往往是因为is_rush特征过于粗糙——它只区分了是或不是没有区分工作日和休息日早期的dayofweek和is_rush包含的信息冗余但非线性组合潜力未被释放随机森林又没有足够深的分裂去识别周日早晨与周二早晨的差异。雨天高估则指向特征编码问题天气类别 3 的样例太少模型学不到足够的“恶劣天气压低需求”的规则。此时可以引入降雨强度等级作为序数特征或者用当日租赁均值和滞后一小时租赁量做滑动平均特征。5.2 数据泄漏排查标准化与交叉验证的常见误区共享单车数据带时间戳最容易犯两个错误。第一在划分训练集和测试集之前对整个数据集做了StandardScaler().fit_transform()导致测试集的均值与标准差参与了训练数据的标准化——测试集信息外泄SVM 的成绩虚高。第二用随机切分做 K 折交叉验证模型学会了时间段的偏移特征真实测试时预测全部失效。快速自查方法把训练好的模型在时间排序的测试集上重新评估一次 R²对比随机切分时的结果如果几乎没下降可以考虑从数据泄漏或异常值影响的角度重新检查特征。此外预测结果等于一个接近常数的低估值时优先检查is_rush和天气类别两个最高贡献特征的取值范围是否与测试集一致——时间跨度过大时数据分布可能偏移在线监控中需要定时重训练。5.3 随机森林缺失特征重要性的容错方案随机森林能从时间戳中拟合大部分周期规律SVM 的 RBF 核理论上也能处理非线性但用于业务解释时二者存在一个明显差异SVM 不直接提供特征重要性。项目包中只提供了随机森林的特征重要性图这是简洁的选择但在实际交付中通常补一个替代方法——用permutation_importance对训练好的 SVM 估计特征影响from sklearn.inspection import permutation_importance r permutation_importance(svr_pipe, X_test, y_test, n_repeats10, scoringneg_mean_squared_error) for i in r.importances_mean.argsort()[::-1]: print(f{feat_cols[i]}: {r.importances_mean[i]:.5f} ± {r.importances_std[i]:.5f})n_repeats10表示对每个特征随机打乱 10 次每次打乱都会让模型误差增加多少。误差增量越大说明该特征对预测越重要。这个指标不需要重新训练模型十分钟内就能对 SVM 的解释性做出定量补充和随机森林的基尼重要性相互印证。最终呈现给业务方的图表应该同时包含排列重要性的柱状图与随机森林的特征重要性图跨模型的一致口径更有说服力。5.4 超参数对训练时间的实际影响规模不大、数据量在几千行级别的train.csv上SVM 网格搜索的开销远高于随机森林。gamma的候选值按 0.01、0.1、1 三档扫一遍C按 10、100、1000 三档扫一遍RBF 核在矩阵求解上的复杂度近似 O(n²)数据量超过 1 万行后每次 fit 都会显著卡顿。传统做法是先用少量代表性样本跑一个小型网格锁定合理区间后再全量训练随机森林在此反而优势明显——n_estimators增加基本呈线性耗时并行调参在多核机器上非常顺手。两种模型在数据量倍数增长时的耗时曲线相反选型时要把重训练频率也纳入考量范围而不是只盯着单次测试集上的 R²。本文还有配套的精品资源点击获取