I'm new to grobbing and I am trying to create a simple grid.arrange object but cannot figure out how to might a compact/tight layout.
Below is a simple example of what I'm trying to run and the grob that I get.
library(grid)
library(gridExtra)
name = textGrob("My Name", gp=gpar(fontsize = 20, fontface = "bold"))
name2 = textGrob("Second Name", gp=gpar(fontsize = 16))
tbl = tableGrob(head(iris))
grid.arrange(name, name2, tbl)
UPDATE:
Using the answer found here I was able to get the text compact but I am still struggling to get the table to be right under the text.
library(grid)
library(gridExtra)
name = textGrob("My Name", gp=gpar(fontsize = 20, fontface = "bold"))
name2 = textGrob("Second Name", gp=gpar(fontsize = 16))
tbl = tableGrob(head(iris))
margin = unit(0.5, "line")
grid.newpage()
grid.arrange(name, name2, tbl,
heights = unit.c(grobHeight(name) + 1.2*margin,
grobHeight(name2) + margin,
unit(1,"null")))
Typically you'd use the top= argument for a single grob. With two grobs like this, it might be easiest to combine them in a table; the major hurdle is that gtable doesn't consider justification so you have to adjust the positions yourself,
library(gtable)
justify <- function(x, hjust="center", vjust="top", draw=FALSE){
w <- sum(x$widths)
h <- sum(x$heights)
xj <- switch(hjust,
center = 0.5,
left = 0.5*w,
right=unit(1,"npc") - 0.5*w)
yj <- switch(vjust,
center = 0.5,
bottom = 0.5*h,
top=unit(1,"npc") - 0.5*h)
x$vp <- viewport(x=xj, y=yj)
if(draw) grid.draw(x)
return(x)
}
title <- gtable_col('title', grobs = list(name,name2),
heights = unit.c(grobHeight(name) + 1.2*margin,
grobHeight(name2) + margin))
grid.newpage()
grid.arrange(justify(title, vjust='bottom'), justify(tbl))
Related
I have a question very similar to what discussed here:
Adding text to a grid.table plot
my ultimate goal however is to have a title 60mm from the top of the table, and a subtitle 2mm below the title.
I came up with this code that is almost there but not there, meaning, the subtitle is 2mm from the top of the table, and 2mm below the title, as expected.
library(gridExtra)
library(grid)
library(gtable)
d <- head(iris)
table <- tableGrob(d)
title <- textGrob("Title",gp=gpar(fontsize=50))
subtitle <- textGrob("subtitle", x=0, hjust=0,
gp=gpar( fontface="italic"))
padding <- unit(2,"mm")
table <- gtable_add_rows(table,
heights = grobHeight(subtitle)+ padding,
pos = 0)
padding <- unit(60,"mm")
table <- gtable_add_rows(table,
heights = grobHeight(title) + padding,
pos = 0)
table <- gtable_add_grob(table, list(title, subtitle),
t=c(1, 2), l=c(1,1),
r=ncol(table))
png('tmp.png', width = 480, height = 480, bg = "#FFECDB")
grid.newpage()
grid.draw(table)
dev.off()
I wonder if anybody has a suggestion on how to fix it.
Thank you
I'm new to grid tables, but it appears the order of your grobs matters, at least for padding. Is this the result you are expecting?
library(gridExtra)
library(grid)
library(gtable)
d <- head(iris)
table <- tableGrob(d)
title <- textGrob("Title", gp = gpar(fontsize=50))
subtitle <- textGrob("subtitle", x=0, hjust=0, gp=gpar( fontface="italic"))
table <- gtable_add_rows(table, heights = grobHeight(subtitle) + unit(58,"mm"), pos = 0)
table <- gtable_add_rows(table, heights = grobHeight(title) - unit(60,"mm"), pos = 0)
table <- gtable_add_grob(table, list(title, subtitle), t=c(1,2), l=c(1,1), r=ncol(table))
png('tmp.png', width = 480, height = 480, bg = "#FFECDB")
grid.newpage()
grid.draw(table)
dev.off()
I have a grid composed of several ggplots and want to add an x axis, where axis ticks and annotations are added between the plots. I could not came up with a better solution than to create a custom plot for the axis and adding it below with arrangeGrob. But they do not align with the plots (I draw arrows where the numbers should be). Also there is a large white space below which I don't want.
I will also need an analogue for the y-axis.
library(ggplot2)
library(gridExtra)
library(ggpubr)
library(grid)
# Create a grid with several ggplots
p <-
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
theme_transparent() +
theme(plot.background = element_rect(color = "black"))
main.plot <- arrangeGrob(p, p, p, p, p, p, p, p, ncol = 4, nrow = 2)
# grid.draw(main.plot)
# Now add an x axis to the main plot
x.breaks <- c(0, 1, 2.5, 8, 10)
p.axis <- ggplot() +
ylim(-0.1, 0) +
xlim(1, length(x.breaks)) +
ggpubr::theme_transparent()
for (i in seq_along(x.breaks)) {
p.axis <- p.axis +
geom_text(aes_(x = i, y = -0.01, label = as.character(x.breaks[i])), color = "red")
}
# p.axis
final.plot <- arrangeGrob(main.plot, p.axis, nrow = 2)
grid.draw(final.plot)
Any help appreciated.
Note: In the code below, I assume each plot in your grid has equal width / height, & used equally spaced label positions. If that's not the case, you'll have to adjust the positions yourself.
Adding x-axis to main.plot:
library(gtable)
# create additional row below main plot
# height may vary, depending on your actual plot dimensions
main.plot.x <- gtable_add_rows(main.plot, heights = unit(20, "points"))
# optional: check results to verify position of the new row
dev.off(); gtable_show_layout(main.plot.x)
# create x-axis labels as a text grob
x.axis.grob <- textGrob(label = x.breaks,
x = unit(seq(0, 1, length.out = length(x.breaks)), "npc"),
y = unit(0.75, "npc"),
just = "top")
# insert text grob
main.plot.x <- gtable_add_grob(main.plot.x,
x.axis.grob,
t = nrow(main.plot.x),
l = 1,
r = ncol(main.plot.x),
clip = "off")
# check results
dev.off(); grid.draw(main.plot.x)
You can do the same for the y-axis:
# create additional col
main.plot.xy <- gtable_add_cols(main.plot.x, widths = unit(20, "points"), pos = 0)
# create y-axis labels as a text grob
y.breaks <- c("a", "b", "c") # placeholder, since this wasn't specified in the question
y.axis.grob <- textGrob(label = y.breaks,
x = unit(0.75, "npc"),
y = unit(seq(0, 1, length.out = length(y.breaks)), "npc"),
just = "right")
# add text grob into main plot's gtable
main.plot.xy <- gtable_add_grob(main.plot.xy,
y.axis.grob,
t = 1,
l = 1,
b = nrow(main.plot.xy) - 1,
clip = "off")
# check results
dev.off(); grid.draw(main.plot.xy)
(Note that the above order of x-axis followed by y-axis should not be switched blindly. If you are adding rows / columns, it's good habit to use gtable_show_layout() frequently to check the latest gtable object dimensions, & ensure that you are inserting new grobs into the right cells.)
Finally, let's add some buffer on all sides, so that the labels & plot borders don't get cut off:
final.plot <- gtable_add_padding(main.plot.xy,
padding = unit(20, "points"))
dev.off(); grid.draw(final.plot)
I want to plot data for a linear model in a main plot and a plot of the effects (forest plot) as a subplot using arrangeGrob.
Here are the data:
set.seed(1)
main.df <- data.frame(sample=c(paste("E.plus.A.plus",1:3,sep="_"),paste("E.minus.A.plus",1:3,sep="_"),paste("E.plus.A.minus",1:3,sep="_"),paste("E.minus.A.minus",1:3,sep="_")),
replicate=rep(1:3,4),cpm=c(rnorm(12)),
factor.level=factor(c(rep("E.plus.A.plus",3),rep("E.minus.A.plus",3),rep("E.plus.A.minus",3),rep("E.minus.A.minus",3)),
levels=c("E.plus.A.plus","E.minus.A.plus","E.plus.A.minus","E.minus.A.minus")))
effects.df <- data.frame(factor.level=c("E.plus.A.plus-E.minus.A.plus","E.plus.A.plus-E.plus.A.minus","E.plus.A.plus-E.minus.A.minus",
"E.minus.A.plus-E.plus.A.minus","E.minus.A.plus-E.minus.A.minus","E.plus.A.minus-E.minus.A.minus"),
effect=rnorm(6),effect.df=runif(6,0,0.5),p.value=runif(6,0,1),y=1:6+0.2)
effects.df$effect.high <- effects.df$effect+effects.df$effect.df
effects.df$effect.low <- effects.df$effect-effects.df$effect.df
effects.df$factor.level <- factor(effects.df$factor.level,levels=effects.df$factor.level)
The ggplots:
require(ggplot2)
require(grid)
require(gridExtra)
main.plot <- ggplot(main.df,aes(x=replicate,y=cpm,color=factor.level))+geom_point(size=3)+
facet_wrap(~factor.level,ncol=length(levels(main.df$factor.level)))+
labs(x="replicate",y="cpm")+scale_x_continuous(breaks=unique(main.df$replicate))+theme_bw()+
theme(legend.key=element_blank(),panel.border=element_blank(),strip.background=element_blank(),axis.title=element_text(size=8),plot.title=element_text(size=9,hjust=0.5))
Which is:
sub.plot <- ggplot(effects.df,aes(x=effect,y=factor.level,color=factor.level))+geom_point(size=2.5,shape=19)+geom_errorbarh(aes(xmax=effect.high,xmin=effect.low),height=0.1)+
geom_vline(xintercept=0,linetype="longdash",colour="black",size=0.25)+theme_bw()+theme(legend.key=element_blank(),panel.border=element_blank(),strip.background=element_blank(),axis.title=element_text(size=7),axis.text=element_text(size=7),legend.text=element_text(size=7),legend.title=element_text(size=7))+
geom_text(aes(x=effects.df$effect,y=effects.df$y,label=format(signif(effects.df$p.value,2),scientific=T)),size=2.5)
And is:
And here's how I try to combine them into a single plot:
if(!is.null(dev.list())) dev.off()
blank <- grid.rect(gp = gpar(col = "white"))
sub.plot.grob <- arrangeGrob(blank,sub.plot,ncol=1)
combined.plot <- arrangeGrob(main.plot,sub.plot,ncol=2,widths=c(1,1))
grid.arrange(combined.plot)
which gives:
How do I adjust the position and dimensions so that sub.plot is smaller (all layers, e.g., text are reduced proportionally), and is positioned below the legend of main.plot?
I strongly recommend the package cowplot for this sort of task. Here, I am building three nested sets (the main plot to the left, then the two legends together at the top right, then the sub plot at the bottom right). Note the wonderful get_legend function that make pulling the legends incredibly easy.
plot_grid(
main.plot + theme(legend.position = "none")
, plot_grid(
plot_grid(
get_legend(main.plot)
, get_legend(sub.plot)
, nrow = 1
)
, sub.plot + theme(legend.position = "none")
, nrow = 2
)
, nrow = 1
)
gives:
Obviously I'd recommend changing one (or both) of the color palettes, but that should give what you want.
If you really want the legend with the sub.plot, instead of with the other legend, you could skip the get_legend.
You can also adjust the width/height of the sets using rel_widths and rel_heights if you want something other than the even sizes.
As an additional note, cowplot sets its own default theme on load. I generally revert to what I like by running theme_set(theme_minimal()) right after loading it.
here's a grid.arrange solution,
grid.arrange(grobs = replicate(4, ggplot(), simplify = FALSE),
layout_matrix = cbind(c(1,1), c(3,2), c(4, 2)),
widths = c(2,1,1))
with those bits and pieces,
get_legend <- function(p) {
g <- ggplotGrob(p)
id <- grep("guide", g$layout$name)
g$grobs[[id]]
}
leg1 <- get_legend(main.plot); leg2 <- get_legend(sub.plot)
gl <- list(main.plot + theme(legend.position = "none"),
sub.plot + theme(legend.position = "none"), leg1, leg2)
grid.arrange(grobs = gl,
layout_matrix = cbind(c(1,1), c(3,2), c(4, 2)),
widths = c(2,1,1))
I'm creating ggplots in a loop and then using grid.arrange to plot each of my figures on one page in a lattice-type graph. The problem I have is that I have a border around each figure and they merge together when I plot them. Does anyone know how to add white space between the figures. I've looked for information about figure padding and also toyed around with trying to add blank geom_rect between my plots, but so far no luck. Some simplified code is provided below. Thanks for any help you can offer.
p = vector("list", 3) #List for arranging grid
for(ii in 1:3){
p[[ii]] = ggplot(mtcars, aes(x = wt, y = mpg))+
geom_point()+
theme(plot.background = element_rect(colour = 'black', size = 2))
}
do.call("grid.arrange", c(p, ncol=1))
I tried quite a few different efforts to get the viewports to be smaller within a 3 x 1 layout and finally realized that just adding some blank space with narrow heights in the 5 x 1 layout was pretty easy:
Layout <- grid.layout(nrow = 5, ncol = 1,
heights=c(1, .1, 1, .1, 1) )
# could have written code to alternate heights or widths with gaps
grid.show.layout(Layout)
vplayout <- function(...) { # sets up new page with Layout
grid.newpage()
pushViewport(viewport(layout = Layout))
}
subplot <- function(x, y) viewport(layout.pos.row = x,
layout.pos.col = y)
mmplot <- function(p=p) { # could make more general
vplayout()
print(p[[1]], vp = subplot(1, 1 ))
print(p[[2]], vp = subplot(3, 1))
print(p[[3]], vp = subplot(5, 1 ))
}
mmplot(a, z)
alternatively, this experimental version of gtable offers a similar interface to grid.arrange,
library(ggplot2)
library(gtable)
lp <- replicate(3, qplot(rnorm(10), rnorm(10)) +
theme(plot.background=element_rect(size = 3, colour="black")),
simplify = FALSE)
lg <- lapply(lp, ggplotGrob)
g <- do.call(gtable_arrange, c(lg, ncol=1, draw=FALSE))
g <- gtable_add_rows(g, heights = unit(1, "line"), pos = 1)
g <- gtable_add_rows(g, heights = unit(1, "line"), pos = 3)
grid.newpage()
grid.draw(g)
I have recently started using the grid.table function from the gridExtra package to turn tabular data into png image files for use on the web. I've been delighted with it so far as it produces very good-looking output by default, sort of like a ggplot2 for tables. Like the person who asked this question I would love to see the ability to specify the justification for individual columns but that would be icing on what is an already more-ish cake.
My question is whether it is possible to add text around a grid.table so that I can give plotted tables a title and a footnote. It seems to me this should be feasible, but I don't know enough about grid graphics to be able to work out how to add grobs to the table grob. For example, this code:
require(gridExtra)
mydf <- data.frame(Item = c('Item 1','Item 2','Item 3'),
Value = c(10,15,20), check.names = FALSE)
grid.table(mydf,
gpar.coretext=gpar(fontsize = 16),
gpar.coltext = gpar(fontsize = 16),
gpar.rowtext = gpar(fontsize = 16),
gpar.corefill = gpar(fill = "blue", alpha = 0.5, col = NA),
h.even.alpha = 0.5,
equal.width = FALSE,
show.rownames = FALSE,
show.vlines = TRUE,
padding.h = unit(15, "mm"),
padding.v = unit(8, "mm")
)
generates this plot:
when I would really like to be able to do something like the following in code rather than by editing the image with another application:
To place text close to the table you'll want to evaluate the table size first,
library(gridExtra)
d <- head(iris)
table <- tableGrob(d)
grid.newpage()
h <- grobHeight(table)
w <- grobWidth(table)
title <- textGrob("Title", y=unit(0.5,"npc") + 0.5*h,
vjust=0, gp=gpar(fontsize=20))
footnote <- textGrob("footnote",
x=unit(0.5,"npc") - 0.5*w,
y=unit(0.5,"npc") - 0.5*h,
vjust=1, hjust=0,gp=gpar( fontface="italic"))
gt <- gTree(children=gList(table, title, footnote))
grid.draw(gt)
Edit (17/07/2015) With gridExtra >=2.0.0, this approach is no longer suitable. tableGrob now returns a gtable, which can be more easily customised.
library(gridExtra)
d <- head(iris)
table <- tableGrob(d)
library(grid)
library(gtable)
title <- textGrob("Title",gp=gpar(fontsize=50))
footnote <- textGrob("footnote", x=0, hjust=0,
gp=gpar( fontface="italic"))
padding <- unit(0.5,"line")
table <- gtable_add_rows(table,
heights = grobHeight(title) + padding,
pos = 0)
table <- gtable_add_rows(table,
heights = grobHeight(footnote)+ padding)
table <- gtable_add_grob(table, list(title, footnote),
t=c(1, nrow(table)), l=c(1,2),
r=ncol(table))
grid.newpage()
grid.draw(table)
If you want just the title (no footnote), here is a simplified version of #baptiste's example:
title <- textGrob("Title", gp = gpar(fontsize = 50))
padding <- unit(0.5,"line")
table <- gtable_add_rows(
table, heights = grobHeight(title) + padding, pos = 0
)
table <- gtable_add_grob(
table, list(title),
t = 1, l = 1, r = ncol(table)
)
grid.newpage()
grid.draw(table)