为具有不同面数的地块获得相同的高度,并且 coord_fixed?
Get same height for plots having different facet numbers, and coord_fixed?
让我用图片解释我的意思:
set.seed(1) ## dummy data.frame:
df <- data.frame( value1 = sample(5:15, 20, replace = T), value2 = sample(5:15, 20, replace = T),
var1 = c(rep('type1',10), rep('type2',10)), var2 = c('a','b','c','d'))
## Plot 1
ggplot() +
geom_point(data = df, aes(value1, value2)) +
facet_grid(~var1) +
coord_fixed()
ggsave("plot_2facet.pdf", height=5, units = 'in')
#Saving 10.3 x 5 in image
## Plot 2 which I want to save in a separate file (!)
ggplot() +
geom_point(data = df, aes(value1, value2)) +
facet_grid(~var2) +
coord_fixed()
ggsave("plot_4facet.pdf", height=5, units = 'in')
#Saving 10.3 x 5 in image
现在这里发生了什么,设备具有相同的高度,但地块具有不同的高度。但我想为地块获得相同的高度。
在上面的代码中,我试图只指定高度,但 ggsave 然后只为设备采用固定宽度尺寸。
我尝试了 theme(plot.margin = margin(t=1,b=1))
,但这并没有改变任何东西。
取出coord_fixed()
得到相同高度的地块:
但是我想用coord_fixed()
。
是否有解决方案,或者我是否需要 "guess" 设备的宽度尺寸以获得正确的绘图高度?
干杯
编辑
理想情况下,这些图应该在单独的设备/文件中创建。
这对 ggplot 来说有点棘手,所以请原谅冗长、令人费解且诚然有点老套的答案。基本问题是 coord_fixed
,y-axis 的高度与 x-axis 的长度密不可分。
我们可以通过两种方式打破这种依赖关系:
通过使用 scale_y_continuous
的 expand
参数。这允许我们将 y 轴扩展超出数据范围的给定数量。棘手的一点是知道要扩展多少,因为这取决于 hard-to-predict 绘图的所有元素,包括有多少个面以及轴标题和标签的大小等。
允许两个图的宽度不同。如上所述,这里棘手的事情是如何找到正确的宽度,因为这取决于绘图的其他各个方面。
首先我展示了我们如何解决第一个版本(扩展了多少y-axis)。然后使用类似的方法和一些额外的技巧,我们也可以解决变宽版本。
找到扩展多少的解决方案y-axis
鉴于预测绘图区域有多大的困难(这取决于绘图所有元素的相对大小),我们可以做的是保存一个虚拟绘图,在其中我们对绘图区域进行阴影处理在黑色中,读回图像文件,然后测量黑色区域的大小以确定绘图区域有多大:
1) 让我们从将绘图分配给变量开始
p1 = ggplot(df1) +
geom_point(aes(value1, value2)) +
facet_grid(~var1) +
coord_fixed()
p2 = ggplot(df1) +
geom_point(aes(value1, value2)) +
facet_grid(~var2) +
coord_fixed()
2) 现在我们可以保存这些图的一些虚拟版本,这些图只显示一个黑色矩形,其中绘图区域是:
t_blank = theme(strip.background = element_rect(fill = NA),
strip.text = element_text(color=NA),
axis.title = element_text(color = NA),
axis.text = element_text(color = NA),
axis.ticks = element_line(color = NA))
p1 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn1 <- tempfile(fileext = '.png'), height=5, units = 'in')
p2 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn2 <- tempfile(fileext = '.png'), height=5, units = 'in')
3) 然后我们将这些读入一个数组(只需要第一个色带就足够了)
library(png)
p1.saved = readPNG(fn1)[,,1]
p2.saved = readPNG(fn2)[,,1]
4) 计算每个绘图区域的高度(值为零的black-shaded区域)
p1.height = diff(row(p1.saved)[range(which(p1.saved==0))])
p2.height = diff(row(p2.saved)[range(which(p2.saved==0))])
5) 根据这些找出我们需要将绘图区域扩大多少。请注意,我们从 1.1 中减去高度的比率,以说明原始地块已经在每个方向上按默认量 0.05 扩展的事实。 免责声明 -- 此公式适用于您的示例。我没有时间更广泛地检查它,它可能还需要调整以确保其他地块的普遍性
height.expand = 1.1 - p2.height / p1.height
6) 现在我们可以使用这个扩展因子保存图
ggsave("plot_2facet.pdf", p1, height=5, units = 'in')
ggsave("plot_4facet.pdf", p2 + scale_y_continuous(expand=c(height.expand, 0)),
height=5, units = 'in')
找到宽度改变多少的解决方案
首先,让我们将第一个图的宽度设置为我们想要的
p1.width = 10
现在,使用与上一节中相同的方法,我们可以找到该图中绘图区域的高度。
p1 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn1 <- tempfile(fileext = '.png'), height=5, width = p1.width, units = 'in')
p1.saved = readPNG(fn1, info = T)[,,1]
p1.height = diff(row(p1.saved)[range(which(p1.saved==0))])
接下来,我们找到第二个绘图必须具有的最小宽度才能获得相同的高度(注意 - 我们在这里寻找最小值,因为任何比这更大的宽度都不会增加高度,它已经填满了垂直 space,但会简单地在左边和右边添加白色space)
我们将使用函数 uniroot
求解宽度,该函数会找到函数过零的位置。要使用 uniroot
,我们首先定义一个函数,该函数将在给定宽度作为参数的情况下计算绘图的高度。然后 returns 该高度与我们想要的高度之间的差异。此函数中的 if (x==0) x = -1e-8
行是一个肮脏的技巧,它允许 uniroot 求解一个达到零但不跨越它的函数 - see here.
fn2 <- tempfile(fileext = '.png')
find.p2 = function(w){
p = p2 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn2, p, height=5, width = w, units = 'in')
p2.saved = readPNG(fn2, info = T)[,,1]
p2.height = diff(row(p2.saved)[range(which(p2.saved==0))])
x = abs(p1.height - p2.height)
if (x==0) x = -1e-8
x
}
N1 = length(unique(df$var1))
N2 = length(unique(df$var2))
p2.width = uniroot(find.p2, c(p1.width, p1.width*N2/N1))
现在我们已准备好保存具有正确宽度的绘图,以确保它们具有相同的高度。
p1
ggsave("plot_2facet.pdf", height=5, width = p1.width, units = 'in')
p2
ggsave("plot_4facet.pdf", height=5, width = p2.width$root, units = 'in')
您可以使用很棒的 egg
包来做到这一点(事实证明)。我实际上并不知道这是如何工作的,或者它是否比这种情况更普遍;我只是在 ggarrange 计算出对齐的基础上下了赌注。如果有人能阐明这一点,那就太好了!
library(egg)
getScale <- ggarrange(p1, p2, draw = F, ncol=2)
p1_sc <- ggarrange(p1, heights = getScale$heights[2])
ggsave("plot_2facet.pdf", plot=p1_sc, height=5, units = 'in')
p2_sc <- ggarrange(p2, heights = getScale$heights[2])
ggsave("plot_4facet.pdf", plot=p2_sc, height=5, units = 'in')
是的,我真的不知道这是怎么回事:
getScale$heights[2]
# [1] max(1*1null, 1*1null)
class(getScale$heights[2])
# [1] "unit.list" "unit"
编辑 ..它似乎确实概括了
p3 <- ggplot() +
geom_point(data = df, aes(value1, value2)) +
facet_wrap(~var2, nrow=2) +
coord_fixed()
getScale <- ggarrange(p1, p2, p3, draw = F, ncol=3)
p1_sc <- ggarrange(p1, heights = getScale$heights[2])
ggsave("plot_2facet.pdf", plot=p1_sc, height=5, units = 'in')
p2_sc <- ggarrange(p2, heights = getScale$heights[2])
ggsave("plot_4facet.pdf", plot=p2_sc, height=5, units = 'in')
p3_sc <- ggarrange(p3, heights = getScale$heights[2])
ggsave("plot_4facet_2row.pdf", plot=p3_sc, height=5, units = 'in')
让我用图片解释我的意思:
set.seed(1) ## dummy data.frame:
df <- data.frame( value1 = sample(5:15, 20, replace = T), value2 = sample(5:15, 20, replace = T),
var1 = c(rep('type1',10), rep('type2',10)), var2 = c('a','b','c','d'))
## Plot 1
ggplot() +
geom_point(data = df, aes(value1, value2)) +
facet_grid(~var1) +
coord_fixed()
ggsave("plot_2facet.pdf", height=5, units = 'in')
#Saving 10.3 x 5 in image
## Plot 2 which I want to save in a separate file (!)
ggplot() +
geom_point(data = df, aes(value1, value2)) +
facet_grid(~var2) +
coord_fixed()
ggsave("plot_4facet.pdf", height=5, units = 'in')
#Saving 10.3 x 5 in image
现在这里发生了什么,设备具有相同的高度,但地块具有不同的高度。但我想为地块获得相同的高度。
在上面的代码中,我试图只指定高度,但 ggsave 然后只为设备采用固定宽度尺寸。
我尝试了 theme(plot.margin = margin(t=1,b=1))
,但这并没有改变任何东西。
取出coord_fixed()
得到相同高度的地块:
但是我想用coord_fixed()
。
是否有解决方案,或者我是否需要 "guess" 设备的宽度尺寸以获得正确的绘图高度?
干杯
编辑
理想情况下,这些图应该在单独的设备/文件中创建。
这对 ggplot 来说有点棘手,所以请原谅冗长、令人费解且诚然有点老套的答案。基本问题是 coord_fixed
,y-axis 的高度与 x-axis 的长度密不可分。
我们可以通过两种方式打破这种依赖关系:
通过使用
scale_y_continuous
的expand
参数。这允许我们将 y 轴扩展超出数据范围的给定数量。棘手的一点是知道要扩展多少,因为这取决于 hard-to-predict 绘图的所有元素,包括有多少个面以及轴标题和标签的大小等。允许两个图的宽度不同。如上所述,这里棘手的事情是如何找到正确的宽度,因为这取决于绘图的其他各个方面。
首先我展示了我们如何解决第一个版本(扩展了多少y-axis)。然后使用类似的方法和一些额外的技巧,我们也可以解决变宽版本。
找到扩展多少的解决方案y-axis
鉴于预测绘图区域有多大的困难(这取决于绘图所有元素的相对大小),我们可以做的是保存一个虚拟绘图,在其中我们对绘图区域进行阴影处理在黑色中,读回图像文件,然后测量黑色区域的大小以确定绘图区域有多大:
1) 让我们从将绘图分配给变量开始
p1 = ggplot(df1) +
geom_point(aes(value1, value2)) +
facet_grid(~var1) +
coord_fixed()
p2 = ggplot(df1) +
geom_point(aes(value1, value2)) +
facet_grid(~var2) +
coord_fixed()
2) 现在我们可以保存这些图的一些虚拟版本,这些图只显示一个黑色矩形,其中绘图区域是:
t_blank = theme(strip.background = element_rect(fill = NA),
strip.text = element_text(color=NA),
axis.title = element_text(color = NA),
axis.text = element_text(color = NA),
axis.ticks = element_line(color = NA))
p1 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn1 <- tempfile(fileext = '.png'), height=5, units = 'in')
p2 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn2 <- tempfile(fileext = '.png'), height=5, units = 'in')
3) 然后我们将这些读入一个数组(只需要第一个色带就足够了)
library(png)
p1.saved = readPNG(fn1)[,,1]
p2.saved = readPNG(fn2)[,,1]
4) 计算每个绘图区域的高度(值为零的black-shaded区域)
p1.height = diff(row(p1.saved)[range(which(p1.saved==0))])
p2.height = diff(row(p2.saved)[range(which(p2.saved==0))])
5) 根据这些找出我们需要将绘图区域扩大多少。请注意,我们从 1.1 中减去高度的比率,以说明原始地块已经在每个方向上按默认量 0.05 扩展的事实。 免责声明 -- 此公式适用于您的示例。我没有时间更广泛地检查它,它可能还需要调整以确保其他地块的普遍性
height.expand = 1.1 - p2.height / p1.height
6) 现在我们可以使用这个扩展因子保存图
ggsave("plot_2facet.pdf", p1, height=5, units = 'in')
ggsave("plot_4facet.pdf", p2 + scale_y_continuous(expand=c(height.expand, 0)),
height=5, units = 'in')
找到宽度改变多少的解决方案
首先,让我们将第一个图的宽度设置为我们想要的
p1.width = 10
现在,使用与上一节中相同的方法,我们可以找到该图中绘图区域的高度。
p1 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn1 <- tempfile(fileext = '.png'), height=5, width = p1.width, units = 'in')
p1.saved = readPNG(fn1, info = T)[,,1]
p1.height = diff(row(p1.saved)[range(which(p1.saved==0))])
接下来,我们找到第二个绘图必须具有的最小宽度才能获得相同的高度(注意 - 我们在这里寻找最小值,因为任何比这更大的宽度都不会增加高度,它已经填满了垂直 space,但会简单地在左边和右边添加白色space)
我们将使用函数 uniroot
求解宽度,该函数会找到函数过零的位置。要使用 uniroot
,我们首先定义一个函数,该函数将在给定宽度作为参数的情况下计算绘图的高度。然后 returns 该高度与我们想要的高度之间的差异。此函数中的 if (x==0) x = -1e-8
行是一个肮脏的技巧,它允许 uniroot 求解一个达到零但不跨越它的函数 - see here.
fn2 <- tempfile(fileext = '.png')
find.p2 = function(w){
p = p2 + geom_rect(aes(xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf), fill='black') +
t_blank
ggsave(fn2, p, height=5, width = w, units = 'in')
p2.saved = readPNG(fn2, info = T)[,,1]
p2.height = diff(row(p2.saved)[range(which(p2.saved==0))])
x = abs(p1.height - p2.height)
if (x==0) x = -1e-8
x
}
N1 = length(unique(df$var1))
N2 = length(unique(df$var2))
p2.width = uniroot(find.p2, c(p1.width, p1.width*N2/N1))
现在我们已准备好保存具有正确宽度的绘图,以确保它们具有相同的高度。
p1
ggsave("plot_2facet.pdf", height=5, width = p1.width, units = 'in')
p2
ggsave("plot_4facet.pdf", height=5, width = p2.width$root, units = 'in')
您可以使用很棒的 egg
包来做到这一点(事实证明)。我实际上并不知道这是如何工作的,或者它是否比这种情况更普遍;我只是在 ggarrange 计算出对齐的基础上下了赌注。如果有人能阐明这一点,那就太好了!
library(egg)
getScale <- ggarrange(p1, p2, draw = F, ncol=2)
p1_sc <- ggarrange(p1, heights = getScale$heights[2])
ggsave("plot_2facet.pdf", plot=p1_sc, height=5, units = 'in')
p2_sc <- ggarrange(p2, heights = getScale$heights[2])
ggsave("plot_4facet.pdf", plot=p2_sc, height=5, units = 'in')
是的,我真的不知道这是怎么回事:
getScale$heights[2]
# [1] max(1*1null, 1*1null)
class(getScale$heights[2])
# [1] "unit.list" "unit"
编辑 ..它似乎确实概括了
p3 <- ggplot() +
geom_point(data = df, aes(value1, value2)) +
facet_wrap(~var2, nrow=2) +
coord_fixed()
getScale <- ggarrange(p1, p2, p3, draw = F, ncol=3)
p1_sc <- ggarrange(p1, heights = getScale$heights[2])
ggsave("plot_2facet.pdf", plot=p1_sc, height=5, units = 'in')
p2_sc <- ggarrange(p2, heights = getScale$heights[2])
ggsave("plot_4facet.pdf", plot=p2_sc, height=5, units = 'in')
p3_sc <- ggarrange(p3, heights = getScale$heights[2])
ggsave("plot_4facet_2row.pdf", plot=p3_sc, height=5, units = 'in')