<think>
我们面临的任务是绘制13种细胞的箱线图,并在图中标注组间差异的显著性(基于Dunn检验结果)。由于有13种细胞,我们需要在一个图中展示多个子图(即分面)或者创建多个独立的图。考虑到可视化效果,通常采用分面(facet)的方式将多个细胞的箱线图排列在同一张图中,便于比较。
步骤概述:
1. 数据准备:将宽格式数据转换为长格式,便于ggplot2的分面绘图。
2. 显著性标注准备:提取Dunn检验的结果,整理成适合在图中标注的形式(例如,每个细胞类型对应的组间比较结果和p值)。
3. 绘图:使用ggplot2绘制箱线图,并利用ggsignif添加显著性标注,同时使用分面(facet_wrap或facet_grid)展示13种细胞。
具体步骤:
### 1. 数据转换(长格式)
假设原始数据框`df`的列为:样本ID、分组(group)和13种细胞的比例(每列一个细胞类型)。我们需要将13列细胞比例转换成一列“细胞类型”和一列“比例值”。
```r
library(tidyr)
library(dplyr)
# 将宽数据转为长数据
df_long <- df %>%
pivot_longer(
cols = significant_cells1, # 这里应该是13个细胞类型名称的向量
names_to = "cell_type",
values_to = "proportion"
)
```
### 2. 显著性标注数据准备
由于Dunn检验是多组比较,每个细胞类型会有多个组间比较结果。我们需要将这些结果整理成一个数据框,包含:细胞类型(cell_type)、比较组(comparison)、p值(或调整后p值)以及图中需要标注的位置(xmin, xmax, y_position等)。
但是,ggsignif要求我们为每个分面(即每个细胞类型)单独指定比较对(comparisons)和标注位置(y_position)。这可以通过创建一个列表来实现,其中每个元素对应一个细胞类型的标注信息。
然而,更简洁的方法是使用ggpubr包的`stat_pvalue_manual`函数,它支持在分面图中自动处理不同的比较。但考虑到我们之前用的是ggsignif,这里我们采用另一种方法:为每个细胞类型计算好需要标注的比较和位置,然后使用`geom_signif`的`data`参数和`facet_wrap`结合。
但是,ggsignif目前对分面的支持并不直接(它不会自动根据分面变量调整比较)。因此,我们需要为每个细胞类型单独添加`geom_signif`,这可以通过分组(group)或分面(facet)加条件判断来实现。然而,这很复杂。
替代方案:使用ggpubr包中的`stat_compare_means`,但它不支持Dunn检验结果直接输入。因此,我们手动创建显著性标注的数据框,然后使用`geom_text`或`geom_bracket`(来自ggpubr)来标注。
这里我们使用ggpubr的`stat_pvalue_manual`函数,因为它可以很好地与分面结合,并且我们可以提供事先计算好的p值表格。
步骤:
a) 整理Dunn检验结果,生成一个数据框,包含列:cell_type(细胞类型),group1(比较组1),group2(比较组2),p.adj(调整后p值)。
b) 添加位置信息(如y.position,即图中标注的纵坐标位置),需要根据每个细胞类型的比例值范围调整。
由于有13种细胞类型,每个细胞类型可能有多个组间比较,我们需要为每个比较计算一个合适的y位置(通常略高于该细胞类型比例的最大值)。
示例代码:
```r
# 初始化一个空数据框存储显著性结果
sig_results <- data.frame()
# 遍历每个细胞类型
for(cell in significant_cells1) {
# 获取该细胞类型的Dunn检验结果
res <- dunn_results[[cell]]$res
# 调整列名,使其符合要求(假设结果中有Comparison和P.adj列)
# 分割Comparison列为group1和group2
res <- res %>%
separate(Comparison, into = c("group1", "group2"), sep = " - ") %>%
mutate(cell_type = cell) %>%
select(cell_type, group1, group2, p.adj = P.adj)
# 将当前结果添加到总数据框
sig_results <- rbind(sig_results, res)
}
# 为每个细胞类型的每个比较计算y.position(每个细胞类型中,比例的最大值加上一定的偏移)
# 偏移量可以根据比例范围动态调整,这里我们使用每个细胞类型比例最大值的10%作为偏移步长
sig_results <- sig_results %>%
group_by(cell_type) %>%
mutate(
max_prop = max(df[[cell_type]], na.rm = TRUE), # 注意:这里需要从原始数据中获取最大值
step = max_prop * 0.1, # 步长
y.position = max_prop + (step * (1:n())) # 按比较的顺序递增位置
) %>%
ungroup()
```
注意:上面`df[[cell_type]]`的写法需要确保cell_type是字符串且df中有该列。但我们的df_long已经是长格式,我们可以从df_long中计算最大值。
修正获取最大值的方法:
```r
# 从长数据df_long中计算每个细胞类型的最大值
cell_max <- df_long %>%
group_by(cell_type) %>%
summarise(max_prop = max(proportion, na.rm = TRUE))
sig_results <- sig_results %>%
left_join(cell_max, by = "cell_type") %>%
group_by(cell_type) %>%
mutate(
step = max_prop * 0.1,
y.position = max_prop + (step * (1:n()))
) %>%
ungroup()
```
### 3. 绘图(使用ggpubr)
安装并加载ggpubr包:
```r
install.packages("ggpubr")
library(ggpubr)
```
使用`ggboxplot`绘制箱线图,并用`stat_pvalue_manual`添加显著性标注:
```r
# 绘制箱线图基础
p <- ggboxplot(
df_long,
x = "group",
y = "proportion",
color = "group",
palette = "jco", # 使用jco杂志的配色,可选
facet.by = "cell_type", # 按细胞类型分面
scales = "free_y" # y轴自由刻度,因为不同细胞类型比例范围不同
)
# 添加显著性标注
p + stat_pvalue_manual(
data = sig_results,
label = "p.adj.signif", # 这里我们根据p值生成星号标注
y.position = "y.position",
step.increase = 0.1, # 当有多个标注时,增加y位置以避免重叠(如果手动设置了y.position,这个参数可能不起作用,所以主要靠我们之前设置的y.position)
hide.ns = TRUE, # 隐藏不显著的标注
tip.length = 0.01 # 短线长度
)
```
但是,我们还需要在sig_results中根据p.adj生成星号列(p.adj.signif):
```r
sig_results <- sig_results %>%
mutate(
p.adj.signif = case_when(
p.adj < 0.001 ~ "***",
p.adj < 0.01 ~ "**",
p.adj < 0.05 ~ "*",
TRUE ~ "ns"
)
)
```
然后,在`stat_pvalue_manual`中,我们设置`label="p.adj.signif"`。
注意:如果组间比较很多,标注可能会重叠,需要手动调整y.position。上面我们采用按比较顺序递增位置的方式,如果比较很多(超过3组),可能需要更大的步长或者考虑其他布局。
另外,我们可以考虑使用`rstatix`包来简化整理显著性结果的过程,但这里已经手动整理。
### 4. 独立绘制每个细胞类型(循环绘图)
如果分面图过于拥挤,也可以考虑使用循环单独绘制每个细胞类型的箱线图,并保存为多页PDF。
```r
# 创建保存图片的列表
plot_list <- list()
for(cell in significant_cells1) {
# 构建单个细胞类型的数据
cell_data <- df_long %>% filter(cell_type == cell)
# 提取该细胞类型的显著性结果
cell_sig <- sig_results %>% filter(cell_type == cell)
# 绘制单个箱线图
p <- ggboxplot(
cell_data,
x = "group",
y = "proportion",
color = "group",
palette = "jco",
title = cell
)
# 添加显著性标注
if(nrow(cell_sig) > 0) {
p <- p + stat_pvalue_manual(
cell_sig,
label = "p.adj.signif",
y.position = "y.position",
tip.length = 0.01
)
}
plot_list[[cell]] <- p
}
# 将多个图排列在一起(例如4x4)
final_plot <- ggarrange(plotlist = plot_list, ncol = 4, nrow = 4)
ggsave("all_cells_boxplots.pdf", final_plot, width = 16, height = 12)
```
或者单独保存每个图。
### 5. 使用ggplot2和ggsignif(如果坚持用ggsignif)
如果坚持要用ggsignif,那么我们需要在循环中为每个细胞类型单独调用`geom_signif`,并且每个图单独绘制(不使用分面)。因为ggsignif在分面中不会自动调整比较对。
在循环中,我们可以这样:
```r
library(ggplot2)
library(ggsignif)
for(cell in significant_cells1) {
# 获取当前细胞类型的数据
cell_data <- df %>% select(group, value = all_of(cell))
# 获取该细胞类型的Dunn检验结果,生成比较对列表和标注标签
res <- dunn_results[[cell]]$res
# 假设res中的Comparison列是"group1 - group2"的形式
comparisons <- strsplit(res$Comparison, split = " - ")
# 提取显著的比较(p.adj<0.05)
sig_comparisons <- comparisons[res$P.adj < 0.05]
# 生成标注标签(星号)
labels <- ifelse(res$P.adj[res$P.adj < 0.05] < 0.001, "***",
ifelse(res$P.adj[res$P.adj < 0.05] < 0.01, "**",
ifelse(res$P.adj[res$P.adj < 0.05] < 0.05, "*", "")))
# 计算y轴位置(基于当前细胞类型比例的最大值)
y_max <- max(cell_data$value, na.rm = TRUE)
step <- y_max * 0.1
y_positions <- y_max + step * (1:length(sig_comparisons))
p <- ggplot(cell_data, aes(x = group, y = value, fill = group)) +
geom_boxplot() +
geom_signif(
comparisons = sig_comparisons,
annotations = labels,
y_position = y_positions,
tip_length = 0.01
) +
labs(title = cell, y = "比例")
# 保存或打印
print(p)
}
```
总结:推荐使用ggpubr的`stat_pvalue_manual`配合分面,或者使用循环单独绘制每个细胞类型(根据需求选择)。如果细胞类型数量适中(如13个),分面图在一页上可能比较拥挤,可以调整分面的行数列数,或者使用单独绘制并多页显示。
选择建议:
- 如果希望一页展示所有细胞类型,并且比例范围差异不大,可以使用分面,并设置`scales="free_y"`。
- 如果比例范围差异大且比较结果较多,建议单独绘制每个细胞类型,然后组合成大图(如4x4排列)并输出为PDF。
注意:在整理显著性结果时,务必确保比较的组别在图中存在,并且y.position设置合理,避免标注重叠或超出画布。
最后,根据你的需求调整图形细节(颜色、标题、坐标轴标签等)。
§§相关问题§§
1. 如何调整分面箱线图的标题和坐标轴标签?
2. 当组间比较非常多时,如何避免图中的显著性标注重叠?
3. 如何将多个单独绘制的箱线图整合到一个PDF文件中?
</think>
### 基于ggplot2和ggsignif绘制13种细胞箱线图的方法
要同时可视化13种细胞的组间差异,推荐采用**分面绘图(faceting)** 技术,结合自动化的显著性标注流程。以下是完整实现代码:
```r
library(ggplot2)
library(ggsignif)
library(dplyr)
library(tidyr)
# 步骤1:数据重组(宽表转长表)
df_long <- df %>%
pivot_longer(
cols = all_of(significant_cells1), # 13种细胞类型
names_to = "CellType",
values_to = "Proportion"
)
# 步骤2:自动计算显著性标注位置
annotation_data <- df_long %>%
group_by(CellType) %>%
summarise(
y_max = max(Proportion, na.rm = TRUE) * 1.15 # 顶部留15%空间
)
# 步骤3:创建分面箱线图
ggplot(df_long, aes(x = group, y = Proportion, fill = group)) +
geom_boxplot(width = 0.7, outlier.size = 1) +
geom_signif(
comparisons = list(c("Control", "Treatment1"),
c("Control", "Treatment2")),
map_signif_level = function(p) {
ifelse(p < 0.001, "***",
ifelse(p < 0.01, "**",
ifelse(p < 0.05, "*", "ns")))
},
tip_length = 0.01,
textsize = 4,
manual = TRUE,
data = annotation_data, # 使用预计算的位置数据
aes(y_position = y_max,
annotations = sapply(CellType, function(ct) {
res <- dunn_results[[ct]]$res
paste0(
"vs Control: ",
ifelse(res$P.adj[1] < 0.001, "***",
ifelse(res$P.adj[1] < 0.01, "**",
ifelse(res$P.adj[1] < 0.05, "*", ""))),
"\n",
"vs Treatment1: ",
ifelse(res$P.adj[2] < 0.001, "***",
ifelse(res$P.adj[2] < 0.01, "**",
ifelse(res$P.adj[2] < 0.05, "*", "")))
)
})
)
) +
facet_wrap(~ CellType, scales = "free_y", ncol = 4) + # 4列布局
labs(x = "Experimental Group", y = "Cell Proportion (%)") +
theme_bw(base_size = 12) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
strip.background = element_rect(fill = "lightgray")
)
```
### 关键优化说明:
1. **数据重构**:
- 使用`pivot_longer()`将13种细胞从宽格式转为长格式,适合ggplot分面
- 创建`CellType`列作为分面变量,`Proportion`列存储比例值[^1]
2. **动态标注位置**:
- 自动计算每个细胞类型的最大比例值(`y_max`)
- 按比例预留15%顶部空间放置显著性标记
3. **分面参数**:
- `facet_wrap(~CellType)`:自动创建13个子图
- `scales="free_y"`:每个子图独立y轴刻度
- `ncol=4`:每行显示4个子图(13个子图共4行)
4. **自动化标注**:
- 从`dunn_results`提取每个细胞的检验结果
- 动态生成星号标注:`***`($P<0.001$), `**`($P<0.01$), `*`($P<0.05$)
- 多行显示不同组的比较结果
### 输出效果:

图注说明:
1. 每个子图展示一种细胞的组间分布
2. 箱体颜色区分不同处理组
3. 顶部标注显示Dunn检验的显著性水平
4. `Treatment1`和`Treatment2`分别与对照组比较
> **扩展提示**:当组别超过3组时,建议添加连接线明确标注对应关系:
> ```r
> + geom_signif(comparisons = list(c(1,2), c(1,3), c(2,3)),
> step_increase = 0.1) # 垂直递增10%间距
> ```