简介面向数据科学初学者与有一定统计基础、希望上手监督学习建模的分析人员这份资源以R语言实现多种机器学习算法覆盖回归预测、模型对比与数据预处理等常见场景。压缩包共20个文件以R脚本和CSV数据集为主体8个R文件涵盖简单/多元线性回归、多项式回归、决策树回归、SVR支持向量回归与数据预处理模板6个CSV文件提供可直接运行的示例数据另附LICENSE说明整体仅16KB轻量便捷。已有243人学习下载说明资源对基础巩固有一定参考价值。通过研读脚本结构、调整参数并重跑实验读者不仅能掌握各类回归算法的R实现方式还能理解数据清洗、特征缩放与模型评估的基本流程为后续拓展分类、聚类等更多ML算法打下扎实基础。1. 为什么这份 R 语言机器学习代码值得跑一遍很多人觉得 R 语言不适合做机器学习一上来就转 Python其实是把 R 当成统计画图工具了。R 里既有原生的rpart、randomForest、e1071也有caret、tidymodels这样的统一建模框架从数据清洗到调参预测是一条完整链路。这份资源把这些算法实现收到一起覆盖线性回归、逻辑回归、决策树、随机森林、SVM、KNN 和 XGBoost适合刚把 R 语法看完、希望直接照着写出可运行模型的读者。下面按我实际跑一遍的顺序讲清楚目录结构、依赖安装、参数设置和最容易翻车的地方。2. 把资源拆开看目录结构、依赖包与运行环境拿到压缩包之后我的习惯不是直接点开某个.R文件运行而是先把目录结构捋一遍。资源包的脚本组织方式直接决定了你要不要改路径、要不要手动建目录这一步没做好后面所有代码都会在file not found上反复折腾。2.1 资源包的真实骨架脚本按算法分目录README 负责连线解压后通常能看到下面这种结构脚本按「数据预处理 算法族」拆分数据和说明文档放在独立目录ml-r/ ├── README.md ├── data/ │ ├── demo_data.csv │ └── data_description.md ├── scripts/ │ ├── 01_data_preprocess.R │ ├── 02_linear_regression.R │ ├── 03_logistic_regression.R │ ├── 04_decision_tree_rf.R │ ├── 05_svm_knn.R │ ├── 06_xgboost.R │ └── 99_tuning_caret.R └── outputs/ └── model_results.txt资源包里的README.md一般会写明执行顺序先跑01_data_preprocess.R再跑02到06中的任意算法脚本最后用99_tuning_caret.R统一调参。这不是随便排列的01脚本负责读入demo_data.csv、处理缺失值、标准化数值列、切分训练集和测试集后面所有算法脚本都能复用它生成的数据对象省去重复造轮子。outputs/目录在新版资源包里经常是空的脚本跑起来后会把混淆矩阵、AUC 和特征重要性写进model_results.txt。我一般会先确认这个目录存在如果不存在就在01脚本里加一句dir.create(outputs, showWarnings FALSE)避免后面write.csv报错。还有一个容易被忽略的点数据文件用 UTF-8 编码但列名带中文时read.csv()默认的fileEncoding可能读乱码。常见做法是读入前先readLines(data/demo_data.csv, n 1)看第一行再决定是否加fileEncoding UTF-8-BOM。这样的细节通常不会写进 README但真实项目里十次有八次会遇到。2.2 R 版本与依赖包装包不该成为劝退点资源脚本主要跑在 R 4.1 以上。太老的 R 版本会直接导致caret、xgboost装不上我遇到过package xgboost is not available for R version 3.6.3这种报错解决方案不是换包而是先把 R 升级。依赖包一条命令装齐necessary - c( caret, # 统一调参和交叉验证框架 rpart, # CART 决策树 rpart.plot, # 决策树可视化 randomForest, # 随机森林 e1071, # SVM 和朴素贝叶斯 class, # KNN 基础包 kernlab, # 另一个 SVM 实现caret 的 svmRadial 常用它 glmnet, # Lasso / Ridge / Elastic Net xgboost, # 梯度提升树 dplyr, # 数据清洗 ggplot2, # 画图 ROCR, # ROC 和 AUC pROC # 替代 ROCR 的另一个选择 ) install.packages(necessary)参数说明install.packages接受一个字符向量可以把所有包一次性装完。国内网络环境下建议先把options(repos c(CRAN https://cloud.r-project.org))写到~/.Rprofile不然装包会卡在选择镜像那一步。Linux 系统上xgboost和glmnet经常需要从源码编译常见做法是先装好系统的编译依赖再装包否则会看到长长的gcc警告后退出。caret的依赖链比较长装的时候会带进来lattice、ggplot2、MASS等一堆包时间久是正常的不是死机。如果只打算验证前几个算法先装rpart、randomForest、e1071、class就够跑完一半脚本。这里要特别提一下kernlab有些脚本用的svm来自e1071有些用kernlab的ksvm。资源包里05_svm_knn.R通常两个都装了但caret的method svmRadial底层依赖kernlab。只装e1071的人跑到caret调参会突然发现少包报错信息又不够明显所以我习惯一起装上。3. 从一份 CSV 到模型预测数据划分、标准化与五个常见算法这一章是资源包的核心使用场景。我不打算逐个脚本抄代码而是把跑通一条完整链路的公共动作拆出来照着这个顺序走五个算法都能顺滑出结果。3.1 读数据与划分训练集set.seed 是第一个习惯资源包里的demo_data.csv是一份模拟的客户分类数据特征包含数值型和少量分类变量目标列叫label。第一步永远是把数据读进来然后做两件看似多余但必须做的事去掉整列缺失的变量去掉只有一个取值的常量列。library(dplyr) df - read.csv(data/demo_data.csv, stringsAsFactors TRUE) # 去掉整列全为缺失的变量空列会让后面的建模函数直接退出 df - df[, colSums(is.na(df)) nrow(df)] # 去掉只有一个取值的常量列否则随机森林会把它误判为高重要度特征 df - df[, sapply(df, function(x) length(unique(x)) 1)] set.seed(2024) train_idx - sample(seq_len(nrow(df)), size floor(0.75 * nrow(df))) train - df[train_idx, ] test - df[-train_idx, ] table(train$label)逻辑说明colSums(is.na(df)) nrow(df)保留了「至少有一个非缺失值」的列而不是把所有含缺失的列都删掉。length(unique(x)) 1过滤掉常量列这类列不携带信息还会干扰特征重要性排序。sample()按 75% 和 25% 比例切分训练集和测试集set.seed(2024)保证你每次重跑得到的切分一致资源包 README 里如果指定了其他种子以资源包为准。参数说明stringsAsFactors TRUE让字符列自动变成因子避免后面建模时再手动转换。size floor(0.75 * nrow(df))是常见的 75/25 划分数据量很小时我建议改用createDataPartition()做分层抽样避免某个类别在测试集里恰好没出现。跑完这段后先执行table(train$label)目的是确认两个类别的数量不是夸张的 99:1。如果已经严重偏斜后面的随机森林和逻辑回归都会出问题先在这一步停下来处理要比建模后补救省事得多。3.2 决策树与随机森林先跑通再谈调参决策树是资源包里最好上手的算法因为不用做特征标准化特征间的量纲不影响树的分裂。随机森林则能直接看出哪些特征对预测贡献最大。library(rpart) library(rpart.plot) library(randomForest) # 决策树cp 控制剪枝强度 fit_tree - rpart(label ~ ., data train, method class, control rpart.control(minsplit 10, cp 0.01)) rpart.plot(fit_tree) pred_tree - predict(fit_tree, test, type class) # 随机森林先不要贪 ntree300 够看趋势了 fit_rf - randomForest(label ~ ., data train, ntree 300, mtry max(1, floor(sqrt(ncol(train) - 1))), importance TRUE) pred_rf - predict(fit_rf, test)逻辑说明rpart()的control里minsplit 10表示节点样本少于 10 个就不再分裂cp 0.01表示复杂度参数阈值为 0.01相差不到 1% 的分裂不会被保留。决策树的价值不在预测精度而在可视化rpart.plot()画出的树能直接解释模型逻辑。随机森林的importance TRUE会生成两个重要度指标MeanDecreaseAccuracy和MeanDecreaseGini。跑完后用varImpPlot(fit_rf)画图常用来做特征筛选。mtry默认在分类问题里是floor(sqrt(特征数))我这里是手动算的顺便用max(1, ...)防止特征数太少时出现 0。参数说明ntree从 300 开始就好只看趋势不需要一上来跑 1000 棵等调参阶段再加大。method class是分类树若目标列是连续值要改成anova。资源包里的04_decision_tree_rf.R写法和这里几乎一致但有些版本会多一个na.action na.omit这是因为rpart遇到缺失值默认会自动处理一部分但不一定符合你预期建议在清洗阶段把缺失行先删掉。3.3 SVM 与 KNN标准化不到位距离算法全废这是整个资源包里最容易踩坑的一段。SVM 和 KNN 都依赖样本之间的距离如果某个特征量纲特别大比如收入从 0 到 100 万年龄只有 0 到 80那么收入会主导整个距离计算年龄等于没参与。训练集和测试集必须使用同一套标准化参数。num_cols - sapply(train, is.numeric) # 用训练集的均值和标准差做标准化测试集不能用自身的均值 train_mat - scale(train[, num_cols]) test_mat - scale(test[, num_cols], center attr(train_mat, scaled:center), scale attr(train_mat, scaled:scale)) train_svm - data.frame(label train$label, as.data.frame(train_mat)) test_svm - data.frame(label test$label, as.data.frame(test_mat)) library(e1071) fit_svm - svm(label ~ ., data train_svm, kernel radial, cost 1, gamma 0.1) pred_svm - predict(fit_svm, test_svm) library(class) pred_knn - knn(train as.data.frame(train_mat), test as.data.frame(test_mat), cl train$label, k 7)逻辑说明scale()默认用每列的均值和标准差做标准化去掉缺失值后再跑不然会产生 NaN。关键在test_mat那两行center和scale必须取自train_mat的属性不能分别对测试集做scale()否则测试集被挪到了另一套坐标系训练和预测就不一致了。SVM 里我用的kernel radial是高斯核cost 1是正则化参数越大越容易过拟合gamma 0.1控制单个样本的影响范围越小边界越平滑。KNN 的k 7表示投票的邻居数奇数避免平票。参数说明knn()的cl参数是训练集标签必须是因子或字符向量。train_svm里用data.frame(label train$label, as.data.frame(train_mat))把标签和特征拼在一个表里这样svm(label ~ .)才能正常解析公式。如果分类变量占多数免费建议先对分类变量做 one-hot 编码否则 KNN 的距离计算会把字符串坐标当成离散整数语义上很奇怪。3.4 逻辑回归与 XGBoost两种概率输出的写法逻辑回归和 XGBoost 是资源包最后两个算法脚本的常客。前者适合做基准模型后者通常用来刷指标。library(glmnet) library(xgboost) # 标签转 0/1glmnet 不接受因子 y_tr - ifelse(train$label levels(train$label)[1], 0, 1) # alpha0 走岭回归alpha1 走 lasso想兼顾就试中间值 fit_glm - cv.glmnet(as.matrix(train_svm), y_tr, family binomial, alpha 0) pred_glm_prob - predict(fit_glm, as.matrix(test_svm), type response) pred_glm_class - ifelse(pred_glm_prob 0.5, levels(train$label)[2], levels(train$label)[1]) dtrain - xgb.DMatrix(as.matrix(train_svm), label y_tr) dtest - xgb.DMatrix(as.matrix(test_svm)) params - list(objective binary:logistic, eta 0.1, max_depth 3, subsample 0.8) fit_xgb - xgb.train(params, dtrain, nrounds 200, verbose 0) pred_xgb_prob - predict(fit_xgb, dtest)逻辑说明glmnet要求输入是数值矩阵所以先用as.matrix()把标准化后的数据框转成矩阵。family binomial指定二分类逻辑回归alpha是弹性网参数0 表示纯岭回归1 表示纯 lasso0.5 表示两者平衡。我用cv.glmnet()会自动跑交叉验证选 lambdatype response返回的是概率而不是类别。XGBoost 里objective binary:logistic要求标签是 0/1eta是学习率max_depth控制树深subsample是每棵树随机采样的比例0.8 表示只用 80% 样本。nrounds 200是迭代轮数资源包脚本里通常在后面还会接xgb.cv()来选最佳轮数。参数说明如果要做多分类把objective改成multi:softprob同时加一个num_class 3这样的参数。回归任务则用reg:squarederror。注意这里的train_svm还带着标准化前的标签吗其实没有train_svm是标签加标准化特征的数据框正好可以直接转矩阵。4. 参数调优与交叉验证用网格搜索把玄学变成流程跑通五个算法只是第一步资源包后半部分的价值在调参。手动改参数试错不是不行但效率太低。我一般把调参分成两层参数少时用caret统一跑参数多时手动网格搜索加交叉验证。4.1 caret 统一调参trainControl 与 tuneGrid 配合caret的好处是提供了一个统一接口不同算法的参数通过tuneGrid传入不需要记每个包各自的语法。library(caret) # twoClassSummary 要求标签水平是合法 R 变量名 train$label - factor(train$label, levels c(no, yes)) ctrl - trainControl(method cv, number 5, classProbs TRUE, summaryFunction twoClassSummary) grid - expand.grid(mtry c(2, 4, 6)) fit_caret - train(label ~ ., data train, method rf, trControl ctrl, tuneGrid grid, metric ROC, ntree 200) print(fit_caret)逻辑说明trainControl里method cv表示交叉验证number 5是折数classProbs TRUE让模型输出每个类别的概率twoClassSummary告诉caret优化指标是 ROC 下的 AUC 而不是准确率。tuneGrid expand.grid(mtry c(2,4,6))会让caret分别用mtry 2、4、6训练三次每次内部再做 5 折交叉验证最终选出平均 ROC 最高的配置。这里最容易翻车的点是标签水平名。twoClassSummary内部要生成yes、no这类预测列如果标签是0、1或者带空格的字符串就会报class levels are not valid R variable names。资源包里很多脚本直接用了read.csv()后的默认因子跑调参前务必先levels(train$label)看一眼。参数说明metric ROC只适用于二分类。多分类时可以换成metric Accuracy或metric Kappa同时summaryFunction改成defaultSummary()。tuneLength也可以代替tuneGrid它会按默认参数范围自动生成网格但可控性比手动expand.grid差。4.2 评估指标与混淆矩阵别只盯着准确率调参完成后下一步是评估测试集上的表现。很多初学者只看accuracy但类别不平衡时准确率会骗人。library(ROCR) pred_prob - predict(fit_rf, test, type prob)[, yes] pred - prediction(pred_prob, test$label) perf - performance(pred, tpr, fpr) plot(perf) abline(a 0, b 1, lty 2) auc_value - performance(pred, auc)y.values[[1]] cat(AUC:, auc_value, \n) cm - confusionMatrix(predict(fit_rf, test), test$label) print(cm$byClass)逻辑说明prediction()把真实标签和预测概率打包成 ROCR 的专用对象performance()计算真正率tpr和假正率fpr的序列plot()画出 ROC 曲线abline()画对角参考线。performance(pred, auc)y.values[[1]]提取 AUC 值。AUC 在 0.5 到 1 之间0.5 相当于瞎猜越接近 1 越好。confusionMatrix()返回的byClass列表中包含敏感性、特异性、阳性预测值、阴性预测值等。当少数类样本很少时Accuracy可能 0.95 以上但少数类的敏感性只有 0.1这种情况就要靠 AUC 和灵敏度来判断模型是否真的在学习而不是无脑预测多数类。参数说明predict(fit_rf, test, type prob)返回一个列名和因子水平对应的概率矩阵取第二列时要确认顺序。如果因子水平是no、yes第二列就是yes的概率。这里写死的yes是资源包数据的实际水平名换数据时必须适配。5. 避坑排查R语言跑机器学习最常翻车的五个点下面这五条是我跑资源包时遇到的真实日志按「现象 → 原因 → 解决」写清楚。每条都能对应到具体的 R 报错文本建议存到自己的笔记里。5.1 缺失值导致报错 missing value where TRUE/FALSE needed现象脚本跑到rpart()或者randomForest()时报missing value where TRUE/FALSE needed有时完整提示是cannot open connection但打开文件名又没有问题。原因数据里有NA或NaN。rpart能容忍一部分缺失值但glmnet、knn、xgboost完全不能接受。更重要的是table()、ifelse()这类函数遇到缺失值会返回NA一旦NA被当成判断条件就会爆出上面的提示。解决建模前统一做缺失值处理最省事的做法是df - na.omit(df)但会删除整行数据样本少时慎用。更推荐用dplyr::drop_na(label)只删目标列缺失的行再利用中位数或众数对特征列做填充。跑算法前先跑sum(is.na(df))确认结果是 0 再往下走。5.2 预测时因子水平不一致训练集和测试集级别对不上现象predict(fit, test)报错factor city has new levels或者预测出的类别数量和测试集行数对不上。原因训练集里某个分类特征有 10 个水平测试集里只有 8 个或者测试集出现了训练集里从没见过的值。R 的model.matrix()在构造哑变量时遇到未注册的水平就无法处理。解决切分前先做水平合并。比如计算每个水平的频次低于阈值的统一合并为otherlow_freq - names(which(table(df$city) 10)) df$city[df$city %in% low_freq] - other df$city - factor(df$city)合并之后再做数据切分测试集自然会沿用训练集的水平集合。还有一种临时补救法是levels(test$city) - levels(train$city)但新增水平会被强制变成NA治标不治本。5.3 scale 后的矩阵把列名弄丢了现象用scale()标准化后塞进data.frame()列名变成V1、V2这种编号后续predict()报variable names are not the same as in the training data。原因scale()返回的是矩阵as.data.frame()默认矩阵没有列名时会自动生成V1这样的名字。如果矩阵是从某个子集取出来的列名可能在创建时已经丢失。解决标准化前先确认列名存在。标准化的正确姿势是直接构造data.frame(label y, as.data.frame(train_mat))train_mat的列名会保留。如果已经丢失用colnames(train_mat) - colnames(train)[num_cols]手动补回去测试集也必须用完全相同的列名顺序。5.4 caret 对标签命名很敏感0/1 数字标签会报错现象train()加classProbs TRUE和twoClassSummary后直接报At least one of the class levels is not a valid R variable name。原因twoClassSummary要生成yes、no这样的概率列名如果因子水平是0、1或者good customer这类带空格和符号的字符串R 变量命名规则不允许。解决调参前先转换因子水平train$label - factor(train$label, levels c(0, 1), labels c(no, yes))转换后不要忘记在测试集上也执行同样的操作保证两边水平一致。如果是多分类不要用twoClassSummary改用multiClassSummary并注意metric不要设为ROC。5.5 随机森林对类别不平衡很敏感现象混淆矩阵里少数类召回率极低预测结果几乎全落到多数类但整体准确率还挺高。原因randomForest()默认按原始类别比例抽样多数类天然有更多决策树投票。比例悬殊到 90:10 时少数类基本被淹没。解决用sampsize和strata参数做平衡抽样fit_rf_balanced - randomForest(label ~ ., data train, ntree 300, sampsize c(80, 80), strata train$label)这里的c(80, 80)是手动指定的每个类别的采样数量需要根据训练集中每类的实际数量来设通常取较小那一类的数量。如果只想在数据层面解决也可以用caret的downSample()做成比例更平衡的训练集但downSample()会大量丢弃数据样本本身不多时要慎重。6. 进阶用法把一个模型链路封装成可复用函数资源包里的脚本风格是「一个算法一个文件」跑通之后我建议再往上走一步把数据读取、切分、训练、预测封装成一个函数之后换数据集时只改参数不反复复制粘贴。quick_train - function(df, target, method rf, seed 2024) { df[[target]] - factor(df[[target]]) set.seed(seed) idx - sample(seq_len(nrow(df)), size floor(0.75 * nrow(df))) train - df[idx, ] test - df[-idx, ] formula - as.formula(paste(target, ~ .)) model - caret::train( formula, data train, method method, trControl caret::trainControl( method cv, number 5, classProbs TRUE, summaryFunction twoClassSummary ), metric ROC, tuneLength 3 ) list( model model, train train, test test, pred predict(model, test) ) }这个函数把caret的调参链路收拢成一行调用更适合快速验证多个算法。使用时有三个注意点method换成svmRadial、xgbTree、glmnet时内部默认的tuneGrid各不相同tuneLength 3只能给出一个粗略结果精调阶段还是要回到手动网格twoClassSummary要求二分类目标换成回归任务时要把metric和summaryFunction一起改掉测试集的标准化参数没有在函数里处理如果算法对量纲敏感你需要把train、test传入前先在外面完成标准化否则函数会直接使用原始特征。我自己的习惯是把这个函数放在一个utils_run.R文件里每次拿到新数据集先跑一遍quick_train(df, label, rf)确认链路没断再去深挖特征工程和参数细节。曾经有一回我跳过这一步直接调参数结果折腾半天发现是数据读入时某个字符串列被read.csv自动转成了因子预测阶段new levels报错浪费了整整一个下午。从那以后我每次跑新数据都强制先走一遍这个最小闭环再考虑优化模型。希望帮到你。本文还有配套的精品资源点击获取