[模型可视化]更好的理解Logistic回归模型输出结果

本文介绍了如何在R语言中利用ggplot2和ggdist包绘制Logistic回归曲线,并结合数据分布进行可视化。通过拟合Logistic回归模型分析电影预算与通过Bechdel测试的关系,展示了如何在图中添加数据点、直方图以及95%置信区间,以更直观地解释模型结果。
摘要由CSDN通过智能技术生成

本文主要内容:

  1. 生成Logistic 回归模型结果
  2. 绘制Logistic回归曲线
  3. 绘制带有数据分布的Logistic回归曲线

当你拟合逻辑回归模型时,有很多方法可以显示结果。最为传统的方法是在表格中展示系数的摘要。但是由于逻辑回归曲线的弯曲性质,通过绘图显示拟合线,通常是一种更好的展示方式。典型的逻辑回归线图的主要缺点是它们通常不显示数据,因为会出现在 y 轴上过度绘图的现象。但是ggdist 包支持在绘制逻辑回归曲线时可以轻松地显示数据。

阅读这篇文章,我假设你了解一些背景知识:

  • 熟悉Logistic回归模型的基本原理和结果分析。

逻辑回归

  • 熟悉R语言的基本操作。数据整理和绘图是在tidyverse包 和 broom 包的帮助下完成的。数据来自包 fivethirtyeight

R语言入门零基础-数据分析tidyverse入门

1. 导入所需包和数据

  • 加载包
if(!require(pacman))install.packages("pacman")

pacman::p_load(
  rio,           # to import data
  here,          # to locate files
  tidyverse,     # to clean, handle, and plot the data (includes ggplot2 package)
  fivethirtyeight,
  broom,
  ggdist,
  showtext        # 支持ggplot显示中文
  ) 

#Set the default theme for ggplot objects to theme_bw()
theme_set(theme_bw())
theme_update(panel.grid = element_blank())
  • 数据集

在这篇文章中,我们将使用 bechdel 数据集。从文档中,该数据是‘反对好莱坞排斥女性的美元和美分案’故事背后的原始数据。

data("bechdel")
skimr::skim(bechdel)
Namebechdel
Number of rows1794
Number of columns15
_______________________
Column type frequency:
character5
factor1
numeric9
________________________
Group variablesNone

Variable type: character

skim_variablen_missingcomplete_rateminmaxemptyn_uniquewhitespace
imdb01810017940
title01183017680
test012160100
binary0144020
code01880850

Variable type: factor

skim_variablen_missingcomplete_rateorderedn_uniquetop_counts
clean_test01TRUE5ok: 803, not: 514, men: 194, dub: 142

Variable type: numeric

skim_variablen_missingcomplete_ratemeansdp0p25p50p75p100hist
year01.002002.558.9819701998200520092013▁▁▂▅▇
budget01.0044826462.6148186026.127000120000002800000060000000425000000▇▁▁▁▁
domgross170.9969132048.2880367309.510163115714219406093354918760507625▇▁▁▁▁
intgross110.99150385700.05210335267.8382826129470764824611898508522783918982▇▁▁▁▁
budget_201301.0055464608.4554918635.608632160689183699578678337905461435929▇▂▁▁▁
domgross_2013180.9995174783.58125965348.8989920546594559936401216783521771682790▇▁▁▁▁
intgross_2013110.99197837984.97283507948.2089933232604962396402414789703171930973▇▁▁▁▁
period_code1790.902.421.1911235▇▇▆▅▂
decade_code1790.901.940.6911223▅▁▇▁▃

我们的因变量是二进制的,它指示给定电影是否通过了 Bechdel 测试。在数据集中的 1,794 部电影中,只有不到一半通过。

bechdel %>% 
  count(binary) %>% 
  mutate(percent = 100 * n/ sum(n))
# A tibble: 2 × 3
  binary     n percent
  <chr>  <int>   <dbl>
1 FAIL     991    55.2
2 PASS     803    44.8

我们唯一的预测变量是budget_2013,每部电影2013年的的预算(美元)。

bechdel %>% 
  ggplot(aes(x = budget_2013)) +
  geom_histogram() +
  facet_wrap(~ binary, ncol = 1)

unnamed-chunk-5-1

为了方便,我们将字符变量 binary 转换为一个传统的 0/1 数值变量,称为 pass

bechdel = bechdel %>% 
  mutate(pass = ifelse(binary == "FAIL", 0, 1))

bechdel %>% 
  select(binary, pass) %>% 
  head()
# A tibble: 6 × 2
  binary  pass
  <chr>  <dbl>
1 FAIL       0
2 PASS       1
3 FAIL       0
4 FAIL       0
5 FAIL       0
6 FAIL       0

2. 建模

我们的Logistic统计模型公式如下:

p a s s i ∼ B i n o m i a l ( n = 1 , p i ) l o g i t ( p i ) = β 0 + β 1 b u d g e t _ 201 3 i pass_i \sim Binomial(n = 1, p_i) \\ logit(p_i) = \beta_0 + \beta_1 budget\_2013_{i} passiBinomial(n=1,pi)logit(pi)=β0+β1budget_2013i

我们使用传统的 logit 连接函数来确保二项式概率被限制在0和1的范围内。我们可以像这样使用基本的 R glm() 函数来拟合这样的模型。

fit = glm(
  data = bechdel,
  family = binomial,
  pass ~ 1 + budget_2013
)

传统方式是在系数表中呈现结果,见下表。

tidy(fit) %>% 
  knitr::kable()
termestimatestd.errorstatisticp.value
(Intercept)0.11131480.06896611.6140510.1065163
budget_20130.00000000.0000000-6.2497240.0000000

由于budget_2013 变量的规模,它的点估计和标准误差都非常小。

为了更清晰的展示自变量的影响效应,下面是预算增加 100,000,000 美元时预期的对数几率减少。

c(coef(fit)[2], confint(fit)[2,]) * 1e8
budget_2013       2.5 %      97.5 % 
 -0.5972374  -0.7875178  -0.4126709 

请注意如何添加 95% 置信区间以获得良好的衡量标准。

3. Logistic回归曲线

现在已经用表格的方式解释了模型。为了更好的理解和解释模型结果,我们将使用广泛使用的仅显示拟合线的方法。

# define the new data
nd <- tibble(budget_2013 = seq(from = 0, to = 500000000, length.out = 100))

p <-
  # 计算拟合线和 标准差SE 的
  predict(fit,
          newdata = nd,
          type = "link",
          se.fit = TRUE) %>% 
  data.frame() %>% 
  mutate(ll = fit - 1.96 * se.fit,
         ul = fit + 1.96 * se.fit) %>% 
  select(-residual.scale, -se.fit) %>% 
  mutate_all(plogis) %>%
  bind_cols(nd)

glimpse(p)
Rows: 100
Columns: 4
$ fit         <dbl> 0.5278000, 0.5202767, 0.5127442, 0.5052059, 0.4976652, 0.4…
$ ll          <dbl> 0.4940356, 0.4881515, 0.4821772, 0.4760998, 0.4699043, 0.4…
$ ul          <dbl> 0.5613120, 0.5522351, 0.5432161, 0.5342767, 0.5254405, 0.5…
$ budget_2013 <dbl> 0, 5050505, 10101010, 15151515, 20202020, 25252525, 303030…
  • 逻辑回归模型的常规线图。
p %>% 
  ggplot(aes(x = budget_2013, y = fit)) +
  geom_ribbon(aes(ymin = ll, ymax = ul),
              alpha = 1/2) +
  geom_line() +
  scale_y_continuous("probability of passing", 
                     expand = c(0, 0), limits = 0:1)

unnamed-chunk-11-1

拟合线为黑色和 95% 置信区间的半透明灰色丝带标记。该曲线很好地展示了预算较大的电影在通过贝克德尔测试时往往做得更差。

3. 通过添加数据改进可视化

如果想将数据添加到我们的绘图中,最简单的方法是使用 geom_point()函数。

p %>% 
  ggplot(aes(x = budget_2013, y = fit)) +
  geom_ribbon(aes(ymin = ll, ymax = ul),
              alpha = 1/2) +
  geom_line() +
  geom_point(data = bechdel,
             aes(y = pass),
             alpha = 1/2) +
  scale_y_continuous("probability of passing", 
                     expand = expansion(mult = 0.01))

unnamed-chunk-12-1

即使通过使用 alpha 参数使点半透明,过度绘图问题也使得很难理解数据。

  • 另一种方法是添加垂直抖动。

我们可以用 geom_jitter() 来做到这一点。

p %>% 
  ggplot(aes(x = budget_2013, y = fit)) +
  geom_ribbon(aes(ymin = ll, ymax = ul),
              alpha = 1/2) +
  geom_line() +
  geom_jitter(data = bechdel,
              aes(y = pass),
              size = 1/4, alpha = 1/2, height = 0.05) +
  scale_y_continuous("probability of passing", 
                     expand = c(0, 0))

unnamed-chunk-13-1

尽管有了很大的改进,但这种方法仍然不能最好地描述budget_2013 值的分布。

  • 添加自变量的分布

如果可能的话,最好用更像直方图的方式明确描述 budget_2013 分布。

p %>% 
  ggplot(aes(x = budget_2013)) +
  geom_ribbon(aes(ymin = ll, ymax = ul),
              alpha = 1/2) +
  geom_line(aes(y = fit)) +
  stat_dots(data = bechdel,
            aes(y = pass, side = ifelse(pass == 0, "top", "bottom")),
            scale = 1/3) +
  scale_y_continuous("probability of passing",
                     expand = c(0, 0))

unnamed-chunk-14-1

使用 stat_dots() 函数,我们添加了点图,这是直方图的绝妙替代品,直方图将每个数据值显示为一个单独的点。对于 side 参数,我们使用条件语句告诉 stat_dots() 我们希望将budget_2013 的一些值显示在底部,而将其他值显示在顶部。使用 scale 参数,我们指出了我们希望点图分布占据 y 轴范围内的总空间的多少。

  • 更精致的版本
p %>% 
  ggplot(aes(x = budget_2013)) +
  geom_ribbon(aes(ymin = ll, ymax = ul),
              alpha = 1/2) +
  geom_line(aes(y = fit)) +
  stat_dots(data = bechdel %>% 
              mutate(binary = factor(binary, levels = c("PASS", "FAIL"))),
            aes(y = pass, 
                side = ifelse(pass == 0, "top", "bottom"),
                color = binary),
            scale = 0.4, shape = 19) +
  scale_color_manual("Bechdel test", values = c("#009E73", "#D55E00")) +
  scale_x_continuous("budget (in 2013 dollars)",
                     breaks = c(0, 1e8, 2e8, 3e8, 4e8),
                     labels = c(0, str_c(1:4 * 100, " mill")),
                     expand = c(0, 0), limits = c(0, 48e7)) +
  scale_y_continuous("probability of passing",
                     expand = c(0, 0)) +
  theme(panel.grid = element_blank())

unnamed-chunk-15-1

  • 添加自变量的直方图分布

其他分布形式也是可能的。例如,这里我们在 stat_slab() 函数中设置slab_type = "histogram" 以将点图换成直方图。

p %>% 
  ggplot(aes(x = budget_2013)) +
  geom_ribbon(aes(ymin = ll, ymax = ul),
              alpha = 1/2) +
  geom_line(aes(y = fit)) +
  # the magic lives here
  stat_slab(data = bechdel %>% 
              mutate(binary = factor(binary, levels = c("PASS", "FAIL"))),
            aes(y = pass, 
                side = ifelse(pass == 0, "top", "bottom"),
                fill = binary, color = binary),
            slab_type = "histogram",
            scale = 0.4, breaks = 40, size = 1/2) +
  scale_fill_manual("Bechdel test", values = c(alpha("#009E73", .7), alpha("#D55E00", .7))) +
  scale_color_manual("Bechdel test", values = c("#009E73", "#D55E00")) +
  scale_x_continuous("budget (in 2013 dollars)",
                     breaks = c(0, 1e8, 2e8, 3e8, 4e8),
                     labels = c(0, str_c(1:4 * 100, " mill")),
                     expand = c(0, 0), limits = c(0, 48e7)) +
  scale_y_continuous("probability of passing",
                     expand = c(0, 0)) +
  theme(panel.grid = element_blank())

unnamed-chunk-16-1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RookieTrevor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值