1

I am creating a custom geom which displays a little plot-in-plot, a small mini barplot that can be displayed somewhere on the plot. You will see an example below (don't ask me why I do it, I need it Because Of Reasons).

My questions:

  • How does ggplot2 choose the colors of the bar? I don't see how they were defined. They magically appear when coord$transform is called. Why are colors for the same cylinder number on different parts of the plot different?
  • How can I make the colors matching between the mini barplots?
  • How can I add a legend describing the bars? I assume that would have to be called from a setup function of the geom or an associated stat, because normally each group gets only one key (and here we need several key per group).

Example: using the mpg data set, we plot mean displacement over time. At each year, we put a small bar plot showing relative proportions of cars with a given number of cylinders (still working on the labels, though).

# library(ggplot2)
# library(grid)
## per year summary – mean displacement 
point.data <- mpg %>% group_by(year) %>% summarise(mean_displ=mean(displ))

## per year, number of models with the  given number of cylinders
bar.data <- mpg %>% group_by(year) %>% count(cyl) %>% left_join(point.data)
## position of the mini barplot
bar.data$x <- bar.data$year
bar.data$y <- bar.data$mean_displ + .15

I use my custom geom to show the bar plots (code for the geom is below)

ggplot(point.data, aes(x=year, y=mean_displ)) + 
  geom_point(size=8, col="lightblue") + 
  geom_line(size=3, col="lightblue") + 
  geom_bar_widget(data=bar.data, 
     aes(x=x, y=y, width=.2, height=.15, 
         group=year, value=n, fill=factor(n))) + 
  xlim(1995, 2010) + ylim(3, 4)

Result:

enter image description here

And here is the code for the geom:

## calculate the mini barplot
.bar_widget_bars <- function(x, y, w, h, v, fill) {

     nv <- length(v)
     v <- h * v / max(v)  # scale the values

     dx <- w / nv         # width of a bar

     # xx and yy are the mid positions of the rectangles
     xx <- x - w/2 + seq(dx/2, w - dx/2, length.out=nv)
     yy <- y - h/2 + v/2

     ## widths and heights of the rectangles
     ww <- rep(dx, nv)
     hh <- v

     list(rectGrob(xx, yy, ww, hh, gp=gpar(fill=fill)))
}


## draw a mini barplot
.bar_widget_draw_group <- function(data, panel_params, coord, wgdata) {

     ct <- coord$transform(data, panel_params)

     grobs <- .bar_widget_bars(x=ct$x[1], y=ct$y[1], 
                               w=ct$width[1], h=ct$height[1], 
                               v=data$value, fill=ct$fill)

     class(grobs) <- "gList"
     gTree("bar_widget_grob", children=grobs)
}

## the widget for the mini barplot
GeomBarWidget <- ggproto("GeomPieWidget",
  Geom,
  required_aes=c("x", "y", "width", "height", "group", "value"),
  default_aes=aes(shape=19, colour="black", fill=NULL, labels=NULL),
  draw_key=draw_key_blank(),
  draw_group=.bar_widget_draw_group,
  extra_params=c("na.rm")
)

geom_bar_widget <- function(mapping = NULL, data = NULL, 
                              stat = "identity",
                              position = "identity", na.rm = FALSE,
                              show.legend = FALSE,
                              inherit.aes = TRUE, ...) {
  layer(
    geom = GeomBarWidget, mapping = mapping,  
    data = data, stat = stat,
    position = position, show.legend = show.legend, 
    inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, ...)
  )
}
January
  • 16,320
  • 6
  • 52
  • 74
  • 1
    it's not as simple as `scale_fill_manual`? That changes the values of the colors for me. – Nova Jul 09 '19 at 12:56
  • Ah damned, yea, I was trying with `scale_color_*` all that time (sound of me slapping my forehead). You are right. However, this does not answer the remaining questions. – January Jul 09 '19 at 13:06
  • I suspect it's because you have a piece of code that says `show.legend = FALSE`? I tried to make it `TRUE` but I get an error... – Nova Jul 09 '19 at 13:24
  • Exactly - you get an error. You can't get a legend like this. Essentially, you need to use `draw_key_...`, but this is called with one line per each group (there are two groups here) to make one key per group. This is why I need to use `draw_key_blank()`. I am stuck here. Also, why don't the colors match? How are they assigned? By what function? – January Jul 09 '19 at 13:33
  • The colors likely don't match because the factor levels are not the same. See https://stackoverflow.com/questions/6919025/how-to-assign-colors-to-categorical-variables-in-ggplot2-that-have-stable-mappin. I can't help you with your grid code, I wish I could! – Nova Jul 09 '19 at 13:37
  • They should be – I am turning it to a factor once for the whole data set, and if I use geom_bar instead of my widgets, it looks OK. – January Jul 09 '19 at 14:21
  • Would you consider using `geom_rect` or `geom_tile` instead of your widget? Then you might be able to predict the behavior of the fill a little easier. – Nova Jul 09 '19 at 15:34
  • No, unfortunately my widgets are the whole point of the exercise. Some of them are more complicated than a mini bar plot. – January Jul 10 '19 at 04:50

1 Answers1

1

OK, so the problem was not ggplot, the problem was the idiot who wrote the code.

In the code above I wrote fill=factor(n), but n is actually the value to show on the bar plot. Instead, I should have used fill=factor(cyl), which works exactly as expected.

January
  • 16,320
  • 6
  • 52
  • 74
  • 1
    Hey, glad you figured it out! Pays to leave it for a while and come back sometimes I guess... that's frustrating but happy you found a solution. – Nova Jul 10 '19 at 13:22