Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Plotting aes(fill=`integer column`) or aes(colour=`integer column`) fail with unrelated error message #785

Closed
chbrown opened this Issue · 2 comments

3 participants

@chbrown

To borrow an example from stackoverflow:

chicks <- data.frame(length = rnorm(100, 4, 2), animal = 2)
carrots <- data.frame(length = rnorm(500, 9, 3), animal = 1)

measured_things <- rbind(chicks, carrots)

ggplot(measured_things, aes(x=length, fill=animal)) + geom_bar()

Which produces the flabbergasting error message:

Error in unit(tic_pos.c, "mm") : 'x' and 'units' must have length > 0

measured_things$status = c('animal', 'not')[measured_things$animal]
# or measured_things$status = as.factor(measured_things$animal)

ggplot(measured_things, aes(x=length, fill=status)) + geom_bar()

Which works beautifully! Seems like aes(fill=...) and aes(colour=...) could very well force conversion to factor, but there might be other use-cases I'm not aware of, but at least the error message could be more helpful.

@BrianDiggs

That is a bad error message, but the problem is not what you think it is. First, animal is numeric, not integer (there is a distinction in R); you get the same error if you set animal=2.5 in chicks.

In general, fill and colour can be mapped to continuous variables, so always forcing to a factor (that is discrete) variable is not a good idea. It might be inside a geom_bar()/stat_bin(), but I'm not sure of the implications of that.

You already found one work-around (paraphrased):

ggplot(measured_things, aes(x=length, fill=factor(animal))) + geom_bar()

Here is another one

ggplot(measured_things, aes(x=length, group=animal, fill=animal)) + geom_bar()

Problem (conceptually)

Mapping a continuous variable to an aesthetic after it has been processed through a stat is not well defined. Each piece of data in the stat results is an aggregation of information from the original data, and it is not clear how to make an aggregated fill or colour aesthetic.

A discrete aesthetic is not a problem; the behavior there is to perform the aggregation on each subset of the discrete variable(s) separately. Then this constant (within a subset) aesthetic can be applied to all points of the aggregation (of that subset). That is why explicitly setting group=animal works; it defines the subsets. By default, group is the interaction of all discrete variables, but in the example there are no discrete variables, so there is only one group.

The error happens because the code does not detect this ill-defined condition and passes data down to ever-lower functions until something can't handle what it is given and the error is thrown then.

Problem (technically)

Splitting up the plotting steps

p <- ggplot(measured_things, aes(x=length, fill=animal)) + geom_bar()
pb <- ggplot_build(p)
pg <- ggplot_gtable(pb)

The error is thrown by

  label_pos <- unit(tic_pos.c, "mm")

in guide_gengrob.colorbar in guide-colorbar.r. tic_pos.c in the call is numeric(0). This is because

> guide$key
[1] fill   .label .value
<0 rows> (or 0-length row.names)

Which happens because the the scale is "empty", so scale_breaks returns numeric(0) in guide_train.colorbar in guide_colorbar.r. That happens because there is no fill in the data and so scale$range$range is NULL.

Which leads to another "workaround":

ggplot(measured_things, aes(x=length, fill=animal)) + stat_bin() +
  scale_fill_continuous(limits=c(1, 2))

which shows that fill isn't doing anything. Note that legend guides get this right:

ggplot(measured_things, aes(x=length, fill=animal)) + stat_bin() +
  scale_fill_continuous(guide=guide_legend())

gives a plot without any legend (or any effect of fill).

I think this is because of

  if (empty(key) || all(is.na(breaks))) return(NULL)

in guide_train.legend in guide-legend.r. Something similar should be in guide_train.colorbar.

I'm going to let someone else who is more familiar with the inner workings of guides to implement this so that it doesn't break anything else.

@hadley
Owner

This sounds like a great feature/horrible bug, but unfortunately we don't currently have the development bandwidth to support it/fix it. If you'd like to submit a pull request that implements this feature/fixes this bug, please follow the instructions in the development vignette.

@hadley hadley closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.