problems with arrangeGrob under R version 3.2.2 - r

I 've updated my R version including all packages and the function arrangeGrob (Package gridExtra) has changed.
On my old version R version 3.1.3 I used it as the following to make corner labels:
loading r packages
library(ggplot2)
library(grid)
library(gridExtra)
example data
a <- 1:20
b <- sample(a, 20)
c <- sample(b, 20)
d <- sample(c, 20)
create a data frame
mydata <- data.frame(a, b, c, d)
create example plots
myplot1 <- ggplot(mydata, aes(x=a, y=b)) + geom_point()
myplot2 <- ggplot(mydata, aes(x=b, y=c)) + geom_point()
myplot3 <- ggplot(mydata, aes(x=c, y=d)) + geom_point()
myplot4 <- ggplot(mydata, aes(x=d, y=a)) + geom_point()
set corner labels
myplot1 <- arrangeGrob(myplot1, main = textGrob("A", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
myplot2 <- arrangeGrob(myplot2, main = textGrob("B", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
myplot3 <- arrangeGrob(myplot3, main = textGrob("C", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
myplot4 <- arrangeGrob(myplot4, main = textGrob("D", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
grid.arrange(myplot1, myplot2, myplot3, myplot4)
and I got the following plot, which was fine:
but under the new R version 3.2.2 the image looks like this:
arrangeGrob opens for every textGrob a new image and I got eight images on one page instead of four. How can I fixed it that the plot looks like in the old version of R and gridExtra?

From Kev's comment:
There has been a rewrite of gridExtra, that is not (fully) backward
compatible - may be the issue. Have a look at the new wiki
cran.r-project.org/web/packages/gridExtra/vignettes/… . Try changing
main to top – user20650

Related

How to combine DiagrammeR with other plots in one panel?

I would like to include a diagram from DiagrammeR in a multipanel plot for example from ggplot2, but unfortunately DiagrammeR does not write on the graphic device. Does anyone know how to do that?
Here is my example, which works fine with two ggplot2 graphs, but not when I combine it with DiagrammeR.
library(ggplot2)
library(DiagrammeR)
library(grid)
library(gridExtra)
set.seed(1)
x <- 1:10
y <- x*rnorm(n=10, mean = 1, sd = 0.2)
data <- data.frame(x,y)
plot <- ggplot(data, aes(x=x,y=y)) + geom_point()
plot1 <- arrangeGrob(plot, top = textGrob("A", x = unit(0.0, "npc"), y =
unit(1, "npc"), just=c("left","top"), gp=gpar(col="black", fontsize=14,
fontfamily="Times")))
plot2 <- arrangeGrob(plot, top = textGrob("B", x = unit(0.0, "npc"), y =
unit(1, "npc"), just=c("left","top"), gp=gpar(col="black", fontsize=14,
fontfamily="Times")))
grid.arrange(plot1, plot2)
d <- grViz("
digraph boxes_and_circles {
# add node statements
node [shape = box]
A; B
node [shape = box]
C; D
A->C; B->D
}
")
d
d <- arrangeGrob(d, top = textGrob("B", x = unit(0.0, "npc"), y = unit(1,
"npc"), just=c("left","top"), gp=gpar(col="black", fontsize=14,
fontfamily="Times")))
grid.arrange(plot1, d)

How to add image to ggplot2 under the grid?

The most popular (and simplest) way to adding image to the ggplot2 graph is annotation_custom:
library(ggplot2)
library(png)
library(grid)
img <- readPNG(system.file("img", "Rlogo.png", package="png"), TRUE)
gpp <- rasterGrob(img, interpolate=TRUE)
gpp$width <- unit(1, "npc")
gpp$height <- unit(1, "npc")
df <- data.frame(x=seq(1,2,0.01),y=seq(1,2,0.01))
ggplot(df,aes(x=x,y=y)) +
annotation_custom(gpp, xmin=1, xmax=2.5, ymin=1, ymax=1.5) +
geom_point()
In this way, image will be placed over the scale grid.
How to place image under the grid, but with bindings to the coords, not to the borders of the plot?
It's possible in the development version of ggplot2.
How to install it see this answer: https://stackoverflow.com/a/9656182/4265407
Minimal working example:
library(devtools)
dev_mode(on=T)
library(ggplot2)
library(png)
library(grid)
img <- readPNG(system.file("img", "Rlogo.png", package="png"), TRUE)
gpp <- rasterGrob(img, interpolate=TRUE)
gpp$width <- unit(1, "npc")
gpp$height <- unit(1, "npc")
df <- data.frame(x=seq(1,2,0.01),y=seq(1,2,0.01))
ggplot(df,aes(x=x,y=y)) +
annotation_custom(gpp, xmin=1, xmax=2.5, ymin=1, ymax=1.5) +
geom_point() + theme(panel.ontop=TRUE,
panel.background = element_rect(colour = NA,fill="transparent"))

R ggplot2 Dual y-axis facet wrap, one histogram and other line

I have two facet wrapped plots, p1 and p2
p1
p2
As you can see, the x-axis values line up for both plots, however the y-axis values differ quite drastically. I would like to overlay p2 onto p1, keeping the p1 y axis on the left and creating another p2 y-axis on the right.
This is what I have right now, but I am unsure of how to correctly combine grobs for p1 and p2.
library(ggplot2)
library(gtable)
library(grid)
themer <- theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background = element_blank(),
panel.margin = unit(0, "lines"),
strip.background = element_rect(fill="#F8F8F8"))
p2 <- ggplot(normaldens, aes(y=density,x=predicted)) +
geom_line(color="red") +
facet_wrap(~ motif) +
labs(title=paste("Methylation Score:",motif_f[j]),x="Methylation Score",y="Density") +
themer
p1 <- ggplot(dat, aes(x=score)) +
geom_histogram( binwidth = bin_width,col="red",fill="blue",alpha=0.2) +
facet_wrap(~ motif) +
labs(title=paste("Methylation Score:",motif_f[j]),x="Methylation Score",y="Counts") +
themer
###### COMBINE GROBS #######
g1 <- ggplot_gtable(ggplot_build(p1))
g2 <- ggplot_gtable(ggplot_build(p2))
combo_grob <- g2
pos <- length(combo_grob) - 1
combo_grob$grobs[[pos]] <- cbind(g1$grobs[[pos]],
g2$grobs[[pos]], size = 'first')
panel_num <- length(unique(df1$z))
for (i in seq(panel_num))
{
# grid.ls(g1$grobs[[i + 1]])
panel_grob <- getGrob(g1$grobs[[i + 1]], 'geom_point.points',
grep = TRUE, global = TRUE)
combo_grob$grobs[[i + 1]] <- addGrob(combo_grob$grobs[[i + 1]],
panel_grob)
}
pos_a <- grep('axis_l', names(g1$grobs))
axis <- g1$grobs[pos_a]
for (i in seq(along = axis))
{
if (i %in% c(2, 4))
{
pp <- c(subset(g1$layout, name == paste0('panel-', i), se = t:r))
ax <- axis[[1]]$children[[2]]
ax$widths <- rev(ax$widths)
ax$grobs <- rev(ax$grobs)
ax$grobs[[1]]$x <- ax$grobs[[1]]$x - unit(1, "npc") + unit(0.5, "cm")
ax$grobs[[2]]$x <- ax$grobs[[2]]$x - unit(1, "npc") + unit(0.8, "cm")
combo_grob <- gtable_add_cols(combo_grob, g2$widths[g2$layout[pos_a[i],]$l], length(combo_grob$widths) - 1)
combo_grob <- gtable_add_grob(combo_grob, ax, pp$t, length(combo_grob$widths) - 1, pp$b)
}
}
pp <- c(subset(g1$layout, name == 'ylab', se = t:r))
ia <- which(g1$layout$name == "ylab")
ga <- g1$grobs[[ia]]
ga$rot <- 270
ga$x <- ga$x - unit(1, "npc") + unit(1.5, "cm")
combo_grob <- gtable_add_cols(combo_grob, g2$widths[g2$layout[ia,]$l], length(combo_grob$widths) - 1)
combo_grob <- gtable_add_grob(combo_grob, ga, pp$t, length(combo_grob$widths) - 1, pp$b)
combo_grob$layout$clip <- "off"
grid.draw(combo_grob)
And I get this error, which I know has to do something with the way I'm combining the two gtables.
Error in gList(list(x = 0.5, y = 0.5, width = 1, height = 1, just = "centre", :
only 'grobs' allowed in "gList"
I don't think you can do a second y-axis within ggplot2, but what about plotting both density and histogram in a single plot and using bar labeling for the counts (instead of trying to hack a second y-axis). Here's an example (using the built-in iris dataset):
First, we'll calculate maximum values of density and count and use these to create scale factors that we'll use to programmatically ensure that the histogram and density plot have about the same vertical scale.
library(dplyr)
# Find maximum value of density
densMax = iris %>% group_by(Species) %>%
summarise(dens = max(density(Sepal.Length)[["y"]])) %>%
filter(dens == max(dens))
# Find maximum value of bin count
countMax = iris %>%
group_by(Species,
bins=cut(Sepal.Length, seq(floor(min(Sepal.Length)),
ceiling(max(Sepal.Length)),
0.25), right=FALSE)) %>%
summarise(count=n()) %>%
ungroup() %>% filter(count==max(count))
Now we scale the histogram bars to the size of the density plot. sf is the scale factor:
ggplot(iris, aes(x=Sepal.Length, sf = countMax$count/densMax$dens)) +
geom_histogram(fill=hcl(195,100,65), colour="grey50", binwidth=0.25) +
geom_density(colour="red", aes(y=..density.. * sf)) +
facet_wrap(~ Species) +
themer
Alternatively, you could go in the other direction, and scale the density plot to the histogram:
# Scale histogram bars to size of density plot
ggplot(iris, aes(x=Sepal.Length, sf = densMax$dens/countMax$count)) +
geom_histogram(aes(y=..count..*sf),
fill=hcl(195,100,65), colour="grey50", binwidth=0.25) +
stat_bin(aes(label=..count.., y=..count..*0.5*sf),
geom="text", size=4, color="white", binwidth=0.25) +
geom_density(colour="red") +
facet_wrap(~ Species) +
themer +
labs(y="Density")

Annotate below a ggplot2 graph

I would like plot a line below a ggplot2 graph with text above it, something like this:
where the starting and ending point of the gene on the x axis can be specified.
My attempt so far:
require(ggplot2)
require(grid)
require(gridExtra)
data = data.frame(y = -log10(runif(100)), x = 1:100)
p = ggplot(data=data, aes(x, y)) + geom_point()
p = p + theme(plot.margin=unit(c(1, 1, 5, 1), "lines"))
t1 = textGrob("Gene1")
p1 = p + annotation_custom(grob=t1, xmin=0, ymin=0, xmax = 3, ymax=-.1)
print(p1)
which gives:
If I try to move the text down by adjusting ymax, then it disappears.
In my answer, I have changed a couple things:
1 - I changed the name of your data to "df", as "data" can cause confusion between objects and arguments.
2 - I removed the extra panel space around the main data plot, so that the annotation wasn't so far away.
require(ggplot2)
require(grid)
require(gridExtra)
# make the data
df <- data.frame(y = -log10(runif(100)), x = 1:100)
p <- ggplot(data=df, aes(x, y)) + geom_point()
# remove this line of code:
# p <- p + theme(plot.margin=unit(c(1, 1, 5, 1), "lines"))
# set up the plot theme for the annotation
blank_axes_and_thin_margin <- theme(axis.text = element_text(color="white"),
axis.title = element_text(color="white"),
axis.ticks = element_blank(),
panel.grid = element_blank(),
panel.border = element_blank(),
plot.margin=unit(c(0, 2, 0,2),"mm"))
# define the position of the arrow (you would change this part)
arrow_start <- min(df$x)
arrow_end <- mean(c(min(df$x), max(df$x)))
arrow_height <- 1
# here's the rectangle with the arrow
t2 <- ggplot(df, aes(x,y))+
theme_bw()+
geom_rect(aes(xmin=min(x), xmax = max(x)),
ymin=0, ymax=4,fill="gray50")+
coord_cartesian(ylim=c(0,4))+
annotate(geom="text", label="Gene1",
x=20, y=2, size=6, color="black")+
geom_segment(x=arrow_start, xend=arrow_end,
y=arrow_height, yend=arrow_height,
color="black", arrow=arrow(ends="both"))+
blank_axes_and_thin_margin
t2
# arrange the graphic objects here
# I use arrangeGrob because it allows you to use ggsave(), unlike grid.arrange
plot_both <- arrangeGrob(p, t2, nrow=2, heights=unit(c(0.75,0.25), "null"))
plot_both
# ta-da !
you can turn clipping off,
g <- ggplotGrob(p1)
g$layout$clip[g$layout$name == "panel"] <- "off"
grid.newpage()
grid.draw(g)

Label individual panels in a multi-panel ggplot2

I'm interested in trying to create simple corner labels for a multipanel figure I am preparing in ggplot. This is similar to this previously asked question, but the answers only explained how to include a label at the top of the plot, not produce a corner label in the format required by many journals. I hope to replicate something similar to the plotrix function corner.label() in ggplot2.
Here is an example using plottrix of what I would like to recreate in ggplot2.
require(plotrix)
foo1<-rnorm(50,25,5)
foo2<-rpois(50,25)
foo3<-rbinom(50,25,0.5)
foo4<-rnbinom(50,25,0.5)
par(mfrow=c(2,2))
hist(foo1)
corner.label(label='a',figcorner=T)
hist(foo2)
corner.label(label='b',figcorner=T)
hist(foo3)
corner.label(label='c',figcorner=T)
hist(foo4)
corner.label(label='d',figcorner=T)
This produces the following:
Thanks for any help in advance!
Two recent changes have made this a lot easier:
The latest release of ggplot2 has added the tag caption which can be used to label subplots.
The package patchwork makes it really easy to plot multiple ggplot objects. https://github.com/thomasp85/patchwork
This means that no altering of grobs is required. Adapting the reproducible example provided by Kev:
library(ggplot2)
# install.package("patchwork")
library(patchwork)
a <- 1:20
b <- sample(a, 20)
c <- sample(b, 20)
d <- sample(c, 20)
mydata <- data.frame(a, b, c, d)
myplot1 <- ggplot(mydata, aes(x=a, y=b)) + geom_point() + labs(tag = "A")
myplot2 <- ggplot(mydata, aes(x=b, y=c)) + geom_point() + labs(tag = "B")
myplot3 <- ggplot(mydata, aes(x=c, y=d)) + geom_point() + labs(tag = "C")
myplot4 <- ggplot(mydata, aes(x=d, y=a)) + geom_point() + labs(tag = "D")
myplot1 + myplot2 + myplot3 + myplot4
Extension: Changing Style:
If you want to change the labelling style, you can either set this individually for each plot or set a theme default. I would recommend the second approach. Add the following line before you build your plots to make the font bold and blue
ggplot2::theme_update(plot.tag = element_text(face = "bold", colour = "blue"))
For more information on customising the theme of ggplot2, see here.
I had the same problem and came up with the following solution, which is a bit different:
loading r packages
library(ggplot2)
library(grid)
library(gridExtra)
example data
a <- 1:20
b <- sample(a, 20)
c <- sample(b, 20)
d <- sample(c, 20)
create a data frame
mydata <- data.frame(a, b, c, d)
create example plots
myplot1 <- ggplot(mydata, aes(x=a, y=b)) + geom_point()
myplot2 <- ggplot(mydata, aes(x=b, y=c)) + geom_point()
myplot3 <- ggplot(mydata, aes(x=c, y=d)) + geom_point()
myplot4 <- ggplot(mydata, aes(x=d, y=a)) + geom_point()
set corner labels
myplot1 <- arrangeGrob(myplot1, top = textGrob("A", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
myplot2 <- arrangeGrob(myplot2, top = textGrob("B", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
myplot3 <- arrangeGrob(myplot3, top = textGrob("C", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
myplot4 <- arrangeGrob(myplot4, top = textGrob("D", x = unit(0, "npc")
, y = unit(1, "npc"), just=c("left","top"),
gp=gpar(col="black", fontsize=18, fontfamily="Times Roman")))
plotting all plots on one page
grid.arrange(myplot1, myplot2, myplot3, myplot4, ncol = 2)
An example:
d <- data.frame(x = runif(16),
y = runif(16),
grp = rep(letters[1:4],each = 4))
ggplot(d,aes(x = x,y = y)) +
facet_wrap(~grp) +
geom_point() +
theme(strip.text = element_text(hjust = -0.05),
strip.background = element_blank())
Here's a solution using a custom labeller function. This doesn't invovle any manipulations to the data. Currently it only works with 1-dimensional facets (facet_wrap). I'm still working on how to increment along a 2-D grid...
Define the labeller function
make_labelstring <- function(mypanels) {
mylabels <- sapply(mypanels,
function(x) {LETTERS[which(mypanels == x)]})
return(mylabels)
}
label_panels <- ggplot2::as_labeller(make_labelstring)
Pass label_panels as the labeller to facet_wrap
library(ggplot2)
data("diamonds")
# create a faceted plot
ggplot(data = diamonds, aes(x = depth, y = price)) +
geom_point() +
facet_wrap(~cut, labeller = label_panels) +
theme(strip.text = element_text(hjust = -0),
strip.background = element_blank())

Resources