科研绘图系列:R语言分组柱状图三(Grouped Bar Chart)

文摘   2024-07-15 09:29   中国  

专注收集和自写可发表的科研图形的数据和代码分享,该系列的数据均可从以下链接下载:

百度云盘链接: https://pan.baidu.com/s/1M4vgU1ls0tilt0oSwFbqYQ
提取码: 请关注WX公zhong号 生信学习者 后台发送 科研绘图 获取提取码

介绍

分组柱状图(Grouped Bar Chart)是一种数据可视化图表,用于比较不同类别(分组)内各子类别(子组)的数值。在分组柱状图中,每个分组有一组并列的柱子,每个柱子代表一个子组的数值,不同的分组用不同的列来表示。

特点:

  • 并列柱子:每个分组内的柱子是并列的,便于在同一分组内比较不同子组。

  • 分组比较:可以直观地比较不同分组之间的数值大小。

  • 多变量展示:同时展示分组和子组两个层次的变量。

适合情况:

  1. 多类别比较:当你需要比较两个或多个类别(如性别、年龄组、地区等)内不同子类别的数据时。

  2. 层次化数据:数据具有层次结构,例如,首先按国家分组,然后在国家内部按城市或州进行分组。

  3. 展示相对差异:当需要展示不同子组在相同分组内的相对大小或比例时。

  4. 避免数据重叠:在某些情况下,使用堆叠柱状图可能导致底层数据难以解读,分组柱状图可以避免这个问题。

  5. 强调分组内比较:如果你更关注于分组内部的比较,而不是分组之间的比较。

  6. 有限的分组数量:当分组数量不是很多时,分组柱状图可以清晰地展示每个分组的数据,如果分组数量太多,图表可能会变得拥挤。

  7. 数据量适中:当每个分组内的子组数量适中,不会使图表过于复杂或难以解读。

加载R包

knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(tidyverse)
library(ggpubr)
library(plyr)
library(ggsignif)
library(ggprism)

# rm(list = ls())
options(stringsAsFactors = F)

# group & color
dose_names <- c("0.5", "1", "2")
dose_colors <- c("#0073C2FF", "#EFC000FF", "#CD534CFF")

supp_names <- c("OJ", "VC")
supp_colors <- c('#999999','#E69F00')

导入数据

data("ToothGrowth")

head(ToothGrowth)
lensuppdose
4.2VC0.5
11.5VC0.5
7.3VC0.5
5.8VC0.5
6.4VC0.5
10.0VC0.5

准备数据

  • 筛选画图数据

plotdata <- ToothGrowth |>
dplyr::mutate(dose = factor(as.character(dose), levels = dose_names),
supp = factor(supp, levels = supp_names)) |>
dplyr::rename(Group = dose,
Subgroup = supp,
Index = len) |>
dplyr::select(Group, Subgroup, Index)

head(plotdata)
GroupSubgroupIndex
0.5VC4.2
0.5VC11.5
0.5VC7.3
0.5VC5.8
0.5VC6.4
0.5VC10.0
  • 通过data_summary函数计算每个分组的mean & sd & se

data_summary <- function(data, varname, groupnames){

summary_func <- function(x, col) {
mean_value <- mean(x[[col]], na.rm = TRUE)
sd_value <- sd(x[[col]], na.rm = TRUE)
length_n <- length(x[[col]])

# standard error= standard deviation/squareroot(n)
se_value <- sd_value / sqrt(length_n)

return(c(mean = mean_value, sd = sd_value, se = se_value))
}

data_sum <- ddply(data, groupnames, .fun=summary_func, varname)
colnames(data_sum)[which(colnames(data_sum) == "mean")] <- varname

return(data_sum)
}

plotdata2 <- data_summary(
data = plotdata,
varname = "Index",
groupnames = c("Group", "Subgroup"))

head(plotdata2)
GroupSubgroupIndexsdse
0.5OJ13.234.4597091.4102837
0.5VC7.982.7466340.8685620
1OJ22.703.9109531.2367520
1VC16.772.5153090.7954104
2OJ26.062.6550580.8396031
2VC26.144.7977311.5171757
  • 计算组间差异P值

df_p_val <- plotdata %>%
rstatix::group_by(Group) %>%
rstatix::t_test(Index ~ Subgroup) %>%
rstatix::adjust_pvalue(p.col = "p", method = "bonferroni") %>%
rstatix::add_significance(p.col = "p.adj") %>%
rstatix::add_xy_position(x = "Group", dodge = 0.8) %>% # important for positioning!
dplyr::mutate(p.adj = round(p.adj, 3))

head(df_p_val)
Group.y.group1group2n1n2statisticdfpp.adj
0.5IndexOJVC10103.169732814.968750.006360.019
1IndexOJVC10104.032769615.357670.001040.003
2IndexOJVC1010-0.046136114.039820.964001.000

画图

分组柱状图展示不同分组的比较结果,以下是代码解释:

  1. ggplot(data = plotdata2, aes(x = Group, y = Index)): 初始化ggplot对象,使用plotdata2作为数据源,将Group映射到x轴,Index映射到y轴。

  2. geom_bar(aes(fill = Subgroup), stat = "identity", ...): 添加条形图层,使用aes(fill = Subgroup)Subgroup映射到条形的填充颜色,stat = "identity"表示条形的高度由数据中的Index确定,position_dodge()确保不同子组的条形在同一分组内并排显示。

  3. geom_errorbar(...): 添加误差条图层,显示数据的标准差范围,aes(color = Subgroup)Subgroup映射到误差条的颜色,position_dodge()调整误差条的位置以适应条形的宽度。

  4. ggprism::add_pvalue(...): 使用ggprism包的add_pvalue函数添加显著性p值标签,xminxmax定义了标签的位置,label定义了标签的格式。

  5. geom_point(...): 在条形图上添加点图层,使用plotdata数据集,点的位置由GroupIndex确定,shape映射到点的形状,position_jitterdodge添加抖动和偏移以避免点的重叠。

  6. labs(x = ""): 设置x轴标签,这里将其设置为空字符串。

  7. scale_y_continuous(...): 设置y轴的比例尺,guide = "prism_minor"指定y轴的次要刻度,minor_breaks设置次要刻度的间隔,limits设置y轴的范围,expand调整y轴的范围,留出一些空间。

  8. scale_fill_prism()scale_color_prism()scale_shape_prism(): 使用ggprism包提供的尺度函数来设置填充颜色、边框颜色和点形状。

  9. guides(color = "none", shape = "none"): 设置图例,这里将颜色和形状的图例设置为不显示。

  10. theme_prism(): 应用ggprism包的主题样式。

  11. theme(...): 自定义图形的主题样式,包括轴标题、轴文本的大小、颜色和字体,以及图例的位置。


pl_prism <- ggplot(data = plotdata2, aes(x = Group, y = Index)) +
geom_bar(aes(fill = Subgroup), stat = "identity",
color = "white", position = position_dodge()) +
geom_errorbar(aes(color = Subgroup,
ymin = Index - sd, ymax = Index + sd),
width = 0.2, position = position_dodge(width = 0.9),
size = 1) +
ggprism::add_pvalue(data = df_p_val,
xmin = "xmin",
xmax = "xmax",
label = "p = {p.adj}",
tip.length = 0) +
geom_point(data = plotdata,
aes(x = Group, y = Index, shape = Subgroup),
position = position_jitterdodge(jitter.width = 0.5,
dodge.width = 0.7),
size = 2, show.legend = FALSE) +
labs(x = "") +
scale_y_continuous(guide = "prism_minor", # prism_offset
minor_breaks = seq(0, 40, 2),
limits = c(0, 40),
expand = expansion(mult = c(0, 0.1))) +
scale_fill_prism() +
scale_color_prism() +
scale_shape_prism() +
guides(color = "none", shape = "none") +
theme_prism() +
theme(axis.title = element_text(size = 12, color = "black", face = "bold"),
axis.text = element_text(size = 10, color = "black"),
text = element_text(size = 9, color = "black"),
legend.position = c(0.15, 0.85))

pl_prism

结果:不同水平下,内部分组的Index指数的差异结果。

如果大家有任何问题,欢迎留言沟通交流

生信学习者
生信教程分享,专注数据分析和科研绘图方向欢迎大家关注,也可一起探讨生信问题