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

ggplot2分面文本重叠与自定义排序冲突问题求解

问题描述

示例数据:

percents = c(10,20,30,40,50,60,70,80,90,100)
percent_cases = rep(percents, 10)
no_cases = sample(1:1000, 100)
no_lineages = no_cases/rnorm(10, 7)
df = as.data.frame(cbind(percent_cases, no_lineages, no_cases))

需要制作分面图,满足两个要求:

  • 分面按采样病例百分比的数值顺序排列(而非默认的字母顺序)
  • 每个分面显示对应数据的lm回归线逆斜率文本

当前使用环境:R 4.0.2(RStudio)、ggplot2 3.3.6。

现有问题

  1. 原代码可实现正确分面顺序,但所有分面的文本重叠在一起:
library(ggplot2)

# 计算分面文本
V1 = rep(NA, length(percents))
for(i in 1:length(percents)){
  df2 = df[df$percent_cases == percents[i],]
  fit = lm(df2$no_lineages~df2$no_cases)
  cf = coef(fit)
  V1[i] = paste0("'",round(1/cf[2]), " cases per lineage'")
}

df3 = as.data.frame(cbind(percents, V1))
names(df3) = c("percent_cases", "V1")

# 设置分面顺序
df$percent_cases_f <- factor(df$percent_cases,levels=c(10,20,30,40,50,60,70,80,90,100))

# 设置分面标题
percent.labs <- c("10% cases observed", "20% cases observed", "30% cases observed",
                  "40% cases observed", "50% cases observed", "60% cases observed",
                  "70% cases observed", "80% cases observed", "90% cases observed",
                  "100% cases observed")
names(percent.labs) <- percents

# 绘图
p <- ggplot(data = df, aes(x = no_cases, y = no_lineages)) +
  geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x) +
  geom_point() + 
  xlab("No. cases observed") + ylab("No. lineages")+ 
  geom_text(data=df3, aes(x = 500, y = 150, label=V1), parse = TRUE, inherit.aes=FALSE) + 
  facet_wrap(percent_cases_f~., labeller = labeller(percent_cases_f = percent.labs)) +
  theme_bw() 
p
  1. 若将df3的列名改为percent_cases_f,文本重叠问题解决,但分面会回到默认的字母顺序(如10%→100%→20%...)。

解决方案

核心是让文本数据框df3中的分面变量和主数据框df保持完全一致的因子类型(含水平顺序),而非单纯修改列名。完整修改代码如下:

library(ggplot2)

# 生成示例数据
percents = c(10,20,30,40,50,60,70,80,90,100)
percent_cases = rep(percents, 10)
no_cases = sample(1:1000, 100)
no_lineages = no_cases/rnorm(10, 7)
df = as.data.frame(cbind(percent_cases, no_lineages, no_cases))

# 计算每个分面的逆斜率文本
V1 = rep(NA, length(percents))
for(i in 1:length(percents)){
  df2 = df[df$percent_cases == percents[i],]
  fit = lm(df2$no_lineages~df2$no_cases)
  cf = coef(fit)
  V1[i] = paste0("'",round(1/cf[2]), " cases per lineage'")
}

# 构造文本数据框:将percents转为和df一致的因子
df3 = data.frame(
  percent_cases_f = factor(percents, levels = c(10,20,30,40,50,60,70,80,90,100)),
  V1 = V1
)

# 给主数据框添加分面用的因子列
df$percent_cases_f <- factor(df$percent_cases,levels=c(10,20,30,40,50,60,70,80,90,100))

# 设置分面标题标签
percent.labs <- c("10% cases observed", "20% cases observed", "30% cases observed",
                  "40% cases observed", "50% cases observed", "60% cases observed",
                  "70% cases observed", "80% cases observed", "90% cases observed",
                  "100% cases observed")
names(percent.labs) <- percents

# 绘图
p <- ggplot(data = df, aes(x = no_cases, y = no_lineages)) +
  geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x) +
  geom_point() + 
  xlab("No. cases observed") + ylab("No. lineages")+ 
  geom_text(data=df3, aes(x = 500, y = 150, label=V1), parse = TRUE, inherit.aes=FALSE) + 
  facet_wrap(~percent_cases_f, labeller = labeller(percent_cases_f = percent.labs)) +
  theme_bw() 
p

原理说明

  • ggplot分面的排序由因子的水平顺序决定,当文本数据框的分面变量与主数据框的因子水平完全匹配时,既能自动将文本分配到对应分面,又不会打乱分面顺序。
  • 之前直接改列名时,df3中的percent_cases_f是字符类型(cbind导致类型转换),ggplot会自动将其转为默认字母顺序的因子,因此分面顺序被打乱;现在直接构造同水平的因子,就和主数据框的分面逻辑保持一致了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 10:48:11