7

I am trying to create a complete set of plots using facet_wrap from the ggplot2 package in R.

As a simplified example I used a subset of the dataset mpg included in ggplot2

library(plyr)
library(ggplot2)
library(gtable)
library(gridExtra)

myData = subset(mpg, manufacturer == "audi" | manufacturer == "chevrolet")
myData = droplevels(myData)

Here is my code to plot the data:

p =  ggplot(myData, aes(x=hwy, y=cty, colour=model) )
p = p + facet_wrap( ~ manufacturer)#, scales="free") # sets panel division 
p = p + geom_point(size = 3) # sets points aspect
p = p + geom_smooth(stat="identity")
print(p)

Now here comes the tricky part... I have another dataframe 'indivParam' with extra information that I would like to display as a table on the plot. Let's say this silly one:

indivParam = ddply(myData, .(manufacturer  ,  model), summarize,
               var1 = unique(class),
               var2 = round(mean(displ)),
               var3 = round(mean(cyl)))

What I am trying to do is to add a sub-table on each panel, with info extracted from indivParam. For example add the following table on the first panel of the plot:

tg = tableGrob(subset(indivParam, manufacturer == "audi"),
           show.rownames=FALSE, gp=gpar(fontsize=8, lwd=2), 
           xmin=15, xmax=30, ymin=10, ymax=20)
grid.newpage()
grid.draw(tg)

I tried several options...

  1. using annotate() but this argument does not pass dataframes...

  2. using annotation_custom() as suggested in this thread : Adding table within the plotting region of a ggplot in r

    p1 = p + annotation_custom(tableGrob(indivParam,
                                show.rownames=FALSE,
                                gp=gpar(fontsize=8, lwd=2)), 
                      xmin=15, xmax=30, ymin=10, ymax=20)
    print(p1)
    

    This does not work either because it displays the entire table on each panel, instead of a sub-table with data related to each panel ()

  3. Finally, after reading the examples on the 'tableGrob' doc page, I tried to create one grid with all the sub-table grobs and simply superimpose it on the plot:

    lg <- lapply(as.character(unique(indivParam$manufacturer)),
         function(x) tableGrob( as.data.frame(dlply(indivParam, .(manufacturer))[x]),
                                name="test",show.rownames=FALSE,
                                gp=gpar(fontsize=8, lwd=2)))
    grid.newpage()
    print(p)
    grid.draw(do.call(arrangeGrob, lg))
    

    But then, the organization does not match the one used by facet.., and I suspect that even if I could put the two tables next to each other, they would be centered and would hide the plots...

Is there any way I could improve this last attempt by choosing the position of the sub-tables? Or is there an even better way to solve this issue? An obvious one would be to use a geom_table() but I don't think this geom exist (yet)...

Any help/hint will be much appreciated!! :-)

Community
  • 1
  • 1
  • 1
    Welcome to SO! This is a great question, really. It is both natural and elaborate; it shows much effort put into it. Good job! – tonytonov Aug 29 '14 at 09:04
  • i agree that `geom_table` would be most natural, but it's unclear whether such a geom would fit well in plot panels (typically tables take a lot of space and would often hide data in other layers). Short of having a smart positioning function (minimise overlap) they're usually best placed manually imho. – baptiste Aug 29 '14 at 11:03
  • Excellent question, well researched and well written, and exactly the same problem as I'm having. Have you by chance found a solution that you'd like to share? – Edward Oct 08 '15 at 09:38
  • Thanks @Edward! Unfortunately, I could not fix this issue and had to move on... Instead of a table, I used a silly geom_text() calling a list of values :-( – Aurelie Calabrese Oct 20 '15 at 18:57

1 Answers1

5

Here is a solution with the amazing package ggpmisc:

library(ggpmisc)
library(dplyr)
library(tibble)

myData <- filter(mpg, manufacturer == "audi" | manufacturer == "chevrolet")

gg <- ggplot(myData, aes(x=hwy, y=cty, colour=model)) + 
  facet_wrap(~ manufacturer) + 
  geom_point(size = 3) +
  geom_smooth(stat="identity")

tb <- myData %>%
  group_by(manufacturer, model) %>%
  summarize(var1 = round(mean(displ)), var2 = round(mean(cyl))) %>%
  ungroup() 

tbs <- lapply(split(tb, tb$manufacturer), "[", -1)
df <- tibble(x = rep(-Inf, length(tbs)), 
             y = rep(Inf, length(tbs)), 
             manufacturer = levels(as.factor(tb$manufacturer)), 
             tbl = tbs)

gg + geom_table(data = df, aes(x = x, y = y, label = tbl),
                hjust = 0, vjust = 1) 

enter image description here

Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225