R: R 逻辑回归

最后更新:2026-08-26

上一课我们学了线性回归——预测连续值(销售额、温度)。但 80% 的真实问题是"是否"——用户会不会买?邮件会不会被点?贷款会不会违约?这一课学逻辑回归——预测概率类别

读完这一课你就能用 R 做:客户流失预测、邮件点击预测、信用评分、A/B 测试建模——数据科学 60% 的分类问题。

1. 你将学到



2. 一个客户流失预测的故事

(1) 痛点:哪些客户会流失?

小赵是 SaaS 公司的数据分析师,manager问:"哪些客户下个月会流失?怎么提前挽留?"

数据:1000 客户,特征 = 年龄/使用时长/客服次数,标签 = 是否流失。

(2) R 的解法

R
# 1. 一行建模型
model <- glm(churn ~ age + usage + support_calls,
             data = df, family = binomial)

# 2. 一行预测概率
df$prob <- predict(model, type = "response")

# 3. 一行评估
library(pROC)
roc_obj <- roc(df$churn, df$prob)
auc(roc_obj)  # [1] 0.85  ← 模型质量

3 行代码 → 流失预测 + 模型质量



3. 逻辑回归原理

(1) 为什么需要逻辑回归?

100%
graph TB
    A[Y 是分类 0/1] --> B[线性回归不适用]
    B --> C[需要把连续值映射到 0-1]
    C --> D[Logit 变换 + Sigmoid]
    D --> E[逻辑回归]
    
    style A fill:#fff3cd
    style B fill:#f8d7da
    style C fill:#d4edda
    style D fill:#cce5ff
    style E fill:#e1d4ff

线性回归预测连续值,逻辑回归预测概率(0-1)。

(2) 数学公式

TEXT 📖 仅展示
P(Y=1) = 1 / (1 + e^(-z))
       │      │      │
       │      │      └─ z = β₀ + β₁X₁ + β₂X₂ + ...
       │      └─ e 指数
       └─ Sigmoid 函数(值域 0-1)

等价写法(logit 变换):

TEXT 📖 仅展示
log(P/(1-P)) = β₀ + β₁X₁ + β₂X₂ + ...

odds = P / (1-P) = 成功概率 / 失败概率。

(3) Sigmoid 函数

R
sigmoid <- function(x) 1 / (1 + exp(-x))

# 例子
sigmoid(0)     # 0.5
sigmoid(2)     # 0.881
sigmoid(-2)    # 0.119
sigmoid(10)    # 0.99995


4. glm() 基本用法

(1) 语法

R
glm(formula, data, family = binomial)
参数 含义
formula y ~ x1 + x2(与 lm 相同)
data 数据框
family binomial(二分类)/ gaussian(线性)/ poisson(计数)

(2) 第一个逻辑回归

R
# 数据
df <- data.frame(
  churn = c(0, 0, 0, 1, 1, 1, 0, 1, 0, 1),
  age = c(25, 30, 35, 50, 55, 60, 28, 58, 32, 65),
  usage = c(80, 70, 60, 30, 20, 10, 75, 15, 65, 5)
)

# 建模
model <- glm(churn ~ age + usage, data = df, family = binomial)
summary(model)

输出:

TEXT 📖 仅展示
Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept)   8.452      4.123   2.050   0.0403 *  
age           0.087      0.043   2.023   0.0431 *  
usage        -0.156      0.058  -2.690   0.0072 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(3) 6 大输出解读

字段 含义 解读
Estimate 系数(logit 空间) 系数为正 → 该变量增大会增加流失概率
Std. Error 标准误 系数估计精度
z value z 统计量 Estimate / Std.Error
Pr(>|z|) p 值 系数显著 ≠ 0 的概率
Null deviance 零模型偏差 仅截距模型
Residual deviance 残差偏差 越小拟合越好
💡 关键z value 取代了 t value(大样本正态近似);p < 0.05 系数显著。



5. 系数解读:odds ratio

(1) 为什么要 odds ratio?

逻辑回归的系数在 logit 空间,不直观。转 odds ratio (OR) 解读:

R
# odds ratio = exp(系数)
exp(coef(model))
# (Intercept)         age         usage
#   4670.123      1.091      0.856

(2) OR 解读

OR 值 含义
OR = 1 变量无影响
OR > 1 变量增大会增加 odds(事件更可能发生)
OR < 1 变量增大会降低 odds(事件更不可能发生)
R
# age 的 OR = 1.09
# 解读:年龄每增 1 岁,流失 odds 增 9%

# usage 的 OR = 0.86
# 解读:使用时长每增 1 单位,流失 odds 降 14%

(3) 95% 置信区间

R
exp(confint(model))
#                  2.5 %    97.5 %
# (Intercept)  12.345  12345.678
# age           1.002      1.198
# usage         0.745      0.978

CI 不包含 1 → 系数显著。



6. 预测

(1) type 参数

R
# type = "response"  ← 概率(0-1)★ 最常用
predict(model, type = "response")

# type = "link"  ← logit 空间(默认)
predict(model, type = "link")

(2) 实战预测

R
# 训练集预测
df$prob <- predict(model, type = "response")

# 阈值 0.5 转类别
df$pred <- ifelse(df$prob > 0.5, 1, 0)

# 新数据预测
new_customers <- data.frame(
  age = c(40, 55, 30),
  usage = c(50, 15, 80)
)
predict(model, new_customers, type = "response")
# [1] 0.123 0.876 0.045
# 解读:年龄 55 使用 15 的客户流失概率 87.6%

(3) 阈值选择

阈值 适用场景
0.5 默认,平衡精准/召回
0.3 减少漏报(医疗诊断、欺诈)
0.7 减少误报(垃圾邮件)
R
# 自定义阈值
df$pred <- ifelse(df$prob > 0.3, 1, 0)


7. 模型评估:ROC + AUC

(1) 混淆矩阵

R
library(caret)

# 真实 vs 预测
confusionMatrix(
  factor(df$pred),
  factor(df$churn),
  positive = "1"
)

#           Reference
# Prediction  0  1
#         0  50  5
#         1  10 35
# Accuracy: 0.85
# Sensitivity(召回): 0.875
# Specificity: 0.833

(2) 4 大指标

指标 公式 含义
Accuracy (TP+TN) / 总数 整体准确率
Precision TP / (TP+FP) 预测为正的中实际正的比例
Recall (Sensitivity) TP / (TP+FN) 实际正的中被预测出的比例
F1 2×P×R/(P+R) P 和 R 的调和平均

(3) ROC 曲线 + AUC

R
library(pROC)

# Calculate ROC
roc_obj <- roc(df$churn, df$prob)
auc(roc_obj)
# [1] 0.92  ← 越接近 1 越好

# 画 ROC
plot(roc_obj, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, lty = 2, col = "gray")  # 随机分类基线

# AUC 解读
# 0.5-0.7:差
# 0.7-0.8:一般
# 0.8-0.9:好
# 0.9-1.0:优秀


8. 训练/测试集划分

R
library(caret)

# 划分 70% 训练 / 30% 测试
set.seed(42)
train_index <- createDataPartition(df$churn, p = 0.7, list = FALSE)
train_data <- df[train_index, ]
test_data <- df[-train_index, ]

# 训练模型
model <- glm(churn ~ age + usage, data = train_data, family = binomial)

# 测试集预测
test_data$prob <- predict(model, test_data, type = "response")
test_data$pred <- ifelse(test_data$prob > 0.5, 1, 0)

# 评估
confusionMatrix(factor(test_data$pred), factor(test_data$churn), positive = "1")

# 真实 AUC
roc_obj <- roc(test_data$churn, test_data$prob)
auc(roc_obj)


9. 多分类(multinom)

R
# 多分类用 multinom(需 nnet 包)
install.packages("nnet")
library(nnet)

# 3 分类
df <- data.frame(
  y = c("A", "A", "B", "B", "C", "C"),
  x = c(1, 2, 3, 4, 5, 6)
)

model <- multinom(y ~ x, data = df)
summary(model)

# 预测
predict(model, type = "class")      # 类别
predict(model, type = "probs")      # 概率


10. 实战:客户流失预测完整流程

下面是一个完整工作流示例,把本课所有逻辑回归知识串起来。

▶ 示例:1000 客户流失预测

R 📖 仅展示
# ============================================
# 1000 客户流失预测
# 功能:完整逻辑回归流程
# ============================================

library(ggplot2)
library(dplyr)
library(caret)
library(pROC)

# 1. Prepare data
set.seed(42)
n <- 1000
df <- tibble(
  age = round(rnorm(n, 40, 12)),
  usage = round(rnorm(n, 50, 20)),
  support_calls = sample(0:10, n, replace = TRUE),
  plan = sample(c("基础", "高级", "企业"), n, replace = TRUE,
                prob = c(0.5, 0.3, 0.2))
) |>
  mutate(
    # 流失概率:年龄大、使用少、客服多 → 易流失
    logit_p = -3 + 0.03 * age - 0.05 * usage + 0.3 * support_calls,
    p = 1 / (1 + exp(-logit_p)),
    churn = rbinom(n, 1, p)
  ) |>
  select(-logit_p, -p)

cat("=== 数据预览 ===\n")
print(head(df, 3))
cat("\n流失率:", round(mean(df$churn) * 100, 2), "%\n")

# 2. 训练/测试集划分
set.seed(42)
train_index <- createDataPartition(df$churn, p = 0.7, list = FALSE)
train_data <- df[train_index, ]
test_data <- df[-train_index, ]

cat("\n训练集:", nrow(train_data), "行\n")
cat("测试集:", nrow(test_data), "行\n\n")

# 3. 训练模型
model <- glm(churn ~ age + usage + support_calls + plan,
             data = train_data, family = binomial)
cat("=== 模型摘要 ===\n")
summary(model)

# 4. 系数解读
cat("\n=== 系数 odds ratio ===\n")
or_df <- tidy(model, conf.int = TRUE, exponentiate = TRUE)
print(or_df |> select(term, estimate, std.error, p.value, conf.low, conf.high))

# 5. 训练集预测
train_data$prob <- predict(model, type = "response")
train_data$pred <- ifelse(train_data$prob > 0.5, 1, 0)

# 6. 测试集预测
test_data$prob <- predict(model, test_data, type = "response")
test_data$pred <- ifelse(test_data$prob > 0.5, 1, 0)

# 7. 训练集评估
cat("\n=== 训练集评估 ===\n")
train_cm <- confusionMatrix(factor(train_data$pred), factor(train_data$churn),
                             positive = "1")
print(train_cm)

# 8. 测试集评估
cat("\n=== 测试集评估 ===\n")
test_cm <- confusionMatrix(factor(test_data$pred), factor(test_data$churn),
                            positive = "1")
print(test_cm)

# 9. ROC + AUC
cat("\n=== AUC ===\n")
roc_train <- roc(train_data$churn, train_data$prob)
roc_test <- roc(test_data$churn, test_data$prob)
cat("训练集 AUC:", round(auc(roc_train), 3), "\n")
cat("测试集 AUC:", round(auc(roc_test), 3), "\n")

# 10. 画 ROC
plot(roc_test, main = "ROC Curve", col = "blue", lwd = 2)
abline(a = 0, b = 1, lty = 2, col = "gray")
legend("bottomright",
       legend = paste0("AUC = ", round(auc(roc_test), 3)),
       col = "blue", lwd = 2)

# 11. 阈值分析
cat("\n=== 阈值分析 ===\n")
thresholds <- seq(0.1, 0.9, by = 0.1)
threshold_results <- lapply(thresholds, function(t) {
  pred <- ifelse(test_data$prob > t, 1, 0)
  cm <- confusionMatrix(factor(pred), factor(test_data$churn), positive = "1")
  data.frame(
    threshold = t,
    precision = cm$byClass["Precision"],
    recall = cm$byClass["Sensitivity"],
    f1 = cm$byClass["F1"]
  )
}) |> bind_rows()
print(threshold_results)

# 12. 找出高风险客户
cat("\n=== 高风险客户 Top 5(流失概率 > 70%)===\n")
high_risk <- test_data |>
  filter(prob > 0.7) |>
  arrange(desc(prob)) |>
  head(5) |>
  select(age, usage, support_calls, plan, prob, pred)
print(high_risk)

# 13. 概率分布图
ggplot(test_data, aes(x = prob, fill = factor(churn))) +
  geom_histogram(bins = 30, alpha = 0.7, position = "identity") +
  labs(title = "流失概率分布(按真实标签)",
       x = "预测流失概率", y = "频数", fill = "真实标签") +
  scale_fill_brewer(palette = "Set1", labels = c("未流失", "流失")) +
  theme_minimal()

# 14. 保存模型
saveRDS(model, "churn_model.rds")
cat("\n=== 模型已保存:churn_model.rds ===\n")
逻辑代码 85 行(超过 40 行限制,仅展示)

预期输出(节选):

TEXT 📖 仅展示
=== 训练集评估 ===
Confusion Matrix:
          Reference
Prediction   0   1
         0 525  42
         1  18 115
Accuracy: 0.91
Sensitivity: 0.733
Specificity: 0.967

=== 测试集评估 ===
Accuracy: 0.89
AUC = 0.92

❓ 常见问题

Q 逻辑回归 vs 线性回归?
A Y 是连续用 lm();Y 是 0/1 用 glm(family = binomial)
Q AUC 多大算好?
A 0.5 = 随机;0.7-0.8 = 一般;0.8-0.9 = 好;0.9+ = 优秀。
Q 多分类用什么?
Annet::multinom()(多分类逻辑回归)或 caret 一行(自动选模型)。3 分类以上考虑随机森林。

📖 小节


📝 作业

  1. 基础题:构造 1 个数据框(200 行 4 列:churn + age + usage + support_calls),用 glm 建模型,用 summary 解读所有输出,计算 exp(coef(model)) 解读 odds ratio。

  2. 基础题:上题模型,用 predict(type = "response") 预测训练集,画实际标签 vs 预测概率的直方图,验证逻辑回归输出是概率(0-1)。

  3. 基础题:上题模型,用 pROC::roc() 画 ROC 曲线,计算 AUC。验证 AUC 0.7-0.9 是好模型。

  4. 进阶题:用 mtcars 数据集构造二分类(vs 是 0/1 引擎类型),glm(vs ~ mpg + wt, family = binomial) 建模型,70/30 划分,计算测试集 AUC 和混淆矩阵。

  5. 挑战题:完整客户流失预测——模拟 1000 客户(4 特征 + 1 标签),完整流程:① 探索 ② 划分 ③ 建模 ④ 评估(混淆矩阵 + ROC + AUC) ⑤ 阈值分析 ⑥ 高风险客户识别 ⑦ 概率分布图 ⑧ 保存模型。把过程截图保存。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏