使用R语言rstatix包进行ANOVA并自动两两比较可视化

关于单因素方差分析,网上有很多方法,比如使用R语言进行单因素方差分析以及结果可视化这个教程。

但是R到底如何做多组样本均数比较及两两比较?我觉得上面的教程是错误的,原因如下:

  1. 没有进行正态性检验和方差齐性检验?
  2. 两两比较直接使用t检验,而不是事后检验

rstatix包是一个非常好的统计学包,含有许许多多的统计函数,关于ANOVA的分析,可以看下吗这个教程,虽然是英语的,但是逻辑性很强。
ANOVA in R: The Ultimate Guide - Datanovia

我们可以用经典的内置ToothGrowth数据进行演示。

构建数据

data("ToothGrowth") ## 加载数据
df <- ToothGrowth ## 重命名数据
df$dose<-as.factor(df$dose) ## 将剂量dose设置为因子,这步很重要

数据统计

library(rstatix) 
library(tidyr)
## 按dose分组计算各组的len均值和标准差
df %>%
  group_by(dose) %>%
  get_summary_stats(len, type = "mean_sd") 
dose variable n mean sd
1 0.5 len 20 10.605 4.5
2 1 len 20 19.735 4.415
3 2 len 20 26.1 3.774

简单可视化数据

library(ggpubr)
ggboxplot(df, x = "dose", y = "len")
image.png

正态性检验

可以使用以下两种方法之一检查正态性假设:

  1. 分析方差分析模型残差。以检查所有组的正态性。此方法更容易,并且当您有许多组或每个组的数据点很少时,它非常方便。

  2. 分别检查每个组的正态性。当只有几个组并且每个组有许多数据点时,可以使用此方法。

模型残差的正态性检验

首先构建残差的线性模型,以QQ图的形式展示结果,见下图所示。

# 构建线性模型
model  <- lm(len ~ dose, data = df)
# 构建残差的QQ图
ggqqplot(residuals(model))
Figure 1: 模型残差的QQ图

计算Shapiro-Wilk正态性检验

shapiro_test(residuals(model))
variable statistic p.value
1 residuals(model) 0.967308970963541 0.1076299868954

在QQ图中,由于所有点都大致沿着参考线落下,因此我们可以假设呈正态分布。这一结论得到了Shapiro Wilk检验的支持。p值不显著(p=0.0798),因此我们可以假设为正态。


各组的正态性检验

计算各组水平的Shapiro-Wilk检验。如果数据为正态分布,则p值应大于0.05。

df %>%
  group_by(dose) %>% 
  shapiro_test(len)
dose variable statistic p
1 0.5 len 0.9406450940929 0.246601486196049
2 1 len 0.931343143558916 0.163882141233336
3 2 len 0.977753538808449 0.901911496439863

根据Shapiro-Wilk的正态性检验,各组均为正态分布(p>0.05)。

请注意,如果样本数量大于 50,则首选正态QQ 图,因为在较大的样本量下,Shapiro-Wilk检验变得非常敏感,即使与正态性有轻微的偏差。

QQ图绘制了给定数据与正态分布之间的相关性。为每组数据绘制QQ图,见Figure 2所示。

ggqqplot(df, "len", facet.by = "dose")
Figure 2: 各组数据分布的QQ图

所有点都大致沿着参考线落下。所以我们可以假设数据是正态分布的。

如果对数据的正态性有疑问,可以使用Kruskal-Wallis检验,这是单因素ANOVA检验的非参数替代方法。

方差齐性检验

  1. 残差结合拟合图可用于检查方差的同质性。

    plot(model, 1)
    
image.png

在上图中,残差和拟合值(每个组的均值)之间没有明显的关系,结果很好。因此,我们可以假设方差的同质性。

  1. 也可以使用 Levene 检验来检查方差的同质性
df %>% levene_test(len ~ dose)
df1 df2 statistic p
1 2 57 0.64573411096315 0.528069457375992

这时,我们要注意,一定要将dose定义为因子,否则会因为结果是数字而报错。

上面的结果中,我们可以看到 p 值> 0.05,这意味组间方差之间没有显著差异。因此,我们可以假设不同组中方差具有齐性或同质性。

在不满足方差齐性假设的情况下,可以使用函数Welch_ANOVA_test()函数计算Welch单因素方差分析测试。该测试不需要假设方差相等。

计算检验

res.aov<- df %>% anova_test(len ~ dose)

res.aov
Effect DFn DFd F p p<.05 ges
1 dose 2 57 67.416 9.53e-16 * 0.703

在上表中,该列对应于广义eta平方(效应大小)。它测量结果变量(这里是植物)中的变异性比例,可以用预测因子(这里是治疗)来解释。效应大小为0.703(26%)意味着70.3%的变化可归因于剂量条件

从上述ANOVA表中可以看出,各组之间存在显著差异(p=9.53e-16),显示为”*“,F(2,57)=67.416,p=9.53e-16,eta2[g]=0.703。

该结果可以在可视化的时候引用上。

事后检验

常用单因素方差分析用于多组间的两两比较的方法是Tukey事后检验(而不是简单的t检验)。可用rstati的tukey_hsd()函数计算。

而对于方差不齐的情况下可以使用Games Howell事后检验,即rstati的games_howell_test ()函数

pwc <- tukey_hsd(df, len ~ dose)

pwc

另外,还有几个同等的代码,结果一样

df %>% tukey_hsd(len ~ dose)

aov(len ~ dose, data = df) %>% tukey_hsd()

term group1 group2 null.value estimate conf.low conf.high p.adj p.adj.signif
1 dose 0.5 1 0 9.13 5.90180527932805 12.3581947206719 2e-8 ****
2 dose 0.5 2 0 15.495 12.266805279328 18.7231947206719 1.12e-11 ****
3 dose 1 2 0 6.36499999999999 3.13680527932805 9.59319472067194 0.0000425 ****

可视化报告

我们可以将单因素方差分析的结果报告如下:

进行单因素方差分析,以评估3个不同剂量组的牙齿生长是否不同:0.5(n=20)、1.0(n=20)和2.0(n=20)。

数据表示为平均值+/-标准差。不同剂量组之间的牙齿生长在统计学上有显著差异,F(2,57)=67.416,p=9.53e-16,广义eta平方=0.703。

Tukey事后分析显示,三组数据具有统计学意义(p=0.012)

带有p值的箱示图可视化结果

使用add_xy_position()函数自动进行两两分组比较,同时确定x和y轴位置。

## 添加各组比较和坐标轴位置
pwc <- pwc %>% add_xy_position(x = "dose")
term group1 group2 null.value estimate conf.low conf.high p.adj p.adj.signif y.position groups xmin xmax
1 dose 0.5 1 0 9.13 5.90180527932805 12.3581947206719 2e-8 **** 35.388 0.5,1 1 2
2 dose 0.5 2 0 15.495 12.266805279328 18.7231947206719 1.12e-11 **** 37.62 0.5,2 1 3
3 dose 1 2 0 6.36499999999999 3.13680527932805 9.59319472067194 0.0000425 **** 39.852 1,2 2 3

  • 使用stat_pvalue_manual()函数可以自动添加两两比较的p值和显示的位置,还可以选择是否显示无意义的结果,以及是显示星号还是数值
  • 使用labs()函数添加整体结果和方法
## ggpubr绘图,加整体p值和两两比较p值
ggboxplot(df, x = "dose", y = "len") +
    stat_pvalue_manual(pwc,hide.ns = TRUE ) +#隐藏无意义的结果
    labs(subtitle = get_test_label(res.aov, detailed = TRUE),
        caption = get_pwc_label(pwc)
    )
image.png

当然,我们也可以适当美化,比如配色,主题什么的,见Figure 3所示。

ggboxplot(df, x = "dose", y = "len",
          add = 'jitter',color = 'dose',
          palette = 'lancet', #设置lancet配色
          ggtheme = theme_bw(), # 设置背景
          legend='none' ## 去除分组标签
          ) +stat_pvalue_manual(pwc, hide.ns = TRUE) +
  labs( subtitle = get_test_label(res.aov, detailed = TRUE), caption = get_pwc_label(pwc))
Figure 3: 各组的两两比较

我们也可以用柱状图来可视化,见Figure 4所示。

ggbarplot(df, x = "dose", y = "len",
          add = 'mean_sd',fill = 'dose',
          palette = 'npg', #设置lancet配色
          ggtheme = theme_pubclean(), # 设置背景
          legend='none' ## 去除分组标签
          ) +stat_pvalue_manual(pwc, hide.ns = TRUE) +
  labs( subtitle = get_test_label(res.aov, detailed = TRUE), caption = get_pwc_label(pwc))
Figure 4: 各组的两两比较

放宽标准的方差齐性假设

经典的单因素方差分析检验要求所有组的方差相等。在本例,Levene检验不显著,提示方差齐性假设证明是良好的。

在违反方差齐性假设的情况下,我们如何进行ANOVA检验呢?

在无法假设方差齐性的情况下(即Levene检验显著时),使用Welch单因素检验是标准单单因素方差分析的替代方法。

在这种情况下,可以使用Games-Howell事后检验成对t检验(不假设方差相等)来比较所有可能的群体差异组合。

# Welch One way ANOVA test
res.aov2 <- df %>% welch_anova_test(len ~ dose)
# Pairwise comparisons (Games-Howell)
pwc2 <- df %>% games_howell_test(len ~ dose)
# 可视化: box plots with p-values
pwc2 <- pwc2 %>% add_xy_position(x = "dose", step.increase = 1)

ggboxplot(df, x = "dose", y = "len",
          add = 'jitter',color = 'dose',
          palette = 'lancet', #设置lancet配色
          ggtheme = theme_bw(), # 设置背景
          legend='none' ## 去除分组标签
          )  +
  stat_pvalue_manual(pwc2, hide.ns = TRUE) + # 如果不想隐藏无意义的,可以设置hide.ns = F
  labs(
    subtitle = get_test_label(res.aov2, detailed = TRUE),
    caption = get_pwc_label(pwc2)
    )
image.png

两组方法的比较

将两种方法进行比较,见Figure 5所示。

p1<-ggboxplot(df, x = "dose", y = "len",
          add = 'jitter',color = 'dose',
          palette = 'lancet', #设置lancet配色
          ggtheme = theme_bw(), # 设置背景
          legend='none', ## 去除分组标签
          title = 'ANOVA text') +stat_pvalue_manual(pwc, hide.ns = TRUE,label = 'p.adj') +
  labs( subtitle = get_test_label(res.aov, detailed = TRUE), caption = get_pwc_label(pwc))

p2<-ggboxplot(df, x = "dose", y = "len",
          add = 'jitter',color = 'dose',
          palette = 'lancet', #设置lancet配色
          ggtheme = theme_bw(), # 设置背景
          legend='none', ## 去除分组标签
         title = 'Welch one-way test' )  +
  stat_pvalue_manual(pwc2, hide.ns = TRUE,label = 'p.adj') +
  labs(subtitle = get_test_label(res.aov2, detailed = TRUE),
    caption = get_pwc_label(pwc2)
    )
## 拼图
cowplot::plot_grid(p1,p2)
Figure 5: 两种方法的比较

当然,如果使用放宽条件的情况,其实还有一个更简单的办法,就是直接使用ggstatsplot这个包

library(ggstatsplot)
p3<-ggbetweenstats(df,dose,len,p.adjust.method = 'none')
p3
image.png
cowplot::plot_grid(p3,p2)
image.png

可以看到结果是一样的.


另外,rstatix包也可以进行双因素、多因素的ANOVA,具体的可以去https://www.datanovia.com/en/lessons/anova-in-r学习教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343