如何在R语言中绘制多人口统计学分组箱线图并添加对应p值?
在R中绘制整合多箱线图并添加分组差异p值
以下是实现需求的完整步骤,包含数据处理、统计检验和绘图代码:
1. 加载必要的包
library(tidyverse) # 数据处理+绘图 library(rstatix) # 便捷统计检验
2. 准备/导入数据
先模拟一份示例数据集(替换成你的真实数据即可):
set.seed(123) # 保证结果可重复 df <- tibble( ID = 1:200, V1 = c(rnorm(100, 50, 10), rnorm(50, 55, 10), rnorm(50, 45, 10)), 性别 = sample(c("男", "女"), 200, replace = TRUE), 年龄组 = sample(c("18-30", "31-50", "51+"), 200, replace = TRUE), 教育程度 = sample(c("高中及以下", "本科", "硕士及以上"), 200, replace = TRUE) )
3. 数据格式转换
将宽格式数据转为长格式,统一处理所有人口统计学分组变量:
df_long <- df %>% select(-ID) %>% pivot_longer(cols = -V1, names_to = "分组变量", values_to = "分组水平")
4. 计算分组差异p值
根据分组水平数量选择检验方法:二分类用Wilcoxon秩和检验,多分类用Kruskal-Wallis检验(若数据符合正态分布+方差齐性,可替换为t.test或anova):
p_values <- df_long %>% group_by(`分组变量`) %>% summarise( p = get_pvalue(V1 ~ `分组水平`, method = ifelse(n_distinct(`分组水平`) == 2, "wilcox.test", "kruskal.test"))$p, .groups = "drop" ) %>% mutate( # 格式化p值标签,提升可读性 p_label = case_when( p < 0.001 ~ "p < 0.001", p < 0.01 ~ paste0("p = ", round(p, 3)), TRUE ~ paste0("p = ", round(p, 2)) ), # 设置p值标注的Y轴位置(略高于V1的最大值) y_pos = max(df$V1) * 1.1 )
5. 绘制整合箱线图(分面版,更清晰)
将不同人口统计学变量拆分为子图,每个子图上方标注对应的p值:
ggplot(df_long, aes(x = `分组水平`, y = V1)) + geom_boxplot(fill = "#4292c6", alpha = 0.7) + # 按分组变量分面,X轴自适应每个变量的水平 facet_wrap(~`分组变量`, scales = "free_x") + # 添加p值标注 geom_text(data = p_values, aes(x = 1, y = y_pos, label = p_label), hjust = 0.5, size = 4, fontface = "bold") + # 美化主题 theme_bw() + labs(x = "分组", y = "V1", title = "V1在不同人口统计学分组下的分布及差异p值") + theme( plot.title = element_text(hjust = 0.5, size = 14, face = "bold"), axis.title = element_text(size = 12), strip.text = element_text(size = 12, face = "bold") )
可选:同一X轴版(不分面)
若希望所有分组放在同一X轴展示:
# 生成合并的X轴标签 df_long <- df_long %>% mutate(x_label = paste(`分组变量`, `分组水平`, sep = "\n")) # 计算p值标注的X轴位置(每个分组变量的中间位置) p_values <- df_long %>% group_by(`分组变量`) %>% summarise( p = get_pvalue(V1 ~ `分组水平`, method = ifelse(n_distinct(`分组水平`) == 2, "wilcox.test", "kruskal.test"))$p, x_pos = median(which(df_long$`分组变量` == cur_group()$`分组变量`)), .groups = "drop" ) %>% mutate( p_label = case_when( p < 0.001 ~ "p < 0.001", p < 0.01 ~ paste0("p = ", round(p, 3)), TRUE ~ paste0("p = ", round(p, 2)) ), y_pos = max(df$V1) * 1.1 ) # 绘图 ggplot(df_long, aes(x = x_label, y = V1)) + geom_boxplot(fill = "#4292c6", alpha = 0.7) + geom_text(data = p_values, aes(x = x_pos, y = y_pos, label = p_label), size = 4, fontface = "bold") + theme_bw() + labs(x = "人口统计学分组", y = "V1", title = "V1在不同人口统计学分组下的分布及差异p值") + theme( plot.title = element_text(hjust = 0.5, size = 14, face = "bold"), axis.title = element_text(size = 12), axis.text.x = element_text(angle = 45, hjust = 1) )
注意事项
- 若你的真实数据中分组变量是字符型,建议用
factor()转换为因子类型,可自定义水平顺序。 - 若数据符合正态分布且方差齐性,可将检验方法替换为
t.test(二分类)或anova(多分类)。 - 可根据需求调整箱线图颜色、主题样式、p值标注位置等参数。
内容的提问来源于stack exchange,提问作者Gerda
相关产品推荐
相关产品推荐

