Подтвердить что ты не робот

Как разместить две легенды самостоятельно в ggplot

Заголовок довольно хорошо охватывает его.

У меня есть две легенды, относящиеся к размеру и цвету, и хочу иметь один, скажем, сверху и один внутри графика.

Возможно ли это, и если да, то как

ТИА

4b9b3361

Ответ 1

По моему мнению, в основном существует ограниченный контроль над легендами в ggplot2. Вот параграф из книги Хэдли (стр. 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)

enter image description here

enter image description here

Ответ 3

Использование ggplot2 и cowplot (= расширение ggplot2).

Этот подход аналогичен подходу Sandy, поскольку он выделяет легенду как отдельные объекты и позволяет размещать объекты самостоятельно. Первоначально он был разработан для нескольких легенд, которые принадлежат двум или более сюжетам в сетке сюжетов.

Идея заключается в следующем:

  1. Создать Plot1, Plot2,..., PlotX без легенд
  2. Создать Plot1, Plot2,..., PlotX с легендами
  3. Извлечь легенды из шага 1 & 2 на отдельные объекты
  4. Настройте сетку легенд и расположите легенды так, как вы хотите
  5. Создавайте сетки, комбинируя сюжеты и легенды

Это кажется довольно сложным и требует много времени и кода, но настроенный один раз, вы можете адаптировать его и использовать для любого вида настройки сюжета/легенды.

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)
)

Created on 2019-10-05 by the reprex package (v0.3.0)


Изменение порядка последнего вызова plot_grid() перемещает легенды вправо:

cowplot::plot_grid(plot1, plot2, legends, ncol = 3, 
                   rel_widths = c(0.45, 0.45, 0.1))

Example2