【问题标题】:How to fix an error when adding a manual scale in ggplot?在ggplot中添加手动比例时如何修复错误?
【发布时间】:2019-02-11 22:47:38
【问题描述】:

当我添加手动比例时,如下例所示。我收到以下错误消息:

“警告信息: 在 if (self$guide == "none") return() 中: 条件的长度 > 1 并且只使用第一个元素"

是什么导致了这个错误信息,我该如何解决?

非常感谢您的帮助!

d <- data.frame(week = seq(1, 52, 1), 
            revenue = round(runif(52, 0, 100)), 0)

p <- ggplot(data = d, aes(x = week, y = revenue, fill = 'lightskyblue')) + 
  geom_bar(stat = 'identity', colour = 'black') 


p <- p + scale_fill_identity(name = NULL,
                             guide = guide_legend(label.position = 'top'),
                             labels = c('2019'))

p

【问题讨论】:

  • 看起来像一个错误,我找不到你的(精心设计的)mwe 有什么问题。我建议您将其报告为issue。 (源中有两个(self$guide == "none")实例,一个在ScaleDiscreteIdentity,一个在ScaleContinuousIdentity,我的猜测是你触发了第二个但两者都容易受到影响。)
  • 感谢您的建议。我将其发布为问题 (#3127)。

标签: r ggplot2


【解决方案1】:

简答

支持独立的 MCVE,但我想强调的是,您收到了 警告 消息,而不是错误。在这种特殊情况下,您无需修复任何内容。您可以忽略该消息,因为代码可以正常工作。

更长的解释

正如@r2evans 所说,这是由于scale_*_identity 代码[found here]。如?scale_fill_identity 中所述,默认情况下这些比例不会生成图例,但您可以通过将guide = "none" 参数替换为其他类型的指南来覆盖它。

它应该如何工作:

此图例创建行为在train 函数中为ScaleDiscreteIdentity / ScaleContinuousIdentity 编码,该函数检查引导参数的值是否为“none”。如果是这样,请不要创建图例。否则,使用ScaleDiscrete / ScaleContinuous 中的train 函数来创建适当的图例:

ScaleDiscreteIdentity <- ggproto("ScaleDiscreteIdentity", ScaleDiscrete,
  ...,
  train = function(self, x) {
    # do nothing if no guide, otherwise train so we know what breaks to use
    if (self$guide == "none") return()
    ggproto_parent(ScaleDiscrete, self)$train(x)
  }
)

ScaleContinuousIdentity <- ggproto("ScaleContinuousIdentity", ScaleContinuous,
  ...,
  train = function(self, x) {
    # do nothing if no guide, otherwise train so we know what breaks to use
    if (self$guide == "none") return()
    ggproto_parent(ScaleContinuous, self)$train(x)
  }
)

?scale_fill_identity 中的示例表明可以使用guide = "legend" 代替guide = "none"。在实践中,guide = guide_legend(...) 也可以使用。事实上,?guides 表示:

scale_* 中的guide = "legend"guide = guide_legend() 的语法糖

实际工作原理(至少基于我当前的 3.1.0 版本):

对于scale_*_identityguide = "legend"guide = guide_legend(...) 之间存在细微差别。我已经削减了 MCVE 来说明这一点:

p <- ggplot(data = d, 
            aes(x = week, y = revenue, fill = 'lightskyblue')) + 
  geom_col()

p1 <- p + scale_fill_identity(guide = guide_legend())
p2 <- p + scale_fill_identity(guide = "legend")
p3 <- p + scale_fill_identity(guide = "none")

p1 和 p2 产生相同的图例,但 p1 触发警告,而 p2 不会。 (当然p3使用默认选项,不会触发警告。)造成这种差异的原因在于它们的填充比例,可以在p&lt;1/2/3&gt;$scales$scales[[1]]找到:

> p1$scales$scales[[1]]$guide
$`title`
list()
attr(,"class")
[1] "waiver"

$title.position
NULL

$title.theme
NULL

... #omitted for space. it goes on for a while

> p2$scales$scales[[1]]$guide
[1] "legend"

> p3$scales$scales[[1]]$guide
[1] "none"

p1$scales$scales[[1]]$guide 有 21 项,而 p2$scales$scales[[1]]$guidep3$scales$scales[[1]]$guide 各包含一个字符串。因此,当train 函数检查self$guide == "none" 时,p1 返回一个 TRUE / FALSE 值列表,每个项目一个,而 p2 和 p3 返回一个 TRUE / FALSE。

> p1$scales$scales[[1]]$guide == "none"
         title title.position    title.theme    title.hjust    title.vjust          label 
         FALSE          FALSE          FALSE          FALSE          FALSE          FALSE 
label.position    label.theme    label.hjust    label.vjust       keywidth      keyheight 
         FALSE          FALSE          FALSE          FALSE          FALSE          FALSE 
     direction   override.aes           nrow           ncol          byrow        reverse 
         FALSE          FALSE          FALSE          FALSE          FALSE          FALSE 
         order  available_aes           name 
         FALSE          FALSE          FALSE 
> p2$scales$scales[[1]]$guide == "none"
[1] FALSE
> p3$scales$scales[[1]]$guide == "none"
[1] TRUE

由于train 函数只期望从其self$guide == "none" 检查返回单个值,因此p1 会触发条件长度> 1 的警告消息。

有没有关系

不是真的。当面对来自 p1 的 21 个 TRUE / FALSE 值列表时,train 函数只查看第一个 --- 这是 FALSE,因为 p1$scales$scales[[1]]$guide$title 的默认值是 waiver()。所以 p1 的 guide = guide_legend() 与 p2 的 guide = "legend" 的签出方式相同。您可以忽略警告并继续。

...只是不要在guide_legend 中将您的图例标题命名为“none”:

p4 <- p + scale_fill_identity(guide = guide_legend(title = "none"))
# this will cause the legend to disappear, because the first value in
# p4$scales$scales[[1]]$guide == "none" will actually be TRUE

还是应该解决这个问题

是的,虽然我想说这不是一个严重的错误,因为代码通常按预期运行(上面的 p4 是一个边缘案例)。

我想修复应该很简单,例如:

# this
if (all(self$guide == "none")) return() 

# instead of this
if (self$guide == "none") return()

【讨论】:

  • 感谢您深入研究代码库并弄清楚这一点。你是对的,它应该被修复。同时,我想提一下,代码示例是倒退的。手动缩放应该使用scale_*_manual(),而不是scale_*_identity()(见我的回答)。
  • @ClausWilke 我完全同意。这是说明技术部分的答案。我试图考虑合法的用例,其中一个人可能想要一个带有scale_*_identity 的图例......我设法想出的只是颜色/形状/线型/等值的自我演示。 :)
【解决方案2】:

answer by Z.lin 很好地找出了这里出了什么问题。这确实是ggplot2中的一个错误。但是,我想借此机会指出您的代码是倒退的。您通常不应该使用带有scale_*_identity() 的指南,因为身份标度采用数据值并按原样显示它们(即,没有需要在指南中显示的映射)。因此,这些音阶并不意味着有传说。

如果您想手动为元素着色,那么在美学映射中您应该使用所表示的数据值(此处为fill = '2019'),然后使用scale_fill_manual() 将该数据值映射到颜色。注意在这种情况下没有警告,因为scale_*_manual() 已经使用不同的指南代码变体进行了广泛的测试,而scale_*_identity() 没有。

library(ggplot2)

d <- data.frame(week = seq(1, 52, 1), 
                revenue = round(runif(52, 0, 100)), 0)

ggplot(data = d, aes(x = week, y = revenue, fill = '2019')) + 
  geom_bar(stat = 'identity', colour = 'black') +
  scale_fill_manual(
    values = c(`2019` = 'lightskyblue'),
    name = NULL,
    guide = guide_legend(label.position = 'top')
  )

reprex package (v0.2.1) 于 2019 年 2 月 11 日创建

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多