Adding sub-tables on each panel of a facet ggplot in r
Asked Answered
L

1

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!! :-)

Lithia answered 28/8, 2014 at 17:21 Comment(4)
Welcome to SO! This is a great question, really. It is both natural and elaborate; it shows much effort put into it. Good job!Culp
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.Lati
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?Hedda
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 :-(Lithia
V
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

Valdis answered 19/10, 2019 at 14:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.