ggplot绘图仅显示四级因子PAN首个水平的问题求助
问题描述
我用以下代码绘制带密度图的箱线图:
ggplot(data_alk_pan, aes(x = PAN, y = MEAS_RESULT, fill = PAN)) + theme(legend.position = "none") + theme(text = element_text(size = 10)) + scale_x_discrete(labels = paste(levels(data_alk_pan$PAN), "\n(N=",table(data_alk_pan$PAN),")", sep="")) + labs(y = "Concentration (mg/l)", x = "Pan") + ggdist::stat_halfeye( aes(thickness = stat(pdf*n)), adjust = 0.5, justification = -.5, .width = 0, point_colour = NA ) + geom_boxplot(alpha = 0.5, varwidth = TRUE)
该代码对数据中其他因子均能正常工作,但因子PAN仅显示第一个水平。我已通过运行levels(data_alk_pan$PAN)确认R识别PAN的所有水平,返回结果为:
[1] "Pan_01" "Pan_02" "Pan_03" "Pan_all"
所有水平都有数据(n>0),但绘图仅显示Pan_01。补充的部分数据如下:
> dput(data_alk_pan[1:200, c(8,13)]) structure(list(MEAS_RESULT = c(170, 180, 150, 140, 160, 140, 330, 130, 130, 210, 130, 130, 310, 400, 200, 160, 160, 160, 170, 170, 220, 170, 200, 240, 200, 190, 200, 180, 190, 240, 250, 260, 280, 270, 250, 290, 260, 260, 250, 230, 270, 260, 220, 260, 250, 310, 270, 86, 270, 150, 290, 290, 300, 290, 270, 210, 280, 300, 390, 270, 190, 240, 240, 260, 210, 250, 210, 220, 190, 260, 210, 190, 240, 280, 240, 210, 220, 220, 200, 250, 310, 260, 290, 280, 180, 280, 310, 310, 300, 300, 280, 190, 180, 180, 200, 210, 180, 280, 390, 290, 320, 310, 320, 260, 330, 330, 390, 300, 350, 270, 190, 180, 310, 270, 300, 270, 330, 310, 320, 310, 340, 330, 340, 270, 340, 57, 360, 28, 320, 170, 21, 370, 320, 330, 430, 340, 300, 350, 300, 300, 340, 260, 270, 230, 180, 210, 210, 240, 230, 260, 250, 230, 250, 270, 280, 170, 190, 190, 190, 180, 190, 250, 250, 260, 270, 180, 260, 260, 260, 260, 250, 260, 280, 290, 130, 150, 190, 180, 190, 240, 270, 260, 230, 220, 170, 190, 270, 270, 260, 280, 260, 260, 270, 280, 290, 270, 190, 230, 240, 170), PAN = structure(c(4L, 4L, 4L, 4L, 4L, 4L, 1L, 4L, 4L, 1L, 4L, 4L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L), .Label = c("Pan_01", "Pan_02", "Pan_03", "Pan_all"), class = "factor")), row.names = c(NA, 200L), class = "data.frame")
如何让PAN的四个水平都显示在图中?
问题原因与解决方法
根源分析
问题出在scale_x_discrete的标签设置上:
scale_x_discrete(labels = paste(levels(data_alk_pan$PAN), "\n(N=",table(data_alk_pan$PAN),")", sep=""))
直接用paste拼接会把所有水平的标签合并成单个字符串,而非对应每个水平的标签向量。ggplot将这个单一字符串作为所有x轴水平的标签,导致仅第一个水平被渲染。
修复方案
需确保labels参数是与因子水平长度匹配的向量,以下三种方法均可解决:
方法1:使用mapply逐个拼接标签
ggplot(data_alk_pan, aes(x = PAN, y = MEAS_RESULT, fill = PAN)) + theme(legend.position = "none") + theme(text = element_text(size = 10)) + scale_x_discrete(labels = mapply(function(lvl, n) paste0(lvl, "\n(N=", n, ")"), levels(data_alk_pan$PAN), table(data_alk_pan$PAN))) + labs(y = "Concentration (mg/l)", x = "Pan") + ggdist::stat_halfeye( aes(thickness = stat(pdf*n)), adjust = 0.5, justification = -.5, .width = 0, point_colour = NA ) + geom_boxplot(alpha = 0.5, varwidth = TRUE)
方法2:使用purrr::map2_chr(需加载purrr包)
library(purrr) ggplot(data_alk_pan, aes(x = PAN, y = MEAS_RESULT, fill = PAN)) + theme(legend.position = "none") + theme(text = element_text(size = 10)) + scale_x_discrete(labels = map2_chr(levels(data_alk_pan$PAN), table(data_alk_pan$PAN), ~paste0(.x, "\n(N=", .y, ")"))) + labs(y = "Concentration (mg/l)", x = "Pan") + ggdist::stat_halfeye( aes(thickness = stat(pdf*n)), adjust = 0.5, justification = -.5, .width = 0, point_colour = NA ) + geom_boxplot(alpha = 0.5, varwidth = TRUE)
方法3:提前在数据框中创建标签列
library(dplyr) data_alk_pan <- data_alk_pan %>% mutate(PAN_label = paste0(PAN, "\n(N=", table(PAN)[as.character(PAN)], ")")) ggplot(data_alk_pan, aes(x = PAN_label, y = MEAS_RESULT, fill = PAN)) + theme(legend.position = "none") + theme(text = element_text(size = 10)) + labs(y = "Concentration (mg/l)", x = "Pan") + ggdist::stat_halfeye( aes(thickness = stat(pdf*n)), adjust = 0.5, justification = -.5, .width = 0, point_colour = NA ) + geom_boxplot(alpha = 0.5, varwidth = TRUE)
效果验证
运行修复后的代码,x轴会完整显示Pan_01、Pan_02、Pan_03、Pan_all四个水平,每个水平下方附带对应的样本量。
内容的提问来源于stack exchange,提问作者EDG
相关产品推荐
相关产品推荐

