facet_wrap与scale_y_continuous绘图冲突问题求助
我最近在处理R语言可视化需求时碰到了个棘手的小问题——用ggplot2绘图时,同时设置facet_wrap(scales="free")和scale_y_continuous()来显示百分比标签会报错,单独用其中一个都能正常运行,但俩放一起就不行。先给大家看看我的测试数据和代码:
测试数据与预览
library(tidyverse) dates <- c("2019-03-01", "2019-04-01", "2019-05-01", "2019-06-01", "2019-07-01") d <- data_frame(month = dates %>% factor(ordered = TRUE), subject = "Abc_14", taux = runif(length(dates)), volume = (runif(length(dates)) * 100) %>% as.integer()) d %>% glimpse()
数据预览结果:
Observations: 5
Variables: 4
$ month2019-03-01, 2019-04-01, 2019-05-01, 2019-06-01, 2019-07-01
$ subject"Abc_14", "Abc_14", "Abc_14", "Abc_14", "Abc_14"
$ taux0.67040271, 0.85711442, 0.94608828, 0.65138723, 0.02835217
$ volume52, 46, 33, 17, 69
报错的绘图代码
d %>% ggplot(aes(volume, taux)) + geom_density2d(size = .2) + geom_point(aes(fill = subject)) + geom_smooth(method = "lm", se = FALSE) + facet_wrap(facets = vars(month), scales = "free") + scale_y_continuous(labels = scales::percent, breaks = seq(0, 1, .2)) + theme(legend.position = "none")
收到的错误信息:
Error in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, : argument 'nsmall' incorrect
问题核心与我的需求
这个冲突的根源在于:当设置scales="free"后,部分分面的Y轴实际范围可能完全不包含我们指定的固定breaks(比如测试数据里最后一个月的taux只有0.028,远低于0.2),这就导致ggplot在尝试格式化标签时出现参数错误。
我的核心需求很明确:
- 必须保留分面的自由缩放,因为不同月份的数据范围差异很大
- Y轴标签必须显示为百分比格式
- 真实数据有200多行、30个subject、10个month,还有缺失组合,数据还在持续更新
解决方案
解决这个问题的关键是让breaks和labels能根据每个分面的实际数据范围动态调整,而不是用固定的seq(0,1,.2)。我们可以用scales::extended_breaks()来生成自适应的breaks,再结合scales::percent()来格式化标签,具体代码如下:
d %>% ggplot(aes(volume, taux)) + geom_density2d(size = .2) + geom_point(aes(fill = subject)) + geom_smooth(method = "lm", se = FALSE) + facet_wrap(facets = vars(month), scales = "free") + scale_y_continuous( labels = scales::percent, breaks = scales::extended_breaks(n = 5) # 可根据需求调整breaks的数量 ) + theme(legend.position = "none")
如果希望尽量保留0到1的区间范围,但仍允许breaks自适应,也可以给extended_breaks()指定范围:
scale_y_continuous( labels = scales::percent, breaks = function(x) scales::extended_breaks(n = 5)(c(0, max(x))) )
这样既满足了分面自由缩放的需求,又能正常显示百分比格式的Y轴标签,而且能完美适配不断更新的动态数据。
内容的提问来源于stack exchange,提问作者Costin

