You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用ggplot2绘制多箱线图展示emmeans成对比较结果

问题描述

我想用ggplot2在同一张图表中绘制多个箱线图,展示模型的成对比较结果。我的模型及使用emmeans进行的成对比较如下:

mod1 <- lmer(CONT_Y ~  MY_GROUP * YEAR + (1|ID), data = dfModels)

group <- emmeans(mod1,~ MY_GROUP|YEAR) 
year <- emmeans(mod1,~YEAR|MY_GROUP) 

my_pairs <- data.frame(group_p) %>% full_join(data.frame(year_p)) 

成对比较结果:

contrastYEARMY_GROUPestimateSEdft.ratiop.value
L1 - L22020-0.910.5360-1.730.09
L1 - L22021-0.310.5360-0.590.56
YEAR2020 - YEAR2021G1-1.140.5360-2.160.03
YEAR2020 - YEAR2021G2-0.540.5360-1.020.31

期望绘制的图表样式为在同一画布中展示多组箱线图,同时标注成对比较的显著性结果。相关原始数据及模型详情如下:

data <- structure(list(PARTICIPANTS = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 
                                        3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 
                                        7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 9L, 9L, 9L, 9L, 10L, 10L, 10L, 
                                        10L, 11L, 11L, 11L, 11L, 12L, 12L, 12L, 12L, 13L, 13L, 13L, 13L, 
                                        14L, 14L, 14L, 14L, 15L, 15L, 15L, 15L, 16L, 16L, 16L, 16L, 17L, 
                                        17L, 17L, 17L, 18L, 18L, 18L, 18L, 19L, 19L, 19L, 19L, 20L, 20L, 
                                        20L, 20L, 21L, 21L, 21L, 21L), CONT_Y = c(19.44, 20.07, 19.21, 
                                                                                  16.35, 11.37, 12.82, 19.42, 18.94, 19.59, 20.01, 19.7, 17.92, 
                                                                                  18.78, 19.21, 19.27, 18.46, 19.52, 20.02, 16.19, 19.97, 13.83, 
                                                                                  15.93, 14.79, 21.55, 18.8, 19.42, 19.27, 19.37, 17.14, 14.45, 
                                                                                  17.63, 20.01, 20.28, 17.93, 19.36, 20.15, 16.06, 17.04, 19.16, 
                                                                                  20.1, 16.44, 18.39, 18.01, 19.05, 18.04, 19.69, 19.61, 16.88, 
                                                                                  19.02, 20.42, 18.27, 18.43, 18.08, 17.1, 19.98, 19.43, 19.71, 
                                                                                  19.93, 20.11, 18.41, 20.31, 20.1, 20.38, 20.29, 13.6, 18.92, 
                                                                                  19.05, 19.13, 17.75, 19.15, 20.19, 18.3, 19.43, 19.8, 19.83, 
                                                                                  19.53, 16.14, 21.14, 17.37, 18.73, 16.51, 17.51, 17.06, 19.42
                                        ), CATEGORIES = structure(c(1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 
                                                                    1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 
                                                                    1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 
                                                                    1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 
                                                                    1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 
                                                                    1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L), .Label = c("A", 
                                                                                                                            "B"), class = "factor"), MY_GROUP = structure(c(1L, 2L, 1L, 2L, 
                                                                                                                                                                            1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 
                                                                                                                                                                            1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 
                                                                                                                                                                            1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 
                                                                                                                                                                            1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 
                                                                                                                                                                            1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L
                                                                                                                            ), .Label = c("G1", "G2"), class = "factor")), row.names = c(NA, 
                                                                                                                                                                                         -84L), class = c("tbl_df", "tbl", "data.frame"))
 

### 重命名列:

data <- data %>%  rename(., YEAR = CATEGORIES)
解决方案

1. 整理成对比较数据

首先需要清理my_pairs中的缺失值,补全分组信息,并给比较类型分类,方便后续绘图:

library(dplyr)
library(tidyr)

my_pairs_clean <- my_pairs %>%
  mutate(
    # 补全组间比较的MY_GROUP信息
    MY_GROUP = case_when(
      grepl("L1 - L2", contrast) ~ rep(c("G1", "G2"), 2),
      TRUE ~ MY_GROUP
    ),
    # 补全年份比较的YEAR信息
    YEAR = case_when(
      grepl("YEAR2020 - YEAR2021", contrast) ~ rep(c("2020", "2021"), 2),
      TRUE ~ YEAR
    ),
    # 标记比较类型
    comp_type = case_when(
      grepl("L1 - L2", contrast) ~ "组间比较",
      TRUE ~ "年份间比较"
    )
  ) %>%
  drop_na()

2. 绘制带显著性标记的多箱线图

使用ggplot2绘制箱线图展示原始数据分布,同时用ggsignif添加成对比较的显著性标记(*代表p<0.05):

library(ggplot2)
library(ggsignif)

# 确保YEAR为字符类型
data$YEAR <- as.character(data$YEAR)

# 绘制分面箱线图,整合两种比较类型
ggplot(data, aes(x = MY_GROUP, y = CONT_Y, fill = YEAR)) +
  geom_boxplot(position = position_dodge(width = 0.8), alpha = 0.7) +
  # 添加组间比较的显著性标记(按年份分面)
  geom_signif(
    data = my_pairs_clean %>% filter(comp_type == "组间比较"),
    aes(xmin = 1, xmax = 2, annotations = ifelse(p.value < 0.05, "*", "")),
    y_position = 22, tip_length = 0.01,
    position = position_dodge(width = 0.8)
  ) +
  # 添加年份间比较的显著性标记(按分组)
  geom_signif(
    data = my_pairs_clean %>% filter(comp_type == "年份间比较"),
    aes(xmin = c(1.2, 2.2), xmax = c(1.8, 2.8), annotations = ifelse(p.value < 0.05, "*", "")),
    y_position = 23, tip_length = 0.01
  ) +
  facet_wrap(~ YEAR) +
  labs(
    title = "分组与年份的成对比较箱线图",
    x = "分组",
    y = "CONT_Y数值",
    fill = "年份"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

3. 可选:合并两个独立比较图

如果希望将两种比较类型的箱线图分开展示并合并在同一画布,可使用gridExtra包:

library(gridExtra)

# 组间比较箱线图(按年份分组)
p_group <- ggplot(data, aes(x = YEAR, y = CONT_Y, fill = MY_GROUP)) +
  geom_boxplot(position = position_dodge(width = 0.8), alpha = 0.7) +
  geom_signif(
    data = my_pairs_clean %>% filter(comp_type == "组间比较"),
    aes(xmin = c(1, 2), xmax = c(1.5, 2.5), annotations = ifelse(p.value < 0.05, "*", "")),
    y_position = 22, tip_length = 0.01,
    position = position_dodge(width = 0.8)
  ) +
  labs(
    title = "不同年份下的组间比较",
    x = "年份",
    y = "CONT_Y数值",
    fill = "分组"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

# 年份间比较箱线图(按分组)
p_year <- ggplot(data, aes(x = MY_GROUP, y = CONT_Y, fill = YEAR)) +
  geom_boxplot(position = position_dodge(width = 0.8), alpha = 0.7) +
  geom_signif(
    data = my_pairs_clean %>% filter(comp_type == "年份间比较"),
    aes(xmin = c(1, 2), xmax = c(1.5, 2.5), annotations = ifelse(p.value < 0.05, "*", "")),
    y_position = 22, tip_length = 0.01,
    position = position_dodge(width = 0.8)
  ) +
  labs(
    title = "不同分组下的年份间比较",
    x = "分组",
    y = "CONT_Y数值",
    fill = "年份"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

# 合并两个图为一行
grid.arrange(p_group, p_year, ncol = 2)

内容的提问来源于stack exchange,提问作者Larissa Cury

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 17:31:58