如何在ggplot中独立定位两个图例
Posted
技术标签:
【中文标题】如何在ggplot中独立定位两个图例【英文标题】:How do I position two legends independently in ggplot 【发布时间】:2012-10-20 01:57:47 【问题描述】:标题很好地涵盖了它。
我有两个关于大小和颜色的图例,并希望有一个,比如说,在顶部,一个在图表中。
这可能吗,如果可以,怎么做
TIA
【问题讨论】:
我认为不可能(图例的放置位置由主题控制,定义图例位置的那些部分仅采用单个值)。但是,我不太确定这不可能成为答案。 猜猜如果是,其中一位专家现在应该已经在了 @pssguy,可以通过一些摆弄来完成。为了控制图例,您需要提取单独的图例,然后将它们排列在最初不包含图例的图中。 【参考方案1】:据我了解,基本上ggplot2
对图例的控制非常有限。这是 Hadley 书中的一段(第 111 页):
ggplot2 尝试使用尽可能少的图例来准确传达情节中使用的美学。如果变量与多个美学一起使用,它会通过组合图例来做到这一点。图 6.14 显示了点 geom 的一个例子:如果颜色和形状都映射到同一个变量,那么只需要一个图例。为了合并图例,它们必须具有相同的名称(相同的图例标题)。因此,如果您更改其中一个合并图例的名称,则需要为所有图例更改名称。
【讨论】:
感谢您的摘录。所有示例都在同一位置显示图例【参考方案2】:可以通过从图中提取单独的图例,然后将图例安排在相关图中来完成。此处的代码使用gtable
包中的函数进行提取,然后使用gridExtra
包中的函数进行排列。目的是要有一个包含颜色图例和尺寸图例的图。首先,从仅包含颜色图例的图中提取颜色图例。其次,从仅包含尺寸图例的图中提取尺寸图例。第三,绘制一个不包含图例的图。第四,将剧情和两个传说合并成一个新的剧情。
# Some data
df <- data.frame(
x = 1:10,
y = 1:10,
colour = factor(sample(1:3, 10, replace = TRUE)),
size = factor(sample(1:3, 10, replace = TRUE)))
library(ggplot2)
library(gridExtra)
library(gtable)
library(grid)
### Step 1
# Draw a plot with the colour legend
(p1 <- ggplot(data = df, aes(x=x, y=y)) +
geom_point(aes(colour = colour)) +
theme_bw() +
theme(legend.position = "top"))
# Extract the colour legend - leg1
leg1 <- gtable_filter(ggplot_gtable(ggplot_build(p1)), "guide-box")
### Step 2
# Draw a plot with the size legend
(p2 <- ggplot(data = df, aes(x=x, y=y)) +
geom_point(aes(size = size)) +
theme_bw())
# Extract the size legend - leg2
leg2 <- gtable_filter(ggplot_gtable(ggplot_build(p2)), "guide-box")
# Step 3
# Draw a plot with no legends - plot
(plot <- ggplot(data = df, aes(x=x, y=y)) +
geom_point(aes(size = size, colour = colour)) +
theme_bw() +
theme(legend.position = "none"))
### Step 4
# Arrange the three components (plot, leg1, leg2)
# The two legends are positioned outside the plot:
# one at the top and the other to the side.
plotNew <- arrangeGrob(leg1, plot,
heights = unit.c(leg1$height, unit(1, "npc") - leg1$height), ncol = 1)
plotNew <- arrangeGrob(plotNew, leg2,
widths = unit.c(unit(1, "npc") - leg2$width, leg2$width), nrow = 1)
grid.newpage()
grid.draw(plotNew)
# OR, arrange one legend at the top and the other inside the plot.
plotNew <- plot +
annotation_custom(grob = leg2, xmin = 7, xmax = 10, ymin = 0, ymax = 4)
plotNew <- arrangeGrob(leg1, plotNew,
heights = unit.c(leg1$height, unit(1, "npc") - leg1$height), ncol = 1)
grid.newpage()
grid.draw(plotNew)
【讨论】:
嗨桑迪。这是一个令人印象深刻的例子。您能否将heights
参数的使用逻辑分解为arrangeGrob
,例如在plotNew <- arrangeGrob(leg1, plot, heights = unit.c(leg1$height, unit(1, "npc") - leg1$height), ncol = 1)
中?我知道heights
作为参数传递给grid.layout
,但我无法看到它是如何在这里使用的。谢谢。
嗨@Faheem,有两个图要垂直排列:leg1
和plot
;因此unit.c()
函数中有两个高度。 leg1
具有绝对高度,由 leg1$height
给出。第二个高度unit(1, "npc") - leg1$height
从设备高度中减去图例高度,得到plot
的可用高度。在第一个示例中,类似的逻辑适用于宽度。有两个要绘制的 grobs:情节和图例。因此unit.c()
函数中有两个宽度;一个用于plotNew
,第二个用于leg2
。
谢谢,桑迪。还有一个问题——你怎么知道设备的高度是unit(1, "npc")
?如果您将其中一些信息添加到您的问题中,这将很有用。您可以提及 (a) heights
参数和 widths
参数传递给 grid.layout
,(b) unit(1, "npc")
是设备的高度,因此 (c) 例如leg1$height
和 unit(1, "npc") - leg1$height
分别是图例和主图的高度。如果你愿意,我可以添加。
@Faheem, ?grid::unit
提供单位的详细信息。严格来说,unit(1, "npc")
是视口的高度(或宽度)。 @baptiste,gridExtra
包的作者,之前在 SO 上写过这种结构,也在其他地方,但我现在找不到它。其他人(例如@agstudy)在他们对 SO 的回复中使用了类似的结构。因此,至于在我的帖子中不包括细节,我假设已经掌握了知识或可以轻松访问的知识。
嗨桑迪。好的,我看到页面显示“视口的宽度和高度为 1 个单位。”。我不知道viewport
指的是该设备。谢谢。【参考方案3】:
使用ggplot2
和cowplot
(= ggplot2 扩展)。
该方法类似于 Sandy 的方法,因为它将图例作为单独的对象取出,并让您独立进行放置。它主要是为多个图例设计的,这些图例属于图网格中的两个或多个图。
思路如下:
-
创建 Plot1、Plot2、...、PlotX 没有图例
使用图例创建 Plot1、Plot2、...、PlotX
将第 1 步和第 2 步中的图例提取到单独的对象中
设置图例网格并按照您想要的方式排列图例
创建结合图和图例的网格
这似乎有点复杂且耗时/代码,但设置一次,您可以适应并使用它来进行各种情节/图例自定义。
library(ggplot2)
library(cowplot)
# Some data
df <- data.frame(
Name = factor(rep(c("A", "B", "C"), 12)),
Month = factor(rep(1:12, each = 3)),
Temp = sample(0:40, 12),
Precip = sample(50:400, 12)
)
# 1. create plot1
plot1 <- ggplot(df, aes(Month, Temp, fill = Name)) +
geom_point(
show.legend = F, aes(group = Name, colour = Name),
size = 3, shape = 17
) +
geom_smooth(
method = "loess", se = F,
aes(group = Name, colour = Name),
show.legend = F, size = 0.5, linetype = "dashed"
)
# 2. create plot2
plot2 <- ggplot(df, aes(Month, Precip, fill = Name)) +
geom_bar(stat = "identity", position = "dodge", show.legend = F) +
geom_smooth(
method = "loess", se = F,
aes(group = Name, colour = Name),
show.legend = F, size = 1, linetype = "dashed"
) +
scale_fill_grey()
# 3.1 create legend1
legend1 <- ggplot(df, aes(Month, Temp)) +
geom_point(
show.legend = T, aes(group = Name, colour = Name),
size = 3, shape = 17
) +
geom_smooth(
method = "loess", se = F, aes(group = Name, colour = Name),
show.legend = T, size = 0.5, linetype = "dashed"
) +
labs(colour = "Station") +
theme(
legend.text = element_text(size = 8),
legend.title = element_text(
face = "italic",
angle = -0, size = 10
)
)
# 3.2 create legend2
legend2 <- ggplot(df, aes(Month, Precip, fill = Name)) +
geom_bar(stat = "identity", position = "dodge", show.legend = T) +
scale_fill_grey() +
guides(
fill =
guide_legend(
title = "",
title.theme = element_text(
face = "italic",
angle = -0, size = 10
)
)
) +
theme(legend.text = element_text(size = 8))
# 3.3 extract "legends only" from ggplot object
legend1 <- get_legend(legend1)
legend2 <- get_legend(legend2)
# 4.1 setup legends grid
legend1_grid <- cowplot::plot_grid(legend1, align = "v", nrow = 2)
# 4.2 add second legend to grid, specifying its location
legends <- legend1_grid +
ggplot2::annotation_custom(
grob = legend2,
xmin = 0.5, xmax = 0.5, ymin = 0.55, ymax = 0.55
)
# 5. plot "plots" + "legends" (with legends in between plots)
cowplot::plot_grid(plot1, legends, plot2,
ncol = 3,
rel_widths = c(0.45, 0.1, 0.45)
)
由reprex package (v0.3.0) 于 2019 年 10 月 5 日创建
更改最终plot_grid()
调用的顺序将图例移到右侧:
cowplot::plot_grid(plot1, plot2, legends, ncol = 3,
rel_widths = c(0.45, 0.45, 0.1))
【讨论】:
以上是关于如何在ggplot中独立定位两个图例的主要内容,如果未能解决你的问题,请参考以下文章