简体   繁体   English

override.aes与list参数的命名向量

[英]override.aes with named vectors for list argument

As far as I can see, when using override.aes , one needs to "hard code" values in the list argument depending on the order of the legend... Is it possible to used named vectors instead? 据我所知,当使用override.aes ,需要根据图例的顺序在list参数中“硬编码”值...是否可以使用命名向量?

Example: 例:

library(ggplot2)
set.seed(1)
df1 <- data.frame(x = 1:25, y_line = rnorm(25, 1, 0.01))

p <- ggplot(df1, aes(x, y_line)) +
      geom_line(aes(colour = 'line')) +
      geom_point(aes(y = 1, colour = 'point')) + 
      geom_line(aes(y = 1, color = 'point'), linetype = 'dotted') 

# This specifies values by order:
p + guides(colour = guide_legend(override.aes = list(linetype = c('dotted', 'solid'), shape = c(NA, 16))))

# Attempt to use named vectors does not change the output 
p + guides(colour = guide_legend(override.aes = list(linetype = c(point = 'dotted', line = 'solid'), shape = c(NA, 16))))

Both result in the same legend: 两者都产生相同的图例:

This is the same when using quoted names. 使用带引号的名称时也是如此。

Created on 2019-05-07 by the reprex package (v0.2.1) reprex包创建于2019-05-07(v0.2.1)

I'm sorry but I think I may have missed exactly for what purpose you would want to use this, so I apologize if my workaround doesn't adress your need directly. 对不起,但我想我可能完全错过了你想要使用它的目的,所以如果我的解决方法没有直接解决你的需要,我很抱歉。

I'm unsure wether it can be done within the guides function, but what you can do, is to use guide_legend() in conjunction with scale_colour_* to set the order in advance with breaks . 我不确定它是否可以在guides功能中完成,但你可以做的是使用guide_legend()scale_colour_*来提前设置breaks顺序。 I think this mimicks your intended behaviour: 我认为这模仿了你的预期行为:

p <- ggplot(df1, aes(x, y_line)) +
  geom_line(aes(colour = 'line')) +
  geom_point(aes(y = 1, colour = 'point')) +
  geom_line(aes(y = 1, colour = 'point'), linetype = 'dotted') +
  scale_colour_discrete(
    breaks = c("point", "line"),
    guide = guide_legend(override.aes = list(linetype = c("dotted", "solid"),
                                             shape = c(16, NA)))
    )

在此输入图像描述

Note: I haven't tested this out thoroughly, & there will probably be unforeseen hiccups down the line when the hack interacts with other unobserved functions within ggplot2. 注意:我没有彻底测试过这一点,当黑客与ggplot2中的其他未观察到的函数交互时,可能会出现无法预料的打嗝。 The package's inner workings can be pretty hard to divine, but hopefully this provides a starting point... 包装的内部工作可能非常难以理解,但希望这提供了一个起点......

The legend building part occurs within ggplot2:::build_guides (un-exported function). 图例构建部分出现在ggplot2:::build_guides (未导出的函数)中。 As you've observed, the names of a named vector in override.aes are ignored during the process. 正如您所观察到的,在此过程中将override.aes中命名向量的名称。 One possible workaround is to insert some code into the function to get the named vector in the correct (based on legend labels) order. 一种可能的解决方法是在函数中插入一些代码,以便以正确的(基于图例标签)顺序获取命名向量。 I've also added a check for the default aesthetic parameters, for cases where we may want to override the aesthetics for only one or two labels, & leave the rest as default. 我还添加了一个默认美学参数的检查,以便我们可能只想覆盖一个或两个标签的美学,并将其余部分保留为默认值。

Here's the code to be inserted. 这是要插入的代码。 I've only tried it on linetype, shape, & size. 我只尝试过线型,形状和尺寸。 Offhand, linetype is the only case that comes to my mind with both numerical & categorical values, so that's the specific scenario covered below for default.aes . 另外,linetype是我脑海中唯一一个带有数值和分类值的情况,因此这是default.aes下面介绍的具体方案。

# define a function that completes each element in the override.aes list if
# it's a named vector, by arranging it in the order used by the legend labels,
# & replacing any unsupplied value with the latest (based on most recent layer) 
# default aesthetic value for that specific element
complete.override.aes <- function(gdef, default.aes){
  override.aes <- gdef$override.aes
  if(!any(sapply(override.aes, function(x) !is.null(names(x))))){
    return(gdef)
  }
  key.label <- gdef$key$.label
  for(i in seq_along(override.aes)){
    if(!is.null(names(override.aes[[i]]))){
      x <- override.aes[[i]][key.label]
      default.x <- default.aes[[names(override.aes)[[i]]]]
      if(!is.na(default.x)){
        x <- dplyr::coalesce(x,
                             rep(default.x,
                                 times = length(key.label)))
      }
      names(x) <- NULL
      override.aes[[i]] <- x
    }
  }
  gdef$override.aes <- override.aes
  gdef
}

# extract default aes associated with each layer in ggplot object,
# combine, & remove duplicates (keep latest where applicable)
default.aes <- sapply(layers, function(x) x$geom$default_aes)
default.aes <- purrr::flatten(default.aes)
default.aes <- default.aes[!duplicated(default.aes, fromLast = TRUE)]
# for linetype (if applicable), map from numeric to string
if(!is.null(default.aes[["linetype"]]) &
   is.numeric(default.aes[["linetype"]])){
  if(default.aes[["linetype"]] == 0) default.aes[["linetype"]] <- 7
  default.aes[["linetype"]] <- c("solid", "dashed", "dotted",
                                 "dotdash", "longdash", "twodash",
                                 "blank")[default.aes[["linetype"]]]
}

gdefs <- lapply(gdefs, complete.override.aes, default.aes)

To use this, run trace(ggplot2:::build_guides, edit = TRUE) and insert the above code after line 32 (ie after return(zeroGrob()) & before gdefs <- guides_merge(gdefs) ). 要使用它,运行trace(ggplot2:::build_guides, edit = TRUE)并在第32行之后插入上面的代码(即在return(zeroGrob())gdefs <- guides_merge(gdefs) )之前。

(Alternatively, we can insert the code into our own version of the above function & name it build_guides2 , define a modified version of ggplot2:::ggplot_gtable.ggplot_built which calls on that instead of ggplot2:::build_guides , then a modified version of ggplot2:::print.ggplot which calls on that instead of ggplot_gtable . However, this gets unwieldy quickly, takes up a considerable amount of space, & is tangential to the topic at hand, so I'm not going into details for that here.) (或者,我们可以将代码插入到我们自己的上述函数版本中,并将其命名为build_guides2 ,定义ggplot2:::ggplot_gtable.ggplot_built的修改版本,调用它而不是ggplot2:::build_guides ,然后修改后的版本ggplot2:::print.ggplot调用而不是ggplot_gtable 。然而,这很快变得笨拙,占用了相当大的空间,并且与手头的主题相关,所以我不打算详细说明。)

Result: 结果:

# correct mapping for linetype
p + guides(colour = guide_legend(
             override.aes = list(linetype = c(point = 'dotted', line = 'solid'), 
                                 shape = c(NA, 16))))

情节

# both linetype & shape use named vectors, & specify one value each
# (otherwise linetype defaults to "solid" & shape to 19)
p + guides(colour = guide_legend(
             override.aes = list(linetype = c(point = 'dotted'), 
                                 shape = c(line = 8))))

情节2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM