Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix an error when adding a manual scale in ggplot?

Tags:

r

ggplot2

When I add a manual scale like in the example below. I get the following error message:

"Warning message: In if (self$guide == "none") return() : the condition has length > 1 and only the first element will be used"

What is causing this error message and how do I solve it?

Your help is much appreciated!

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
like image 375
Savas Ali Avatar asked Feb 07 '19 18:02

Savas Ali


Video Answer


1 Answers

Short answer

Upvoted for a self-contained MCVE, though I'd like to emphasize that you got a warning message, not an error. In this particular case, you don't need to fix anything. You can ignore the message, as the code works perfectly fine as intended.

Longer explanation

As @r2evans noted, this is due to the scale_*_identity code [found here]. As described in ?scale_fill_identity, these scales do not produce a legend by default, though you can override that by substituting the guide = "none" argument with other types of guides.

How it's supposed to work:

This legend-creation behaviour is coded in the train function for ScaleDiscreteIdentity / ScaleContinuousIdentity, which checks to see whether the guide parameter's value is "none". If so, create no legend. Otherwise, use the train function from ScaleDiscrete / ScaleContinuous to create the appropriate legend:

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

The examples in ?scale_fill_identity indicate that guide = "legend" can be used instead of guide = "none". In practice, guide = guide_legend(...) can also be used. In fact, ?guides states:

guide = "legend" in scale_* is syntactic sugar for guide = guide_legend()

How it actually works (at least based on my current version 3.1.0):

For scale_*_identity, there is a subtle difference between guide = "legend" and guide = guide_legend(...). I've trimmed down the MCVE to illustrate the point:

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

plot

Both p1 and p2 results in the same legend, yet p1 triggers the warning, while p2 does not. (p3, of course, uses the default option and triggers no warning.) The reason for this difference lies in their fill scales, which can be found at p<1/2/3>$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 has 21 items, while p2$scales$scales[[1]]$guide and p3$scales$scales[[1]]$guide each comprises of a single character string. As such, when the train function checks for self$guide == "none", p1 returns a list of TRUE / FALSE values, one for each item, while p2 and p3 returns a single 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

Since the train function only expects a single value to be returned from its self$guide == "none" check, p1 triggers the warning message for condition length > 1.

Does it matter:

Not really. When faced with a list of 21 TRUE / FALSE values from p1, the train function only looks at the first one---which is FALSE because p1$scales$scales[[1]]$guide$title's default value is waiver(). So p1's guide = guide_legend() checks out the same way as p2's guide = "legend". You can ignore the warning and carry on.

... Just don't name your legend title "none" within guide_legend:

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

Should it be addressed anyway:

Yes, though I'd say it's not a critical bug, as the code generally behaves as intended (p4 above is a fringe case).

I imagine the fix should be pretty simple anyway, e.g.:

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

# instead of this
if (self$guide == "none") return()
like image 85
Z.Lin Avatar answered Oct 06 '22 01:10

Z.Lin