避免在分面ggplots中轴刻度标签的碰撞

11
在多面板/分面图中,我喜欢将面板合并在一起,使它们之间没有空间,例如使用theme(panel.spacing=grid::unit(0,"lines"))(Edward Tufte认为这样做很好,因为面板间的间隔会产生分散注意力的视觉效果,也会浪费数据空间,而我相信了他的说法)。
问题在于,根据各个分面行/列内数值的具体范围,相邻分面的轴标签可能会发生重叠。例如,在此图中,顶部面板中的底部刻度标签与中间面板中的顶部刻度标签有碰撞。
dd <- data.frame(x=rep(1:3,3),
                 y=c(0.1,0.2,0.3,
                     0.1,0.4,0.6,
                     1,2,3),
                 f=factor(rep(letters[1:3],each=3)))
library(ggplot2)
ggplot(dd,aes(x,y))+
   facet_grid(f~.,scale="free")+
   geom_point()+
   theme_bw(base_size=24)+
   theme(panel.spacing=grid::unit(0,"lines"))
ggsave("tmp1.png",width=4,height=6)

输入图像描述

我想要构建一个通用、方便的解决方案来解决这个问题——通过适当的方式(对于每个分面而言将不一样,因为它们的范围是异质的)扩展每个分面的限制,但抑制(至少)极值的标签和(可能的)刻度线。 我以前用非常 hacky 的方法在 scale_y_continuous 中设置了一个专门用途的 breaks 函数来实现这一点。我可能已经想到了其他的方法(如果我能让它们正常工作,我会把它们发布为答案),但我正在寻找可以指定 labels 和/或 breaks 的合理稳健的通用函数。

这与Automate tick max and min in faceted ggplot 不同,后者只是想要按分面获取最大/最小值。

这在一般情况下很棘手,可能无法完全解决;我考虑过仅清除极值标签,但如果只有两个或三个刻度标签,这种方法就会失败。也许有一个使用 expand_limits() 的解决方案,但在多个分面上很难实现...


应该在 ggplot2 的 GitHub 代码库中将此作为问题进行提交。 - coatless
也许可以检查范围的间断点,并将其与标签cex进行比较,以找到触发极端标签抑制的不可接受的最小比率?甚至可以触发更通用版本的先前黑客。 - K.Daisey
1
我知道这并没有什么帮助,但是……真的很遗憾,使用 ggplot2,你必须学习一个全新(且只有一次)的面向对象系统的基础知识,才能修改像这样的小细节。而使用 lattice,你只需要添加自定义的 prepanel()yscale.components() 函数就可以了。我期待着大家在这里提出的任何解决方案。 - Josh O'Brien
@JoshO'Brien,我认为通过scale_y_continuousbreakslabels参数有可能得到一个合理的解决方案。更多的是关于在那里放置内容的问题(就像prepanel()一样...)。 - Ben Bolker
1个回答

4

编辑 更新至ggplot2 ver 3.0.0

一种可能的方法是修改构建数据。可以获取原始绘图的限制,并应用乘法扩展因子。主要和次要断点的相对位置也需要进行调整。注意,该函数允许选择要应用扩展的面板。

这与在scale_y_continuous中使用 expand有什么区别(除了能够选择面板)?此函数保留了刻度标记和网格线在原始图中的情况。只是它们占用的空间更小。但是,expand将压缩比例,如果可以,ggplot会添加新标签、网格线和刻度标记。

很容易破坏该函数的运行 - ggplot已经在最近的版本中更改了构建数据元素的名称。

dd <- data.frame(x=rep(1:3,3),
                 y=c(0.1,0.2,0.3,
                     0.1,0.4,0.6,
                     1,2,3),
                 f=factor(rep(letters[1:3],each=3)))
library(ggplot2)


p = ggplot(dd,aes(x,y))+
   facet_grid(f~.,scale="free")+
   geom_point()+
   theme_bw(base_size=24)+
   theme(panel.spacing=grid::unit(0,"lines"))


expand = function(plot, mult = 0.1, applyTo) {
# Get the build data
gd = ggplot_build(plot)

# Get the original limits,
# and calculate the new limits,
# expanded according to the multiplicative factor
limits <- sapply(gd$layout$panel_params, "[[", "y.range")    # Original limits
fac = mult*(limits[2,] - limits[1, ])  
newlimits = rbind(limits[1, ] - fac, limits[2, ] + fac)

# The range for the new limits
range = newlimits[2, ] - newlimits[1, ]

# Calculate the new y.major and y.minor relative positions
# and put them along with the new limits back into the build data

N = dim(gd$layout$panel_params)[1]  # Number of panels

for(i in applyTo) {
y.major = (gd$layout$panel_params[[i]]$y.major_source - newlimits[1, i]) / range[i]
y.minor = (gd$layout$panel_params[[i]]$y.minor_source - newlimits[1, i]) / range[i]

gd$layout$panel_params[[i]]$y.range = newlimits[, i]

gd$layout$panel_params[[i]]$y.major = y.major
gd$layout$panel_params[[i]]$y.minor = y.minor
}

# Get the gtable
ggplot_gtable(gd)
}


# Plot it
grid::grid.draw(expand(p, mult = 0.1, applyTo = 1:2))

原始文本
enter image description here

修改后
enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接