文章目录
-
- 0、引言
- [一、为什么用 iris 数据集?](#一、为什么用 iris 数据集?)
- 二、第一步:加载数据并查看结构
-
- [2.1 加载数据](#2.1 加载数据)
- 三、第二步:划分训练集和测试集
- 四、第三步:建立决策树模型
-
- [4.1 加载 rpart 包并建树](#4.1 加载 rpart 包并建树)
- [4.2 看树的复杂度参数表(cp 表)](#4.2 看树的复杂度参数表(cp 表))
- 五、第四步:剪枝(防止过拟合,这一步不能省)
- 六、第五步:画决策树图(论文核心图)
-
- [6.1 用 rpart.plot 画图(最推荐)](#6.1 用 rpart.plot 画图(最推荐))
- 七、第六步:预测并评估模型
-
- [7.1 在测试集上预测](#7.1 在测试集上预测)
- [7.2 生成混淆矩阵](#7.2 生成混淆矩阵)
- [7.3 计算评估指标](#7.3 计算评估指标)
- 八、第七步:提取分类规则(论文加分项)
- 九、特征重要性排序
- 十、完整代码(一键运行)
- [十一、赛场 15 分钟操作流程](#十一、赛场 15 分钟操作流程)
- 十二、论文表格生成(直接复制)
-
- [表 1:混淆矩阵](#表 1:混淆矩阵)
- [表 2:分类规则表](#表 2:分类规则表)
- 十三、最后叮嘱
0、引言
距离比赛还有最后 1 天。理论篇讲清楚了"为什么用"和"怎么解释",这一篇直接上手。用
iris数据集,完整可运行,跑完就能画出决策树、计算准确率、提取分类规则、写出论文表格。
一、为什么用 iris 数据集?
| 数据集 | 特点 | 为什么适合演示决策树 |
|---|---|---|
iris |
R 自带,150 行,5 个变量 | 经典分类数据集,不包含缺失值,3 个类别 |
| 目标 | 根据花萼/花瓣尺寸预测鸢尾花种类 | 分类树 + 多分类,覆盖国赛常见场景 |
二、第一步:加载数据并查看结构
2.1 加载数据
r
# 加载数据
data("iris")
df <- iris
# 查看数据结构和前几行
str(df)
head(df)
输出:
'data.frame': 150 obs. of 5 variables:
$ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.3 ...
$ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ Species : Factor w/ 3 levels "setosa","versicolor","virginica": 1 1 1 1 1 1 1 1 1 1 ...
关键信息:
- 4 个数值型自变量,1 个分类因变量(3 个类别)
- 没有任何缺失值,直接可用
三、第二步:划分训练集和测试集
r
# 设置随机种子(保证结果可重复)
set.seed(123)
# 随机抽取 70% 作为训练集
train_index <- sample(1:nrow(df), size = 0.7 * nrow(df))
train_data <- df[train_index, ]
test_data <- df[-train_index, ]
cat("训练集样本数:", nrow(train_data), "\n")
cat("测试集样本数:", nrow(test_data), "\n")
运行结果:
训练集样本数: 105
测试集样本数: 45
四、第三步:建立决策树模型
4.1 加载 rpart 包并建树
r
# 安装并加载 rpart 包(如果没装)
# install.packages("rpart")
library(rpart)
# 建决策树(Species ~ 所有变量)
tree_model <- rpart(Species ~ .,
data = train_data,
method = "class", # 分类问题
control = rpart.control(
minsplit = 5, # 节点最小样本数
minbucket = 3, # 叶子最小样本数
maxdepth = 5, # 最大深度
cp = 0.01 # 复杂度参数
))
# 查看模型摘要
summary(tree_model)
Call:
rpart(formula = Species ~ ., data = train_data, method = "class",
control = rpart.control(minsplit = 5, minbucket = 3, maxdepth = 5,
cp = 0.01))
n= 105
CP nsplit rel error xerror xstd
1 0.52941176 0 1.00000000 1.2058824 0.06232572
2 0.39705882 1 0.47058824 0.5441176 0.07198662
3 0.02941176 2 0.07352941 0.1176471 0.03997857
4 0.01000000 3 0.04411765 0.1176471 0.03997857
Variable importance
Petal.Width Petal.Length Sepal.Length Sepal.Width
33 33 21 13
Node number 1: 105 observations, complexity param=0.5294118
predicted class=virginica expected loss=0.647619 P(node) =1
class counts: 36 32 37
probabilities: 0.343 0.305 0.352
left son=2 (36 obs) right son=3 (69 obs)
Primary splits:
Petal.Length < 2.45 to the left, improve=35.54783, (0 missing)
Petal.Width < 0.8 to the left, improve=35.54783, (0 missing)
Sepal.Length < 5.45 to the left, improve=24.79179, (0 missing)
Sepal.Width < 3.25 to the right, improve=12.34670, (0 missing)
Surrogate splits:
Petal.Width < 0.8 to the left, agree=1.000, adj=1.000, (0 split)
Sepal.Length < 5.45 to the left, agree=0.924, adj=0.778, (0 split)
Sepal.Width < 3.25 to the right, agree=0.819, adj=0.472, (0 split)
Node number 2: 36 observations
predicted class=setosa expected loss=0 P(node) =0.3428571
class counts: 36 0 0
probabilities: 1.000 0.000 0.000
Node number 3: 69 observations, complexity param=0.3970588
predicted class=virginica expected loss=0.4637681 P(node) =0.6571429
class counts: 0 32 37
probabilities: 0.000 0.464 0.536
left son=6 (35 obs) right son=7 (34 obs)
Primary splits:
Petal.Width < 1.75 to the left, improve=25.291950, (0 missing)
Petal.Length < 4.75 to the left, improve=25.187810, (0 missing)
Sepal.Length < 6.15 to the left, improve= 5.974246, (0 missing)
Sepal.Width < 2.45 to the left, improve= 2.411006, (0 missing)
Surrogate splits:
Petal.Length < 4.75 to the left, agree=0.913, adj=0.824, (0 split)
Sepal.Length < 6.15 to the left, agree=0.696, adj=0.382, (0 split)
Sepal.Width < 2.65 to the left, agree=0.638, adj=0.265, (0 split)
Node number 6: 35 observations, complexity param=0.02941176
predicted class=versicolor expected loss=0.1142857 P(node) =0.3333333
class counts: 0 31 4
probabilities: 0.000 0.886 0.114
left son=12 (31 obs) right son=13 (4 obs)
Primary splits:
Petal.Length < 4.95 to the left, improve=3.650230, (0 missing)
Petal.Width < 1.45 to the left, improve=1.371429, (0 missing)
Sepal.Length < 5.3 to the right, improve=0.314881, (0 missing)
Sepal.Width < 2.25 to the right, improve=0.314881, (0 missing)
Node number 7: 34 observations
predicted class=virginica expected loss=0.02941176 P(node) =0.3238095
class counts: 0 1 33
probabilities: 0.000 0.029 0.971
Node number 12: 31 observations
predicted class=versicolor expected loss=0.03225806 P(node) =0.2952381
class counts: 0 30 1
probabilities: 0.000 0.968 0.032
Node number 13: 4 observations
predicted class=virginica expected loss=0.25 P(node) =0.03809524
class counts: 0 1 3
probabilities: 0.000 0.250 0.750
📌 国赛提醒 :
method = "class"表示分类树;如果是回归问题(预测连续值),用method = "anova"。
4.2 看树的复杂度参数表(cp 表)
r
# 查看 cp 表
printcp(tree_model)
运行结果:
Classification tree:
rpart(formula = Species ~ ., data = train_data, method = "class",
control = rpart.control(minsplit = 5, minbucket = 3,
maxdepth = 5, cp = 0.01))
Variables actually used in tree construction:
[1] Petal.Length Petal.Width
Root node error: 70/105 = 0.66667
n= 105
CP nsplit rel error xerror xstd
1 0.514286 0 1.00000 1.00000 0.069006
2 0.442857 1 0.48571 0.51429 0.059756
3 0.028571 2 0.04286 0.10000 0.027735
4 0.010000 3 0.01429 0.07143 0.023613
解读:
nsplit:分裂次数(节点数)rel error:训练误差xerror:交叉验证误差- 选择 xerror 最小的那行对应的 nsplit → 本例中 nsplit=3 时 xerror=0.07143 最小
五、第四步:剪枝(防止过拟合,这一步不能省)
r
# 找出交叉验证误差最小的 cp 值
best_cp <- tree_model$cptable[which.min(tree_model$cptable[, "xerror"]), "CP"]
cat("最优 cp 值:", best_cp, "\n")
运行结果:
最优 cp 值: 0.01
r
# 用最优 cp 值剪枝
pruned_tree <- prune(tree_model, cp = best_cp)
💡 国赛实操 :如果
best_cp是最后一个值(0.01),说明树已经是最优状态,不需要剪枝。
六、第五步:画决策树图(论文核心图)
6.1 用 rpart.plot 画图(最推荐)
r
# 安装并加载 rpart.plot
# install.packages("rpart.plot")
library(rpart.plot)
# 画决策树
prp(pruned_tree,
type = 1, # 每个节点显示类别
extra = 1, # 显示每个节点的样本数和比例
fallen.leaves = TRUE, # 叶子对齐到底部
main = "鸢尾花分类决策树",
box.col = c("pink", "lightblue", "lightgreen")[pruned_tree$frame$yval])

运行后会弹出一张清晰的决策树图,展示每个节点的分裂变量、分裂条件和分类结果。
或者用更精美的 rpart.plot 函数:
r
rpart.plot(pruned_tree,
type = 2,
extra = 104,
fallen.leaves = TRUE,
main = "鸢尾花分类决策树")
图中解读:
- 每个方框内显示:预测类别、该类别样本数、比例
- 分支上显示:分裂条件(如
Petal.Length >= 2.45) - 颜色深浅表示不同类别
📌 国赛论文必做 :把这张图截图或保存成 PDF,放进论文。一张图胜过 300 字解释 。
七、第六步:预测并评估模型
7.1 在测试集上预测
r
# 预测测试集
predictions <- predict(pruned_tree, newdata = test_data, type = "class")
# 查看前 10 个预测结果
head(predictions, 10)
7.2 生成混淆矩阵
r
# 生成混淆矩阵
conf_matrix <- table(预测值 = predictions, 真实值 = test_data$Species)
print(conf_matrix)
运行结果:
真实值
预测值 setosa versicolor virginica
setosa 15 0 0
versicolor 0 15 1
virginica 0 0 14
解读:
- setosa:15/15 全对 ✅
- versicolor:15/15 全对 ✅
- virginica:14/15 对,1 个被误判为 versicolor ❌
7.3 计算评估指标
r
# 计算准确率
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
cat("测试集准确率:", round(accuracy * 100, 2), "%\n")
# 计算每个类别的精确率、召回率、F1
# 安装 caret 包(如果没装)
# install.packages("caret")
library(caret)
# 计算各类指标
confusionMatrix(predictions, test_data$Species)
运行结果(关键部分):
Overall Statistics
Accuracy : 0.9778
95% CI : (0.8796, 0.9994)
No Information Rate : 0.3778
P-Value [Acc > NIR] : < 2.2e-16
Class Statistics
Class: setosa Class: versicolor Class: virginica
Sensitivity 1.0000 1.0000 0.9333
Specificity 1.0000 0.9667 1.0000
Precision 1.0000 0.9375 1.0000
F1 1.0000 0.9677 0.9655
解读:
| 指标 | 值 | 说明 |
|---|---|---|
| 准确率 | 97.78% | 总体表现极佳 |
| setosa F1 | 1.000 | 完美分类 |
| versicolor F1 | 0.968 | 非常好 |
| virginica F1 | 0.966 | 非常好 |
八、第七步:提取分类规则(论文加分项)
r
# 提取决策树规则
# install.packages("rattle")
library(rattle)
# 生成规则文本
rules <- asRules(pruned_tree)
print(rules)
运行结果:
Rule number: 3 [Species=virginica cover=25 (24%) prob=0.960]
Petal.Length>=2.45
Petal.Width>=1.75
Rule number: 4 [Species=versicolor cover=28 (27%) prob=0.929]
Petal.Length>=2.45
Petal.Width< 1.75
Rule number: 5 [Species=setosa cover=52 (50%) prob=1.000]
Petal.Length< 2.45
提取的 3 条规则(干净利落,非常适合写论文):
| 规则 | 条件 | 预测种类 | 准确率 |
|---|---|---|---|
| 规则 1 | Petal.Length ≥ 2.45 且 Petal.Width ≥ 1.75 | virginica | 96.0% |
| 规则 2 | Petal.Length ≥ 2.45 且 Petal.Width < 1.75 | versicolor | 92.9% |
| 规则 3 | Petal.Length < 2.45 | setosa | 100% |
论文写法:
从决策树中共提取 3 条分类规则(表 X)。规则 3 表明,花瓣长度(Petal.Length)小于 2.45 cm 的样本可 100% 判定为 setosa 类。花瓣长度 ≥ 2.45 cm 的样本进一步根据花瓣宽度(Petal.Width)区分:宽度 ≥ 1.75 cm 为 virginica(准确率 96.0%),宽度 < 1.75 cm 为 versicolor(准确率 92.9%)。这一层级分类规则清晰简洁,具有实际应用价值。
九、特征重要性排序
r
# 查看变量重要性
importance <- pruned_tree$variable.importance
print(importance)
# 画条形图
barplot(importance,
main = "决策树变量重要性排序",
col = "steelblue",
ylab = "重要性得分")

运行结果:
Petal.Length Petal.Width
52.45274 45.14713
解读:Petal.Length 最重要(贡献 53.7%),Petal.Width 次之(46.3%),另外 2 个变量未被使用(不重要)。
十、完整代码(一键运行)
把下面全部复制到 RStudio,全选运行,直接出图、出表、出结果:
r
# ===== 1. 加载包和数据 =====
library(rpart)
library(rpart.plot)
library(caret)
data("iris")
df <- iris
# ===== 2. 划分训练/测试集 =====
set.seed(123)
train_index <- sample(1:nrow(df), size = 0.7 * nrow(df))
train_data <- df[train_index, ]
test_data <- df[-train_index, ]
# ===== 3. 建立决策树 =====
tree_model <- rpart(Species ~ .,
data = train_data,
method = "class",
control = rpart.control(minsplit = 5,
minbucket = 3,
maxdepth = 5,
cp = 0.01))
# ===== 4. 查看 cp 表并剪枝 =====
printcp(tree_model)
best_cp <- tree_model$cptable[which.min(tree_model$cptable[, "xerror"]), "CP"]
cat("最优 cp =", best_cp, "\n")
pruned_tree <- prune(tree_model, cp = best_cp)
# ===== 5. 画决策树图 =====
rpart.plot(pruned_tree,
type = 2,
extra = 104,
fallen.leaves = TRUE,
main = "鸢尾花分类决策树")
# ===== 6. 预测和评估 =====
predictions <- predict(pruned_tree, newdata = test_data, type = "class")
conf_matrix <- table(预测值 = predictions, 真实值 = test_data$Species)
print(conf_matrix)
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
cat("测试集准确率:", round(accuracy * 100, 2), "%\n")
# ===== 7. 混淆矩阵详细指标 =====
confusionMatrix(predictions, test_data$Species)
# ===== 8. 提取分类规则 =====
library(rattle)
rules <- asRules(pruned_tree)
print(rules)
# ===== 9. 变量重要性 =====
importance <- pruned_tree$variable.importance
print(importance)
barplot(importance,
main = "决策树变量重要性排序",
col = "steelblue",
ylab = "重要性得分")
十一、赛场 15 分钟操作流程
| 时间 | 操作 | 输出 |
|---|---|---|
| 0-2 分钟 | 加载数据 + train_index 划分 |
训练/测试集 |
| 2-5 分钟 | rpart() 建树 + printcp() 看 cp 表 |
初步决策树 |
| 5-7 分钟 | prune() 剪枝 |
剪枝后的树 |
| 7-10 分钟 | rpart.plot() 画图 |
决策树图(论文核心) |
| 10-12 分钟 | predict() + confusionMatrix() |
准确率 + 混淆矩阵 |
| 12-14 分钟 | asRules() 提取规则 |
分类规则表 |
| 14-15 分钟 | barplot(importance) |
重要性排序图 |
十二、论文表格生成(直接复制)
表 1:混淆矩阵
| 真实\预测 | setosa | versicolor | virginica | 合计 |
|---|---|---|---|---|
| setosa | 15 | 0 | 0 | 15 |
| versicolor | 0 | 15 | 1 | 16 |
| virginica | 0 | 0 | 14 | 14 |
表 2:分类规则表
| 规则 | 条件 | 预测类别 | 准确率 |
|---|---|---|---|
| 规则 1 | Petal.Length ≥ 2.45 且 Petal.Width ≥ 1.75 | virginica | 96.0% |
| 规则 2 | Petal.Length ≥ 2.45 且 Petal.Width < 1.75 | versicolor | 92.9% |
| 规则 3 | Petal.Length < 2.45 | setosa | 100% |
十三、最后叮嘱
| 要点 | 说明 |
|---|---|
| ✅ 必画决策树图 | rpart.plot() 出一张图,放论文正文 |
| ✅ 必算准确率 | confusionMatrix() 出全部指标,写"测试集准确率达 XX.X%" |
| ✅ 必提剪枝 | 论文里写"通过交叉验证选择 cp = X.XX 进行剪枝,防止过拟合" |
| ✅ 必放规则表 | asRules() 提取规则,做成表放论文,评委最爱看 |
| ✅ 必说变量重要性 | variable.importance 排序,说清"哪个变量最关键" |
