ggplot中多个填充图例的说明

14

我是ggplot2的初学者,所以如果这个问题听起来太基础,请谅解。我将感激任何指导。我已经花了4个小时并查看了这个SO thread R: Custom Legend for Multiple Layer ggplot,但最终没有结果。

目标:我想能够为不同图层使用的不同填充颜色应用图例。我做这个示例只是为了测试我对应用ggplot2概念的理解。

另外,我不想更改形状类型; 更改填充颜色是可以的——通过“填充”,我并不意味着我们可以改变“颜色”。因此,如果您能纠正我的错误,我将不胜感激。

尝试 1:这是一个没有手动设置任何颜色的基本代码。

ggplot(mtcars, aes(disp,mpg)) +
geom_point(aes(fill = factor(vs)),shape = 23, size = 8, alpha = 0.4) +
geom_point (aes(fill = factor(cyl)),shape = 21, size = 2) +
geom_rect(aes(xmin = min(disp)-5, ymax = max(mpg) + 2,fill = "cyan"), 
xmax = mean(range(mtcars$disp)),ymin = 25, alpha = 0.02) ##region for high mpg
输出结果看起来像这样: 在此输入图像描述 现在,这个图片有几个问题:
问题1)显示“高公里每加仑区域”的青色矩形已经失去了它的图例。
问题2)ggplot试图从两个 geom_point() 层中结合传说,因此这两个 geom_point() 的传说也混淆了。
问题3) ggplot2 使用的默认颜色调色板使得颜色对我的眼睛不可区分。
所以,我尝试手动设置颜色,即先修复上述第3个问题。
ggplot(mtcars, aes(disp,mpg)) + 
       geom_point(aes(fill = factor(vs)),shape = 23, size = 8, alpha = 0.4)+
       geom_point(aes(fill = factor(cyl)),shape = 21, size = 2) +
       geom_rect(aes(xmin = min(disp)-5, ymax = max(mpg) + 2,fill = "cyan"),
                 xmax = mean(range(mtcars$disp)),ymin = 25, alpha = 0.02) + 
     scale_fill_manual(values = c("green","blue", "black", "cyan", "red", "orange"), 
labels=c("4 gears","6 gears","8 gears","High mpg","0","1"))

以下是输出结果:输入图像描述

问题#4: 我认为ggplot2希望我按照设置图层的顺序提供颜色,即首先设定mtcars $ vs 填充的颜色,然后是mtcars $ cyl 填充和最后一个青色矩形。我通过修改代码来解决了这个问题:

ggplot(mtcars, aes(disp,mpg)) + 
       geom_point(aes(fill = factor(vs)),shape = 23, size = 8, alpha = 0.4) +
       geom_point(aes(fill = factor(cyl)),shape = 21, size = 2) +
       geom_rect(aes(xmin = min(disp)-5, ymax = max(mpg) + 2,fill = "cyan"),
                 xmax = mean(range(mtcars$disp)),ymin = 25, alpha = 0.02) + 
     scale_fill_manual(values = c("red", "orange", "green", "blue", "black", "cyan"), 
labels=c("0","1","4 gears","6 gears","8 gears","High mpg")) #changed the order

所以,我有两个问题:

问题1: 如何修复图例 - 我想要三个不同的图例 - 一个用于矩形填充(我称之为高 mpg 矩形),另一个用于由 mtcars$vs 表示的 geom_point() 填充和最后一个用于由 mtcars$cyl 表示的 geom_point() 填充。

问题2: 我关于颜色按层排序的假设是否正确(即上面讨论的 Issue#4)?我表示怀疑,因为如果有很多因素,我们需要记住它们的顺序并手动应用每个 geom_*() 图层中创建的颜色调色板吗?

作为一个初学者,我花了很多时间在这上面,到处搜索。所以,我会非常感激你的指导。


1
@42- 我同意你关于Tufte和Stephen Few的看法。然而,我只是在测试自己应用ggplot2()技能的能力。所以,我认为他应该没问题。我完全不知道如何解决这个问题。如果你能帮我解决这个问题,我将非常感激。 - watchtower
3个回答

15

(注意,我在几次来回编辑之后对其进行了清理--请查看修订历史记录以获取更多我尝试过的内容。)

这些比例尺确实是用于显示一种类型的数据。一个方法是同时使用colfill,这可以让您至少获得2个图例。然后,您可以添加linetype并使用override.aes稍微调整一下。值得注意的是,我认为这可能会比解决问题更容易导致更多的问题(通常情况下)。如果您非常需要这样做,那么您可以这样做(例如下面的示例)。但是,如果我能说服您:如果有可能,请我恳求您不要使用这种方法。映射到不同的东西(例如shapelinetype)很可能会导致更少的混淆。我在下面举例说明。

此外,在手动设置颜色或填充时,最好使用命名向量来palette确保颜色与您想要的匹配。如果没有,则匹配将按因子级别的顺序进行。

ggplot(mtcars, aes(x = disp
                   , y = mpg)) +
  ##region for high mpg 
  geom_rect(aes(linetype = "High MPG")
            , xmin = min(mtcars$disp)-5
            , ymax = max(mtcars$mpg) + 2
            , fill = "cyan"
            , xmax = mean(range(mtcars$disp))
            , ymin = 25
            , alpha = 0.02
            , col = "black") + 
  ## test diff region
  geom_rect(aes(linetype = "Other Region")
            , xmin = 300
            , xmax = 400
            , ymax = 30
            , ymin = 25
            , fill = "yellow"
            , alpha = 0.02
            , col = "black") + 
  geom_point(aes(fill = factor(vs)),shape = 23, size = 8, alpha = 0.4) +
  geom_point (aes(col = factor(cyl)),shape = 19, size = 2) +
  scale_color_manual(values = c("4" = "red"
                                , "6" = "orange"
                                , "8" = "green")
                     , name = "Cylinders") +
  scale_fill_manual(values = c("0" = "blue"
                               , "1" = "black"
                               , "cyan" = "cyan")
                    , name = "V/S"
                    , labels = c("0?", "1?", "High MPG")) +
  scale_linetype_manual(values = c("High MPG" = 0
                                   , "Other Region" = 0)
                        , name = "Region"
                        , guide = guide_legend(override.aes = list(fill = c("cyan", "yellow")
                                                                       , alpha = .4)))

此处插入图片描述

我认为以下绘图方式适用于几乎所有情况:

ggplot(mtcars, aes(x = disp
                   , y = mpg)) +
  ##region for high mpg 
  geom_rect(aes(linetype = "High MPG")
            , xmin = min(mtcars$disp)-5
            , ymax = max(mtcars$mpg) + 2
            , fill = NA
            , xmax = mean(range(mtcars$disp))
            , ymin = 25
            , col = "black") + 
  ## test diff region
  geom_rect(aes(linetype = "Other Region")
            , xmin = 300
            , xmax = 400
            , ymax = 30
            , ymin = 25
            , fill = NA
            , col = "black") +
  geom_point(aes(col = factor(cyl)
                 , shape = factor(vs))
             , size = 3) +
  scale_color_brewer(name = "Cylinders"
                     , palette = "Set1") +
  scale_shape(name = "V/S") +
  scale_linetype_manual(values = c("High MPG" = "dotted"
                                   , "Other Region" = "dashed")
                        , name = "Region")

输入图片说明

出于某种原因,您坚持使用fill。这里提供了一种方法,其绘制结果与此答案中的第一个图形完全相同,但将fill用作每个层次的美学特征。如果这不是您所坚持的内容,那么我仍然不知道您要寻找什么。

ggplot(mtcars, aes(x = disp
                   , y = mpg)) +
  ##region for high mpg 
  geom_rect(aes(linetype = "High MPG")
            , xmin = min(mtcars$disp)-5
            , ymax = max(mtcars$mpg) + 2
            , fill = "cyan"
            , xmax = mean(range(mtcars$disp))
            , ymin = 25
            , alpha = 0.02
            , col = "black") + 
  ## test diff region
  geom_rect(aes(linetype = "Other Region")
            , xmin = 300
            , xmax = 400
            , ymax = 30
            , ymin = 25
            , fill = "yellow"
            , alpha = 0.02
            , col = "black") + 
  geom_point(aes(fill = factor(vs)),shape = 23, size = 8, alpha = 0.4) +
  geom_point (aes(col = "4")
              , data = mtcars[mtcars$cyl == 4, ]
              , shape = 21
              , size = 2
              , fill = "red") +
  geom_point (aes(col = "6")
              , data = mtcars[mtcars$cyl == 6, ]
              , shape = 21
              , size = 2
              , fill = "orange") +
  geom_point (aes(col = "8")
              , data = mtcars[mtcars$cyl == 8, ]
              , shape = 21
              , size = 2
              , fill = "green") +
  scale_color_manual(values = c("4" = NA
                                , "6" = NA
                                , "8" = NA)
                     , name = "Cylinders"
                     , guide = guide_legend(override.aes = list(fill = c("red","orange","green")))) +
  scale_fill_manual(values = c("0" = "blue"
                               , "1" = "black"
                               , "cyan" = "cyan")
                    , name = "V/S"
                    , labels = c("0?", "1?", "High MPG")) +
  scale_linetype_manual(values = c("High MPG" = 0
                                   , "Other Region" = 0)
                        , name = "Region"
                        , guide = guide_legend(override.aes = list(fill = c("cyan", "yellow")
                                                                   , alpha = .4)))

因为我似乎无法放手不管这个问题——这里有另一种方法,仅使用填充来实现美观效果,然后为单个层制作单独的图例,并使用cowplot将所有内容拼在一起,松散地遵循这个教程

library(cowplot)
library(dplyr)

theme_set(theme_minimal())

allScales <-
  c("4" = "red"
    , "6" = "orange"
    , "8" = "green"
    , "0" = "blue"
    , "1" = "black"
    , "High MPG" = "cyan"
    , "Other Region" = "yellow")

mainPlot <-
  ggplot(mtcars, aes(x = disp
                     , y = mpg)) +
  ##region for high mpg 
  geom_rect(aes(fill = "High MPG")
            , xmin = min(mtcars$disp)-5
            , ymax = max(mtcars$mpg) + 2
            , xmax = mean(range(mtcars$disp))
            , ymin = 25
            , alpha = 0.02) + 
  ## test diff region
  geom_rect(aes(fill = "Other Region")
            , xmin = 300
            , xmax = 400
            , ymax = 30
            , ymin = 25
            , alpha = 0.02) + 
  geom_point(aes(fill = factor(vs)),shape = 23, size = 8, alpha = 0.4) +
  geom_point (aes(fill = factor(cyl)),shape = 21, size = 2) +
  scale_fill_manual(values = allScales)

vsLeg <-
  (ggplot(mtcars, aes(x = disp
                      , y = mpg)) +
     geom_point(aes(fill = factor(vs)),shape = 23, size = 8, alpha = 0.4) +
     scale_fill_manual(values = allScales
                       , name = "VS")
  ) %>%
  ggplotGrob %>%
  {.$grobs[[which(sapply(.$grobs, function(x) {x$name}) == "guide-box")]]}



cylLeg <-
  (ggplot(mtcars, aes(x = disp
                      , y = mpg)) +
     geom_point (aes(fill = factor(cyl)),shape = 21, size = 2) +
     scale_fill_manual(values = allScales
                       , name = "Cylinders")
  ) %>%
  ggplotGrob %>%
  {.$grobs[[which(sapply(.$grobs, function(x) {x$name}) == "guide-box")]]}


regionLeg <-
  (ggplot(mtcars, aes(x = disp
                      , y = mpg)) +
     geom_rect(aes(fill = "High MPG")
               , xmin = min(mtcars$disp)-5
               , ymax = max(mtcars$mpg) + 2
               , xmax = mean(range(mtcars$disp))
               , ymin = 25
               , alpha = 0.02) + 
     ## test diff region
     geom_rect(aes(fill = "Other Region")
               , xmin = 300
               , xmax = 400
               , ymax = 30
               , ymin = 25
               , alpha = 0.02) + 
     scale_fill_manual(values = allScales
                       , name = "Region"
                       , guide = guide_legend(override.aes = list(alpha = 0.4)))
  ) %>%
  ggplotGrob %>%
  {.$grobs[[which(sapply(.$grobs, function(x) {x$name}) == "guide-box")]]}


legendColumn <-
  plot_grid(
    # To make space at the top
    vsLeg + theme(legend.position = "none")
    # Plot the legends
    , vsLeg, regionLeg, cylLeg
    # To make space at the bottom
    , vsLeg + theme(legend.position = "none")
    , ncol = 1
    , align = "v")

plot_grid(mainPlot +
            theme(legend.position = "none")
          , legendColumn
          , rel_widths = c(1,.25))

输入图像描述

正如您所看到的,结果与我展示如何执行此操作的第一种方法几乎相同,但现在不使用任何其他美学。我仍然不明白为什么您认为这种区别很重要,但至少现在有另一种方法可以解决问题。我可以利用这种方法的普遍性(例如,当多个情节共享颜色/符号/线型美学并且您想要使用单个图例时),但我认为在此处使用它没有价值。


1
非常感谢您的回复。然而,我想继续使用 fill 而不是 color。原因是如果我们同时使用颜色和填充,则会破坏测试我关于将多个图例应用于填充类型的 ggplot 技能的目的。希望您能理解。我认为我在问题中已经提到了。再次感谢您的努力。 - watchtower
1
@MarkPeterson 我非常尊重地不同意你的观点。你所说的是理论,而OP直接谈论的是将ggplot2作为一种技能进行操作。从理论上讲,你可能是正确的,但并不代表你就能成为ggplot2的巫师。答案可能需要制作多个单层图形,分别保存图例,并使用gridExtra将它们拼凑在一起。 - Tyler Rinker
2
看看我最近的编辑,里面有一个同时使用两种方法的例子。不要局限于一种方法(例如fill)——如果你试图做ggplot没有明确设计的事情,你可能需要把它们拼凑在一起。如果你有某些原因(赌注?学校作业?)不能使用collinetype,那么请告诉我们(也许可以考虑@TylerRinker建议的cowplot包或gridExtra)。 - Mark Peterson
4
你:“我想用钉子把这些木板连接起来,但我的锤子在中途断了。” 我:“你的工具箱里有螺丝和胶水,你可以用那些代替。” 你:“不,我需要用钉子。” 我:“好的,那么这是使用另一种工具来驱动钉子的方式。” 你:“不,我需要用锤子。” 我:“为什么?” 你:“因为。” 我:“这是一个不好的主意,但这是如何使用锤子的手柄来完成的。” 你:“不,我需要以我在问之前所想到的确切方式使用锤子。” 我:“好吧。” - Mark Peterson
1
添加了一种使用cowplot的方法,这样即使您要覆盖其他美学元素以获得自动图例,也不必使用任何其他美学元素。代码复杂得多,需要更多的手动调整,但是:它确实存在。这就是您要找的吗? - Mark Peterson
显示剩余11条评论

8
现在有一个很棒的ggnewscale包,可以简单地实现这一点。

感谢@Drosof指出这个包。ggnewscale::new_scale_fill()让我能够在同一个ggplot对象上使用scale_fill_brewer()和单独的geom_polygonscale_fill_manual()映射,同时仍然得到美丽的图例。这真是救命稻草。 - Vesanen

-1

请提供一个使用这种方法获取合理图例的示例。 - PatrickT
您需要对图例进行偏移,以避免它们重叠,并在不同层中使用不同的颜色比例尺。 - Simon Woodward
听起来很有前途。你能提供一个例子吗? - PatrickT

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