5

Is there an elegant way to align the tableGrob rows with the axis breaks?

I would like to juxtapose a tableGrob and ggplot chart in R (I need to reproduce some SAS output used in previous versions of a public report). Like this minimal reproducible example:

juxtaposed tableGrob and ggplot chart

This post got me pretty far --- the tableGrob is in the same gtable row as the body of the chart; however, it requires lots of manual fiddling to get the rows in the tableGrob to line up with the axis labels.

I also found this post. Since I'm Sweaving a public report, I would prefer not to use code that isn't readily available in a package on CRAN. That being said, the experimental version of tableGrob appears to accept heights as an argument. If this code will do the trick, and I do choose to use this experimental version, how would I calculate the appropriate row heights?

If there is not an elegant way of doing this, I found these tricks to be helpful:

  • set fontsize AND cex in tableGrob to match ggplot2
  • set padding.v to space table rows in tableGrob
  • modify coordinate limits to accomodate column labels and align with bottom of last row

My MRE code:

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

theme_set(theme_bw(base_size = 8))
df <- head(mtcars,10)
df$cars <- row.names(df)
df$cars <- factor(df$cars, levels=df$cars[order(df$disp, decreasing=TRUE)], ordered=TRUE)
p <- ggplot(data=df, aes(x=hp, y=cars)) +
  geom_point(aes(x=hp, y=cars)) +
  scale_y_discrete(limits=levels(df$cars))+
  theme(axis.title.y = element_blank()) +
  coord_cartesian(ylim=c(0.5,length(df$cars)+1.5))
t <- tableGrob(df[,c("mpg","cyl","disp","cars")],
               cols=c("mpg","cyl","disp","cars"),
               gpar.coretext = gpar(fontsize = 8, lineheight = 1, cex = 0.8),
               gpar.coltext = gpar(fontsize = 8, lineheight = 1, cex = 0.8),
               show.rownames = FALSE,
               show.colnames = TRUE,
               equal.height = TRUE,
               padding.v = unit(1.65, "mm"))
g <- NULL
g <- ggplotGrob(p)
g <- gtable_add_cols(g, unit(2,"in"), 0)
g <- gtable_add_grob(g, t, t=3, b=3, l=1, r=1)

png('./a.png', width = 5, height = 2, units = "in", res = 100)
grid.draw(g)
dev.off()

I have left the car names on the y-axis breaks for troubleshooting purposes, but ultimately I will remove them.

Community
  • 1
  • 1
penguinv22
  • 349
  • 5
  • 12
  • 1
    calculate row heights: figure out how many rows (n), and use `unit(1/n, "npc")` in the cell corresponding to the plot panel from the gtable layout. – baptiste Jan 28 '14 at 01:08
  • @baptiste Thank you. Using your experimental package, I was able to get something working. I was a bit of a hack about it so I'll leave the question open for an answer that is more "proper" than I can offer. Some tips for others: ggplot2::theme() seems clash with experimental tableGrob theme() but I don't really understand themes (or namespaces) so YRMV; I needed to change the theme fonts; also the exp. tableGrob seems to handle the order of factors differently than gridExtra::tableGrob; not sure that justification is implemented; with n = number of breaks no need to pad coordinates on y-axis. – penguinv22 Jan 28 '14 at 05:03

2 Answers2

5

There's now this experimental version of gtable_table

enter image description here

table <- gtable_table(df[,c("mpg","cyl","disp","cars")], 
                      heights = unit(rep(1,nrow(df)), "null"))

g <- ggplotGrob(p)
g <- gtable_add_cols(g, sum(table$widths), 0)
g <- gtable_add_grob(g, table, t=3, b=3, l=1, r=1)

grid.newpage()
grid.draw(g)
baptiste
  • 75,767
  • 19
  • 198
  • 294
  • How would you add column headers? I've tried your example code on github within the table.R file. With `colhead <- gtable_table(t(colnames(d)))`, I get the following error: `Error in mmm < each : comparison of these types is not implemented` – penguinv22 Sep 16 '14 at 19:59
  • you can't use this file with the original gtable, you need to install my forked package for this to work – baptiste Sep 16 '14 at 20:06
  • Thanks for the clarification. I was able to download your fork and install it; however, I noticed that it currently requires R >= 3.2.0. Was this intentional? I would like to include instructions in my source code to use devtools to download and install the package; however, it won't install without manually editing the R version requirement. – penguinv22 Sep 17 '14 at 17:13
  • the reason is a [recent change in grid](https://github.com/wch/r-source/commit/850a82c30e91feb47a0b6385adcbd82988d90413), IIRC ggplot2 won't work with my gtable fork and older R – baptiste Sep 17 '14 at 17:14
2

@Baptiste's answer expanded to demonstrate column labels and cell parameters:

library(ggplot2)
library(gridExtra)
## I manually changed the dependency on 
install.packages(".//gtable_0.2.tar.gz", repos = NULL, type="source")

## The forked version of gtable requires R version 3.2.0 
## which is currently in development (as of 9/17/2014) due to change in grid 
## (https://github.com/wch/r-source/commit/850a82c30e91feb47a0b6385adcbd82988d90413)
## I have not installed the development version.
## However, I was able, in limited testing, to get this to work with R 3.1.0
## and ggplot2_1.0.0
## YRMV
## The following code, commented out, may be more useful with release of R 3.2.0
## library(devtools)
## devtools::install_github("baptiste/gtable")
library(gtable)

theme_set(theme_bw(base_size = 10))

df <- mtcars
df$cars <- row.names(df)
df <- head(df[,c("mpg","cyl","disp","cars")],10)
df$cars <- factor(df$cars, levels=df$cars[order(df$disp, decreasing=TRUE)], ordered=TRUE)

p <- ggplot(data=df, aes(x=disp, y=cars)) +
     geom_point(aes(x=disp, y=cars)) +
     scale_y_discrete(limits=levels(df$cars))+
     theme(axis.title.y = element_blank()) +
     coord_cartesian(ylim = c(0.5,nrow(df)+1))

core <- gtable_table(df[order(df$disp, decreasing=FALSE),],
                   fg.par = list(fontsize=c(8)),
                   bg.par = list(fill=c(rep("lightblue",4),rep("white",4)), alpha=0.5),
                   heights = unit(rep(1,nrow(df)), "null"))

colHead <- gtable_table(t(colnames(df)),
                    fg.par = list(fontsize=c(8)),
                    bg.par = list(fill=c(1), alpha=0.2),
                    heights = unit(0.5, "null"))
table1 <- rbind(colHead, core)

g <- ggplotGrob(p)
g <- gtable_add_cols(g, sum(table1$widths), 0)
g <- gtable_add_grob(g, table1, t=3, b=3, l=1, r=1)

grid.newpage()
grid.draw(g)

enter image description here

penguinv22
  • 349
  • 5
  • 12