恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

R语言回归分析实战:首尔自行车共享需求预测与调参避坑指南

  • 首页
  • 资讯中心
  • /
  • R语言回归分析实战:首尔自行车共享需求预测与调参避坑指南

相关资讯

Qt多线程实战避坑:moveToThread原理、事件循环与安全退出指南 2026/10/13 21:07:24
C++网络编程实战:从零实现一个高并发网络计算器 2026/10/13 21:07:24
AnyPS5远程串流方案:把PS5带进任何屏幕的实战指南 2026/10/13 21:07:24

最新资讯

Tushare数据获取:量化投资中可追溯、可验证的时间序列底座
Android应用RSA加密原理与安全防护实践
小型超市仓库管理系统
小区物业管理系统
IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
个人知识工作插件系统:SQLite+FTS5实现高效检索

今日推荐

IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
小区物业管理系统
小型超市仓库管理系统

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

R语言回归分析实战:首尔自行车共享需求预测与调参避坑指南

发布时间:2026/10/13 21:12:24
R语言回归分析实战:首尔自行车共享需求预测与调参避坑指南 简介这份资源面向具备一定R语言基础、希望上手真实回归建模的数据分析学习者与从业者围绕首尔自行车共享需求预测这一经典赛题展开。数据源自首尔自行车租赁系统目标是根据温度、小时、季节、假期等变量预测每小时所需自行车数量以支撑租赁车辆的稳定调度。压缩包共4个文件约1.71MB包含R脚本、Markdown说明、Word分析报告与CSV数据集分别对应建模代码、项目说明、结果呈现与原始数据结构完整、便于复现。已有257人学习下载。资源完整呈现了从数据读取、特征处理到模型训练与评估的全流程涵盖CUBIST、正则化随机森林、分类回归树、K近邻与条件推断树等模型并借助R²、RMSE、MAE等指标比较性能还包含变量重要性分析指出温度与小时是最具影响力的因素适合作为回归分析与交叉验证的实操参考。1. 首尔自行车共享需求预测为什么 R 的回归分析仍是性价比最高的起点首尔自行车共享系统Seoul Bike / Ttareungyi公开了一份逐小时租赁记录字段涵盖日期、时间、季节、节假日、温度、湿度、风速、能见度、露点、太阳辐射、降雨量和降雪量目标变量是每小时租借的自行车数量。这个数据集在回归分析圈子里被反复使用原因很直接它同时具备时间序列特征、天气特征和类别特征量级适中跑得动又能暴露真实建模中的绝大多数坑。用 R 语言做回归分析来预测每小时所需自行车数量本质上是一个「多特征数值预测」问题核心诉求是让运营方提前知道某个时段该调度多少车、往哪个站点补车。这篇文章面向两类人一类是刚学完 R 基础语法、想找一个完整项目练手的分析师另一类是用过 Python 建模、想看看 R 生态在回归任务上到底顺不顺手的数据从业者。我会从数据读入、特征工程、模型选型一路写到调参和排错中间穿插我自己踩过的坑。读完你至少能拿到一条可复现的建模链路而不是停留在「知道要用回归」这个层面。2. 数据读入与特征工程把小时级租赁记录变成回归能吃的矩阵2.1 先搞清楚每一列到底是什么类型拿到SeoulBikeData.csv之后第一件事不是急着lm()而是把列类型过一遍。原始文件常见的问题是日期是字符串、季节和节假日是中文或英文类别、降雨量列名带空格或特殊字符。R 读入时如果stringsAsFactors处理不当后续建模会直接报错或者把类别当连续变量处理。# 读入首尔自行车数据注意编码和列名处理 bike - read.csv(SeoulBikeData.csv, fileEncoding UTF-8, stringsAsFactors FALSE) # 统一列名去掉空格和特殊符号 names(bike) - c(date, hour, rented, season, holiday, functioning, temp, humidity, wind, visibility, dew_point, solar, rainfall, snowfall) # 检查各列类型 str(bike) # 日期转 Date类别转 factor bike$date - as.Date(bike$date, format %d/%m/%Y) bike$season - factor(bike$season) bike$holiday - factor(bike$holiday) bike$functioning - factor(bike$functioning)这段代码的关键点有三个fileEncoding防止中文系统下读入乱码手动重命名列是为了后续公式书写方便避免反引号包裹带空格的列名as.Date的格式必须和原始文件一致首尔这份数据常见的是日/月/年写错会得到一片 NA。functioning这一列表示当天系统是否正常运营如果为「No」说明数据不可用建模前应该过滤掉。2.2 从日期和时间里榨出真正有用的特征原始数据给了date和hour但直接把它们丢进回归模型意义不大。真正有预测力的是「星期几」「是否周末」「月份」「是否高峰时段」这类派生特征。这一步做得好不好往往比换模型带来的提升还大。# 派生时间特征 bike$weekday - weekdays(bike$date) bike$is_weekend - ifelse(bike$weekday %in% c(Saturday, Sunday), 1, 0) bike$month - as.integer(format(bike$date, %m)) # 构造高峰时段标记早高峰 7-9晚高峰 17-19 bike$peak - ifelse(bike$hour %in% c(7, 8, 9, 17, 18, 19), 1, 0) # 把 weekday 也转成 factor bike$weekday - factor(bike$weekday, levels c(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)) # 过滤掉系统非正常运营的记录 bike - bike[bike$functioning Yes, ]is_weekend和peak用 0/1 编码而不是 factor是因为它们在回归里更接近二值指示变量解释起来也直观。weekday保留 factor 并手动指定 levels是为了让模型输出的系数顺序可控不然 R 默认按字母排序周三跑到周一前面看结果时容易懵。过滤functioning这一步很多人会忘留着「No」的记录相当于往训练集里掺噪声。2.3 天气变量的非线性处理与共线性检查温度、湿度、风速这些天气变量和租赁量之间往往不是直线关系。温度太低或太高骑车的人都少中间区间最多这是个典型的倒 U 型。直接放线性项会欠拟合常见做法是加二次项或者分箱。# 温度二次项捕捉倒 U 型关系 bike$temp_sq - bike$temp^2 # 检查数值变量之间的相关性 num_cols - c(temp, humidity, wind, visibility, dew_point, solar, rainfall, snowfall) cor_matrix - cor(bike[, num_cols]) print(round(cor_matrix, 2)) # 如果 dew_point 和 temp 相关性过高考虑去掉一个 # 常见做法是保留 temp去掉 dew_point跑完cor之后你会看到dew_point和temp的相关性通常很高这在物理上也好理解——露点温度本身就受气温影响。两个高度共线的变量同时放进线性回归系数估计会不稳定方差膨胀。我一般会保留temp和temp_sq把dew_point拿掉。solar和temp也可能有中等相关但通常没到必须删的程度可以先留着看模型表现。提示特征工程阶段每加一列都问自己一句「这列在预测时刻是否已知」。比如rented的滞后项在真实预测场景里可能拿不到训练时用了会造成数据泄漏。3. 线性回归与正则化从 lm 到 glmnet 的选型逻辑3.1 先用 lm 建立可解释的基线不管最后用什么模型我都会先跑一个普通最小二乘回归当基线。原因很简单它快、系数可解释、能帮你判断哪些特征方向是对的。如果temp的系数是正的、rainfall的系数是负的说明数据没读反。# 建立线性回归基线模型 lm_fit - lm(rented ~ hour temp temp_sq humidity wind visibility solar rainfall snowfall season holiday is_weekend peak, data bike) summary(lm_fit) # 看残差图 par(mfrow c(2, 2)) plot(lm_fit)summary输出里重点看三样调整后 R 方、各系数的 p 值、F 统计量。调整后 R 方告诉你模型解释了多大比例的方差p 值帮你判断哪些变量显著F 统计量看整体是否有效。残差图如果出现明显的喇叭口或者曲线趋势说明线性假设不成立需要考虑变换或者换模型。hour直接作为数值放进去其实不太合理因为小时和租赁量的关系是周期性的23 点和 0 点应该接近而不是差很远。更稳妥的做法是把hour转成 factor或者用正弦余弦编码。# 小时的正弦余弦编码保留周期性 bike$hour_sin - sin(2 * pi * bike$hour / 24) bike$hour_cos - cos(2 * pi * bike$hour / 24)3.2 岭回归和 Lasso什么时候该上正则化当特征数量变多、共线性变严重时lm的系数会变得很大且不稳定。岭回归Ridge通过加 L2 惩罚把系数往零压Lasso 用 L1 惩罚还能顺便做特征选择。在 R 里用glmnet包实现核心是选好lambda。library(glmnet) # 构造设计矩阵factor 会自动展开为哑变量 x - model.matrix(rented ~ hour_sin hour_cos temp temp_sq humidity wind visibility solar rainfall snowfall season holiday is_weekend peak, data bike)[, -1] y - bike$rented # 十折交叉验证选 lambda set.seed(42) cv_ridge - cv.glmnet(x, y, alpha 0, nfolds 10) cv_lasso - cv.glmnet(x, y, alpha 1, nfolds 10) # 最优 lambda 下的系数 coef(cv_ridge, s lambda.min) coef(cv_lasso, s lambda.min)alpha 0是岭回归alpha 1是 Lassoalpha在 0 到 1 之间是弹性网。nfolds 10是十折交叉验证数据量不大时够用。lambda.min是交叉验证误差最小时的 lambda另一个选项lambda.1se更保守会给一个更简单的模型。我一般先用lambda.min看效果如果系数太多不好解释再换lambda.1se。model.matrix那行末尾的[, -1]是去掉截距列因为glmnet默认会自己处理截距。忘了去掉会多一列全 1虽然不致命但没必要。3.3 模型评估别只看 R 方R 方高不代表模型好用尤其是当你在预测任务上关心的是误差量级。我习惯同时看 RMSE、MAE 和 R 方并且用交叉验证而不是训练集上的拟合值来评估。# 用交叉验证预测值评估 pred_ridge - predict(cv_ridge, newx x, s lambda.min) rmse - sqrt(mean((y - pred_ridge)^2)) mae - mean(abs(y - pred_ridge)) r2 - 1 - sum((y - pred_ridge)^2) / sum((y - mean(y))^2) cat(RMSE:, round(rmse, 2), \n) cat(MAE:, round(mae, 2), \n) cat(R2:, round(r2, 3), \n)RMSE 对极端误差更敏感MAE 更稳健两个一起看能判断误差分布是否偏斜。如果 RMSE 远大于 MAE说明存在一些预测得特别离谱的样本值得单独拎出来看看是不是异常值或者特殊天气。4. 树模型与调参随机森林和 XGBoost 在 R 里怎么落地4.1 随机森林几乎不用调参就能打的基准线性模型假设特征和目标的关系是线性的或者你手动构造了非线性项而随机森林能自动捕捉交互和非线性。在 R 里用ranger包比randomForest快不少尤其是数据量上去之后。library(ranger) set.seed(42) rf_fit - ranger(rented ~ hour temp humidity wind visibility solar rainfall snowfall season holiday is_weekend peak, data bike, num.trees 500, mtry 4, importance impurity) print(rf_fit$r.squared) sort(rf_fit$variable.importance, decreasing TRUE)num.trees 500是树的数量一般 300 到 1000 之间够用再多边际收益递减。mtry是每次分裂随机抽取的变量数回归任务默认是总变量数的三分之一左右这里手动设成 4 是试出来的。importance impurity输出变量重要性能帮你看哪些特征贡献大。随机森林的缺点是预测值倾向于落在训练集范围内对极端值的预测能力弱。如果你发现模型从来不会预测出特别高或特别低的租赁量这是原因之一。4.2 XGBoost调参空间大但要抓重点XGBoost 在 R 里有xgboost包性能强但参数多。新手容易一上来就网格搜索所有参数跑半天没结果。我的经验是优先调三个max_depth、eta学习率、nrounds。library(xgboost) # 构造训练矩阵 dtrain - xgb.DMatrix(data x, label y) # 基础参数 params - list( objective reg:squarederror, max_depth 6, eta 0.05, subsample 0.8, colsample_bytree 0.8 ) # 交叉验证找最佳轮数 set.seed(42) cv_xgb - xgb.cv( params params, data dtrain, nrounds 1000, nfold 5, early_stopping_rounds 50, verbose 0 ) best_n - cv_xgb$best_iteration cat(最佳轮数:, best_n, \n) # 用最佳轮数训练最终模型 xgb_fit - xgb.train( params params, data dtrain, nrounds best_n )max_depth 6是树的深度太深容易过拟合太浅欠拟合4 到 8 之间先试。eta 0.05是学习率小一点更稳但需要更多轮数。early_stopping_rounds 50表示如果 50 轮验证误差没下降就停省时间。subsample和colsample_bytree是行采样和列采样比例加一点随机性有助于防过拟合。xgb.cv返回的best_iteration就是交叉验证误差最小时的轮数直接拿来训练最终模型比手动试快得多。4.3 树模型的特征重要性怎么读XGBoost 和随机森林都能输出特征重要性但两者的计算方式不同。随机森林的impurity重要性偏向于取值多的变量XGBoost 的gain更可靠一些。# XGBoost 特征重要性 imp - xgb.importance(model xgb_fit) print(imp) # 可视化 xgb.plot.importance(imp, top_n 10)看重要性时注意hour相关的特征hour_sin、hour_cos或者原始hour通常排最前其次是temp和season。如果rainfall或snowfall排在很后面不一定说明它们不重要而是因为这两个变量大部分时候是 0信息量集中在少数样本上树模型不容易利用。5. 避坑与排查首尔自行车回归建模里最容易翻车的五件事5.1 现象模型 R 方很高但预测值全是平均值附近原因数据泄漏。最常见的是把rented的某种变换比如对数、滞后当特征或者训练集和测试集没有按时间切分随机切分导致相邻小时的信息互相渗透。解决按时间顺序切分训练集和测试集比如前 80% 的时间段做训练后 20% 做测试。如果一定要随机切分至少确保同一日期的记录不跨集。5.2 现象glmnet报错「x 和 y 长度不一致」原因model.matrix生成的矩阵行数和y不一致通常是因为构造x时用了na.omit或者数据里有 NA而y没有同步过滤。解决在构造x和y之前先统一处理缺失值或者用同一个索引过滤。complete_idx - complete.cases(bike[, c(rented, temp, humidity)]) bike_clean - bike[complete_idx, ]5.3 现象随机森林预测值范围比真实值窄很多原因随机森林的预测是叶子节点均值的平均天然倾向于向均值收缩对极端值的预测能力弱。解决如果业务上关心高峰时段的预测精度可以对目标变量做变换比如开方或者换用 XGBoost 并调大max_depth。另一个思路是分时段建模高峰和非高峰各训一个模型。5.4 现象hour作为数值放进线性回归残差图呈周期性波动原因小时是周期性变量23 和 0 在数值上差 23但在业务上只差 1 小时。线性模型无法捕捉这种循环关系。解决用正弦余弦编码或者把hour转成 factor。正弦余弦编码更省自由度factor 更灵活但会增加很多哑变量。5.5 现象XGBoost 训练时验证误差先降后升原因过拟合。树的数量太多或者max_depth太大。解决用early_stopping_rounds自动停在最佳轮数同时适当减小max_depth、增大subsample比例。如果数据量小还可以加min_child_weight参数限制叶子节点的最小样本数。6. 把模型推到可用时间序列交叉验证与残差诊断的实操细节前面用的都是普通交叉验证但自行车租赁数据本质上是时间序列随机切分会让模型「偷看」未来。更严谨的做法是滚动窗口交叉验证R 里可以用rsample包的rolling_origin实现。library(rsample) # 按时间排序 bike_sorted - bike[order(bike$date, bike$hour), ] # 滚动窗口交叉验证 rolls - rolling_origin( data bike_sorted, initial 4000, assess 500, cumulative TRUE ) # 对每个切分跑模型并记录 RMSE rmse_list - c() for (i in 1:nrow(rolls)) { train_data - analysis(rolls$splits[[i]]) test_data - assessment(rolls$splits[[i]]) fit - ranger(rented ~ hour temp humidity wind visibility solar rainfall snowfall season holiday is_weekend peak, data train_data, num.trees 300) pred - predict(fit, data test_data)$predictions rmse_list[i] - sqrt(mean((test_data$rented - pred)^2)) } cat(滚动窗口 RMSE 均值:, round(mean(rmse_list), 2), \n) cat(滚动窗口 RMSE 标准差:, round(sd(rmse_list), 2), \n)initial 4000表示第一个训练窗口用 4000 条记录assess 500表示每次用后续 500 条做测试cumulative TRUE表示训练窗口逐步扩大。这样评估出来的 RMSE 更接近真实上线后的表现。如果滚动窗口的 RMSE 标准差很大说明模型在不同时间段的表现不稳定可能需要加入更多时间特征或者分季节建模。残差诊断也别跳过。把残差按小时画箱线图看看有没有某些时段系统性偏高或偏低。# 残差按小时分析 bike_sorted$resid - bike_sorted$rented - predict(rf_fit, data bike_sorted)$predictions boxplot(resid ~ hour, data bike_sorted, main 残差按小时分布)如果早上 8 点的残差中位数明显偏离 0说明模型在这个时段有系统性偏差可以考虑单独为高峰时段加特征或者调整样本权重。最后说一个我自己的习惯每次跑完模型我都会挑几天把预测值和真实值画在同一张图上肉眼过一遍。有一次我发现模型在雨天预测得特别差回去查数据才发现降雨量列有大量 0 值但实际是「微量降雨」被记成了 0这种问题光看指标是发现不了的。模型分析这件事指标是底线肉眼是保险。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号