ggplot2中Facet Wrap移除空行且保留指定排序的实现问题
咱们来一步步拆解你的问题,搞定这个“鱼和熊掌不能兼得”的困境:
问题根源
你在plot_e2里犯了个小错误:重复调用了scale_x_discrete。第二次调用会直接覆盖第一次的limits设置,导致你自定义的排序丢失,还会触发ggplot的警告(提示你重复设置了同一个比例尺)。另外,你对facet_wrap的scales参数理解可能有点偏差——因为用了coord_flip(),坐标轴的对应关系变了,咱们得调整一下。
解决方案
核心思路是:把drop=TRUE和自定义排序的limits合并到同一个scale_x_discrete调用中,同时调整分面的scales参数来匹配翻转后的坐标轴。
以下是修改后的完整代码:
# 生成示例数据(你的代码没问题,保留) c <- c('a', 'a', 'b', 'b') d <- c('firstsecond', 'firstfirst', 'lowerupper', 'lowerlower') e <- c(0.2, 0.3, 0.4, 0.5) f <- c('w', 'v','w', 'v') df <- cbind(c,d,e,f) df<- as.data.frame(df) df$e <- as.numeric(df$e) orderd <- c( 'firstfirst', 'firstsecond', 'lowerupper', 'lowerlower' ) df<- within(df, d <- factor(d, levels=orderd)) # 正确的绘图代码 plot_e3 <- ggplot(df, aes(x = d, y = e, color = f)) + # 修正:用ymin/ymax替代min/max,这是ggplot2的标准语法 geom_pointrange(aes(ymin = e - 1.95 * sqrt(e), ymax = e + 1.95 * sqrt(e)), shape = 15) + theme_bw() + # 调整scales为"free_x":因为coord_flip后,原x轴变为y轴,free_x控制这个分类轴的自由显示 facet_wrap(c ~ ., scales = "free_x", nrow = 5, strip.position = "left") + coord_flip() + # 合并drop和limits:drop=TRUE移除空白,limits保证排序遵循你定义的反转后因子水平 scale_x_discrete(drop = TRUE, limits = rev(levels(df$d))) + scale_colour_viridis_d(begin = 0.75 , end = 0) + geom_text(aes(label = f), colour = "black", size = 2.5, hjust=1.05, vjust=1.2) plot_e3
关键细节解释
合并
scale_x_discrete设置:drop=TRUE会让每个分面只显示当前存在的d水平,而limits=rev(levels(df$d))会保证这些保留的水平严格遵循你自定义的排序(反转后的因子水平),不会因为分面筛选而打乱顺序。调整
scales="free_x":
因为coord_flip()是最后执行的,原x轴(分类轴d)会变成翻转后的y轴。scales="free_x"会让每个分面的原x轴(即翻转后的y轴)只显示当前分面有数据的水平,彻底移除空白区域。关于
rev()的疑问:coord_flip()后,y轴的默认顺序是从上到下对应原x轴的从左到右。你定义的d因子水平是firstfirst → firstsecond → lowerupper → lowerlower,如果不用rev(),翻转后y轴从上到下会是这个顺序;而你用rev()后,原x轴从左到右变成lowerlower → lowerupper → firstsecond → firstfirst,翻转后y轴从上到下就是这个顺序,刚好符合你想要的“firstfirst在最下方”的视觉布局。
这样修改后,你就能同时实现保留自定义排序和移除空白区域的需求啦!
内容的提问来源于stack exchange,提问作者canIchangethis

