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。
现有问题
- 原代码可实现正确分面顺序,但所有分面的文本重叠在一起:
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
- 若将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
相关产品推荐
相关产品推荐

