Juxtapose tableGrob with ggplot2 y-axis
Asked Answered
A

2

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.

Alberthaalberti answered 27/1, 2014 at 21:56 Comment(2)
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.Idomeneus
@Idomeneus 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.Alberthaalberti
I
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)
Idomeneus answered 21/6, 2014 at 19:57 Comment(4)
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 implementedAlberthaalberti
you can't use this file with the original gtable, you need to install my forked package for this to workIdomeneus
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.Alberthaalberti
the reason is a recent change in grid, IIRC ggplot2 won't work with my gtable fork and older RIdomeneus
A
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

Alberthaalberti answered 17/9, 2014 at 18:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.