如何精确限制ggplot坐标轴范围?修正双Y轴缩放异常问题
这两个问题其实都和ggplot处理坐标轴的逻辑有关,我来一步步帮你解决:
当然可以,但要注意两种方法的区别:
ylim(c(low, high))会过滤掉超出范围的数据点,同时设置轴范围,但有时候ggplot会因为图层元素(比如点的大小)自动调整轴范围,导致看起来没生效;coord_cartesian(ylim = c(low, high))是仅裁剪显示范围,不会过滤数据,能精确控制图表显示的轴区间,数据本身保持完整,适合需要精确限制显示范围但不影响数据计算的场景。
你的核心问题在于sec_axis的转换函数里,用了min(.)和max(.)——这里的.代表的是左侧Y轴的刻度范围,而不是你数据中y的实际最小值和最大值。当ggplot自动扩展左侧Y轴范围(比如为了让点完整显示,默认会给轴加一些padding),这个范围就会比你的数据范围大,导致缩放计算出错。
解决方法很直接:直接使用数据中y的固定最小值和最大值来构建转换函数,而不是依赖轴的动态范围。具体步骤如下:
首先,提前计算数据的y极值:
mydata <- data.frame(x = c(0, 1, 2, 3, 4, 5), y = c(20, 55, 69, 72, 73, 72)) y_min <- min(mydata$y) # 20 y_max <- max(mydata$y) # 73 break2 <- seq(0, 100, 10)
然后根据你的需求选择对应的方案:
方案1:允许左侧Y轴自动扩展,但次Y轴严格对应数据极值
这个方案完全满足你“可接受Y轴范围扩大,但要求右侧次Y轴的0必须对应y的最小值20”的需求:
library(ggplot2) ggplot(data = mydata, aes(x = x, y = y)) + geom_point(shape = 21, size = 5, color = "black", fill = "orange", stroke = 1) + scale_x_continuous(name = 'Number of Contacts') + scale_y_continuous( name = "Remembered (%)", sec.axis = sec_axis( trans = ~ (. - y_min) * 100 / (y_max - y_min), name = "Remembered (Index)", breaks = break2 ) )
这样不管左侧Y轴怎么扩展,次Y轴的0都会严格对应y_min=20,100对应y_max=73。
方案2:精确限制左侧Y轴显示范围(同时保证次Y轴正确)
如果你需要左侧Y轴严格显示20-73的范围,用coord_cartesian替代ylim(ylim会过滤数据,coord_cartesian仅裁剪显示范围,更稳妥):
ggplot(data = mydata, aes(x = x, y = y)) + geom_point(shape = 21, size = 5, color = "black", fill = "orange", stroke = 1) + scale_x_continuous(name = 'Number of Contacts') + scale_y_continuous( name = "Remembered (%)", sec.axis = sec_axis( trans = ~ (. - y_min) * 100 / (y_max - y_min), name = "Remembered (Index)", breaks = break2 ) ) + coord_cartesian(ylim = c(20, 73))
为什么原来的代码会出错?
原来的trans = ~ (.-min(.)) * 100 / (max(.) - min(.))中,min(.)取的是左侧Y轴的最小刻度值(比如ggplot可能自动扩展到18左右),max(.)取的是轴的最大刻度值(比如75),所以计算出来的20对应的缩放值是(20-18)/(75-18)*100 ≈ 2.99,自然大于0,而73对应的是(73-18)/(75-18)*100 ≈ 82.09,达不到100。用固定的y_min和y_max就完全避免了这个问题。
内容的提问来源于stack exchange,提问作者Blockhuette

