【问题标题】:Combining color and linetype legends in ggplot在 ggplot 中结合颜色和线型图例
【发布时间】:2019-11-12 10:20:59
【问题描述】:

在使用 ggplot2 生成的绘图中,我无法将颜色和线型参考线组合成一个图例。线型显示时所有线型都以相同的方式键控,或者根本不显示。

我的绘图包括显示大部分观察结果的丝带,以及显示最小值、中值、最大值的线条,有时还包括一年的观察结果。

使用内置 CO2 数据集的示例代码:

library(tidyverse)

myExample <- CO2 %>%  
      group_by(conc) %>%
      summarise(d.min = min(uptake, na.rm= TRUE),
                d.ten = quantile(uptake,probs = .1, na.rm = TRUE),
                d.median = median(uptake, na.rm = TRUE),
                d.ninty = quantile(uptake, probs = .9, na.rm= TRUE),
                d.max = max(uptake, na.rm = TRUE))
myExample <- cbind(myExample, "Qn1"= filter(CO2, Plant == "Qn1")[,5])

plot_plant <- TRUE  # Switch to plot single observation series

myExample %>%
      ggplot(aes(x=conc))+
      geom_ribbon(aes(ymin=d.ten, ymax= d.ninty, fill = "80% of observations"), alpha = .2)+
      geom_line(aes(y=d.min, colour = "c"), linetype = 3, size = .5)+
      geom_line(aes(y=d.median, colour = "e"),linetype = 2, size = .5)+
      geom_line(aes(y=d.max, colour = "a"),linetype = 3, size = .5)+
      {if(plot_plant)geom_line(aes(y=Qn1, color = "f"), linetype = 1,size =.5)}+
      scale_fill_manual("Statistic", values = "blue")+
      scale_color_brewer(palette = "Dark2",name = "",
                         labels = c(
                               a= "Maximum",
                               e= "Median",
                               c= "Minimum",
                               f = current_year
                         ), breaks = c("a","e","c","f"))+
      scale_linetype_manual(name = "")+
      guides(fill= guide_legend(order = 1), color = guide_legend(order = 2), linetype = guide_legend(order = 2))

plot_plant 设置为 TRUE 时,代码会绘制单个观测序列,但线型根本不会出现在图例中:

plot_plant 设置为 FALSE 时,线型会显示在图例中,但我看不出虚线图例条目之间的区别:

情节按预期工作,但我希望线型区别显示在图例中。在视觉上,当我绘制单个观察系列时,它更重要,因为实线和虚线或点线之间的区别更强。

在寻找答案时,我看到了将不同的统计数据(最小值、中值、最大值和单个系列)组合成一个变量并让 ggplot 确定线型的建议(例如 [this post]ggplot2 manually specifying color & linetype - duplicate legend)或制作一个描述线型的散列 [例如]How to rename a (combined) legend in ggplot2?,但这些方法似乎都不能很好地与带状图结合使用。

我尝试将我的数据格式化为长格式,这通常适用于 ggplot。如果我将所有统计数据绘制为线几何图形,但无法使功能区按我想要的方式工作,并且覆盖单个观察系列似乎需要将其存储在不同的数据表中,这将起作用。

【问题讨论】:

    标签: r ggplot2


    【解决方案1】:

    如您所述,ggplot 喜欢长格式数据。所以我建议坚持下去。

    这里我生成了一些虚构的数据:

    library(tibble)
    library(dplyr)
    library(ggplot2)
    library(tidyr)
    
    set.seed(42)
    
    tibble(x = rep(1:10, each = 10), 
           y = unlist(lapply(1:10, function(x) rnorm(10, x)))) -> tbl_long
    

    看起来像这样:

    # A tibble: 100 x 2
           x     y
       <int> <dbl>
     1     1 2.37 
     2     1 0.435
     3     1 1.36 
     4     1 1.63 
     5     1 1.40 
     6     1 0.894
     7     1 2.51 
     8     1 0.905
     9     1 3.02 
    10     1 0.937
    # ... with 90 more rows
    

    然后我group_by(x) 并计算每组中 y 的感兴趣分位数:

    tbl_long %>% 
      group_by(x) %>% 
      mutate(q_0.0 = quantile(y, probs = 0.0), 
             q_0.1 = quantile(y, probs = 0.1),
             q_0.5 = quantile(y, probs = 0.5), 
             q_0.9 = quantile(y, probs = 0.9), 
             q_1.0 = quantile(y, probs = 1.0)) -> tbl_long_and_wide
    

    看起来像:

    # A tibble: 100 x 7
    # Groups:   x [10]
           x     y q_0.0 q_0.1 q_0.5 q_0.9 q_1.0
       <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
     1     1 2.37  0.435 0.848  1.38  2.56  3.02
     2     1 0.435 0.435 0.848  1.38  2.56  3.02
     3     1 1.36  0.435 0.848  1.38  2.56  3.02
     4     1 1.63  0.435 0.848  1.38  2.56  3.02
     5     1 1.40  0.435 0.848  1.38  2.56  3.02
     6     1 0.894 0.435 0.848  1.38  2.56  3.02
     7     1 2.51  0.435 0.848  1.38  2.56  3.02
     8     1 0.905 0.435 0.848  1.38  2.56  3.02
     9     1 3.02  0.435 0.848  1.38  2.56  3.02
    10     1 0.937 0.435 0.848  1.38  2.56  3.02
    # ... with 90 more rows
    

    然后我将除 x、y 以及 10 和 90 百分位变量之外的所有列收集到两个变量中:键和值。新的键变量采用每个值的来源旧变量的名称。其他变量只是根据需要复制下来。

    tbl_long_and_wide %>% 
      gather(key, value, -x, -y, -q_0.1, -q_0.9) -> tbl_super_long
    

    看起来像:

    # A tibble: 300 x 6
    # Groups:   x [10]
           x     y q_0.1 q_0.9 key   value
       <int> <dbl> <dbl> <dbl> <chr> <dbl>
     1     1 2.37  0.848  2.56 q_0.0 0.435
     2     1 0.435 0.848  2.56 q_0.0 0.435
     3     1 1.36  0.848  2.56 q_0.0 0.435
     4     1 1.63  0.848  2.56 q_0.0 0.435
     5     1 1.40  0.848  2.56 q_0.0 0.435
     6     1 0.894 0.848  2.56 q_0.0 0.435
     7     1 2.51  0.848  2.56 q_0.0 0.435
     8     1 0.905 0.848  2.56 q_0.0 0.435
     9     1 3.02  0.848  2.56 q_0.0 0.435
    10     1 0.937 0.848  2.56 q_0.0 0.435
    # ... with 290 more rows
    

    这种格式将允许您同时使用geom_ribbon()geom_smooth(),因为这些行的变量包含在value 中并按key 分组,而要映射到@987654335 的变量@ 和 ymaxvalue 是分开的,并且在每个 x 组中都是相同的。

    tbl_super_long %>% 
      ggplot() + 
      geom_ribbon(aes(x = x, 
                      ymin = q_0.1, 
                      ymax = q_0.9, 
                      fill = "80% of observations"), 
                  alpha = 0.2) + 
      geom_line(aes(x = x, 
                    y = value, 
                    color = key, 
                    linetype = key)) + 
      scale_fill_manual(name = element_text("Statistic"), 
                        guide = guide_legend(order = 1), 
                        values = viridisLite::viridis(1)) + 
      scale_color_manual(name = element_blank(), 
                         labels = c("Minimum", "Median", "Maximum"), 
                         guide = guide_legend(reverse = TRUE, order = 2), 
                         values = viridisLite::viridis(3)) + 
      scale_linetype_manual(name = element_blank(), 
                            labels = c("Minimum", "Median", "Maximum"), 
                            guide = guide_legend(reverse = TRUE, order = 2), 
                            values = c("dotted", "dashed", "solid")) + 
      labs(x = "x", y = "y")
    

    这种包含长但分组的 x 和 y 变量以及独立但重复的 ymin 和 xmin 变量的数据格式将允许您同时使用 geom_ribbon()geom_smooth() 并允许 linetypes 在传说。

    【讨论】:

    • 有没有办法将单个观察系列也纳入其中并显示在图例中?在我的真实数据中,我正在拉时间序列,如果仪表仍然处于活动状态,我想绘制当前年份。使用这种方法,就像在创建数据时添加 z = rep(1:10, times = 10),然后在调用 ggplot 时添加 +geom_line(data = filter(tbl_super_long, z == 10), aes( x = x, y = y), color = "purple")
    • 你想用这个额外的系列还是不用这个系列来计算分位数?
    • 我用系列计算分位数,但无论哪种方式都可以。我的实际数据是来自各种不同站点的每日流量,如果站点仍然处于活动状态,我想播放当年的观察结果。我希望当前年份出现在图例中。我想在 ggplot 之外测试今年的数据,并附加到底部,所以我的键列会有 3 个或 4 个值,但是当我分配特定的线型值和标签时,这会导致问题,因为分配的类型的数量必须匹配不同类型的数量。
    • 是的,您可以按照您的建议将该数据附加到底部。然后,您需要让 ggplot 为您决定比例值,或者在检查数据中有多少级别后编写代码以生成正确数量的值。
    猜你喜欢
    • 1970-01-01
    • 2022-11-22
    • 1970-01-01
    • 1970-01-01
    • 2012-07-05
    • 2021-01-31
    • 1970-01-01
    • 2013-09-02
    • 2015-07-04
    相关资源
    最近更新 更多