ggplot2 & gridExtra: Reduce space between plots, but keep ylab of one plot - r

I would like to plot 3 graphics beside each other via the ggplot2 and gridExtra packages. The graphic on the left side has a ylab, the other 2 graphics do not. All three graphics should have the same size and the space between the graphics should be reduced as much as possible. However, due to the ylab of the graphic on the left side, I am either not able to reduce the space as much as I want; or I am cutting off the ylab.
Consider the following example in R:
library("ggplot2")
library("gridExtra")
# Example data
df <- data.frame(x = 1:10,
y = 1:10)
# Plots
ggp1 <- ggplot(df, aes(x, y)) +
geom_line() + theme_bw() +
ylab("Here is the ylab")
ggp2 <- ggplot(df, aes(x, y)) +
geom_line() + theme_bw() +
ylab("")
ggp3 <- ggplot(df, aes(x, y)) +
geom_line() + theme_bw() +
ylab("")
# Arrange grids
grid.arrange(ggp1, ggp2, ggp3, ncol = 3)
The space between the graphics should be reduced as much as possible.
All graphics should have the same size.
The ylab of the graphic on the left side should be kept.
I was trying to fix the problem with plot.margin, but unfortunately that didn't work.

I would suggest to cbind() the gtables, with the axis removed. null units automatically ensure equal panel widths.
lg <- lapply(list(ggp1,ggp2,ggp3),ggplotGrob)
rm_axis <- function(g){
lay <- g[["layout"]]
cp <- lay[lay$name == "panel",]
g[,-c(1:(cp$l-1))]
}
lg[-1] <- lapply(lg[-1], rm_axis)
grid::grid.draw(do.call(gtable_cbind, lg))

Adding theme (axis.title.y = element_blank()) to ggp2 and ggp3 will reduce the space between them.
library("ggplot2")
library("gridExtra")
# Example data
df <- data.frame(x = 1:10,
y = 1:10)
# Plots
ggp1 <- ggplot(df, aes(x, y)) +
geom_line() + theme_bw() +
ylab("Here is the ylab")
ggp2 <- ggplot(df, aes(x, y)) +
geom_line() + theme_bw() +
ylab("") + theme (axis.title.y = element_blank())
ggp3 <- ggplot(df, aes(x, y)) +
geom_line() + theme_bw() +
ylab("") + theme (axis.title.y = element_blank())
# Arrange grids
grid.arrange(ggp1, ggp2, ggp3, ncol = 3)

Related

multiple axis in a ggplot [duplicate]

Is there a possibility to add a third y-axis to a plot with ggplot2?
I have three different datasources I want to display in the plot. I already added a second y-axis, for the next dataset the scale is again very different, why I'm looking now for a solution.
So far I only found how to add a second axis, for example as shown [here].(https://r-graph-gallery.com/line-chart-dual-Y-axis-ggplot2.html)
But I whether there is an possibility to add another one... Thank you!
This is equally clunky, but shows how it can be done from scratch using only CRAN resources.
library(cowplot)
library(patchwork)
p1 <- ggplot(df, aes(Sepal.Width, Sepal.Length)) +
geom_point() + theme(axis.line = element_line())
p2 <- ggplot(df, aes(Sepal.Width, Petal.Width)) + geom_point() +
theme(axis.line = element_line())
p3 <- ggplot(df, aes(Sepal.Width, Petal.Length)) +
geom_point(aes(color = "Petal.Length")) +
geom_point(aes(y = Sepal.Length/100, color = "Sepal.Length")) +
geom_point(aes(y = Petal.Width / 1000, color = "Petal.Width")) +
theme(axis.line = element_line(),
plot.margin = margin(10, 10, 10, 30))
wrap_elements(get_plot_component(p1, "ylab-l")) +
wrap_elements(get_y_axis(p1)) +
wrap_elements(get_plot_component(p2, "ylab-l")) +
wrap_elements(get_y_axis(p2)) +
p3 +
plot_layout(widths = c(3, 1, 3, 1, 40))
Data used
df <- iris
df$Sepal.Length <- df$Sepal.Length * 100
df$Petal.Width <- df$Petal.Width * 1000
This is a very clunky solution based on extracting elements from previously plotted graphs and editing grid objects. It may or not give you a workable solution.
source("https://raw.githubusercontent.com/davidearn/plague_growth/master/analysis/plots/3axes.R")
set.seed(101)
dd <- data.frame(x=rnorm(20),y=rnorm(20))
library(ggplot2)
gg0 <- ggplot(dd)
g1A <- gg0 + geom_point(aes(x,y))
g1B <- gg0 + geom_point(aes(x,10*y))
g1C <- gg0 + geom_point(aes(x,100*y))
## use return_gtable = TRUE if planning to add further axes
g2 <- combine_axes(g1A,g1B,add_pos="l", return_gtable=TRUE)
g3 <- combine_axes(g2,g1C,add_pos="l")
print(g3)

Is it possible to add a third y-axis to ggplot2?

Is there a possibility to add a third y-axis to a plot with ggplot2?
I have three different datasources I want to display in the plot. I already added a second y-axis, for the next dataset the scale is again very different, why I'm looking now for a solution.
So far I only found how to add a second axis, for example as shown [here].(https://r-graph-gallery.com/line-chart-dual-Y-axis-ggplot2.html)
But I whether there is an possibility to add another one... Thank you!
This is equally clunky, but shows how it can be done from scratch using only CRAN resources.
library(cowplot)
library(patchwork)
p1 <- ggplot(df, aes(Sepal.Width, Sepal.Length)) +
geom_point() + theme(axis.line = element_line())
p2 <- ggplot(df, aes(Sepal.Width, Petal.Width)) + geom_point() +
theme(axis.line = element_line())
p3 <- ggplot(df, aes(Sepal.Width, Petal.Length)) +
geom_point(aes(color = "Petal.Length")) +
geom_point(aes(y = Sepal.Length/100, color = "Sepal.Length")) +
geom_point(aes(y = Petal.Width / 1000, color = "Petal.Width")) +
theme(axis.line = element_line(),
plot.margin = margin(10, 10, 10, 30))
wrap_elements(get_plot_component(p1, "ylab-l")) +
wrap_elements(get_y_axis(p1)) +
wrap_elements(get_plot_component(p2, "ylab-l")) +
wrap_elements(get_y_axis(p2)) +
p3 +
plot_layout(widths = c(3, 1, 3, 1, 40))
Data used
df <- iris
df$Sepal.Length <- df$Sepal.Length * 100
df$Petal.Width <- df$Petal.Width * 1000
This is a very clunky solution based on extracting elements from previously plotted graphs and editing grid objects. It may or not give you a workable solution.
source("https://raw.githubusercontent.com/davidearn/plague_growth/master/analysis/plots/3axes.R")
set.seed(101)
dd <- data.frame(x=rnorm(20),y=rnorm(20))
library(ggplot2)
gg0 <- ggplot(dd)
g1A <- gg0 + geom_point(aes(x,y))
g1B <- gg0 + geom_point(aes(x,10*y))
g1C <- gg0 + geom_point(aes(x,100*y))
## use return_gtable = TRUE if planning to add further axes
g2 <- combine_axes(g1A,g1B,add_pos="l", return_gtable=TRUE)
g3 <- combine_axes(g2,g1C,add_pos="l")
print(g3)

Rotate upper triangle of a ggplot tile heatmap

I've plotted a heat-map like this:
ggplot(test, aes(start1, start2)) +
geom_tile(aes(fill = logFC), colour = "gray", size=0.05) +
scale_fill_gradientn(colours=c("#0000FF","white","#FF0000"), na.value="#DAD7D3")
This plots the upper triangle of a heatmap. What i'd like to plot is the very same triangle, but having the hypotenuse as the x-axis.
How would I do that?
Edit: Added reproducible example
library(ggplot2)
# dummy data
df1 <- mtcars[, c("gear","carb", "mpg")]
# normal tile plot
gg1 <- ggplot(df1, aes(gear, carb, fill = mpg)) +
geom_tile() +
xlim(c(1, 10)) +
ylim(c(1, 10)) +
theme_void() +
theme(legend.position = "none")
Expected output (rotated manually):
Related post using base plot image():
Visualising and rotating a matrix
Possible solution example code is in LDheatmap package using grid.
Using this solution gets the output clipped at the bottom, so workaround would be to add extra plot margins then use grid::viewport() to rotate:
library(ggplot2) #ggplot2_2.2.1
library(grid)
gg1 <- ggplot(df1, aes(gear, carb, fill = mpg)) +
geom_tile() +
xlim(c(1, 10)) +
ylim(c(1, 10)) +
theme_void() +
# add extra margins
theme(legend.position = "none",
plot.margin = unit(c(1, 1, 1, 1), "cm"))
# then rotate
print(gg1, vp = viewport(angle = 45))

box plot in R with additional point

I have a dataframe of multiple columns (let's say n) with different range and a vector of length n. I want different x-axis for each variable to be shown below each box plot. I tried facet_grid and facet_wrap but it gives common x axis.
This is what I have tried:
d <- data.frame(matrix(rnorm(10000), ncol = 20))
point_var <- rnorm(20)
plot.data <- gather(d, variable, value)
plot.data$test_data <- rep(point_var, each = nrow(d))
ggplot(plot.data, aes(x=variable, y=value)) +
geom_boxplot() +
geom_point(aes(x=factor(variable), y = test_data), color = "red") +
coord_flip() +
xlab("Variables") +
theme(legend.position="none")
If you can live with having the text of the x axis above the plot, and having the order of the graphs a bit messed-up this could work:
library(grid)
p = ggplot(plot.data, aes(x = 0, y=value)) +
geom_boxplot() +
geom_point(aes(x = 0, y = test_data), color = "red") +
facet_wrap(~variable, scales = "free_y", switch = "y") +
xlab("Variables") +
theme(legend.position="none") + theme_bw() + theme(axis.text.x=element_blank())
print(p, vp=viewport(angle=270, width = unit(.75, "npc"), height = unit(.75, "npc")))
I'm actually just creating the graph without flipping coords, so that scales = 'free_y' works, swithcing the position of the strip labels, and then rotating the graph.
If you don't like the text above graph (which is understandable), I would consider creating a list of single plots and then putting them together with grid.arrange.
HTH,
Lorenzo

Top to bottom alignment of two ggplot2 figures

I realize that the align.plots function from the ggExtra package has been deprecated and removed. However, I am using my own version as it seems to provide the specific functionality I need. I have looked into faceting to solve my problem but I don't think it will work for my particular issue. What seems to be the problem is that the top-to-bottom images don't align when I use coord_equal on one of them. This doesn't seem to affect left-to-right though. Here is a simplified (or at least as simple as I can make it) version of what I am trying to achieve.
Create some dummy data frames:
source('https://raw.github.com/jbryer/multilevelPSA/master/r/align.R')
require(psych)
df = data.frame(x=rnorm(100, mean=50, sd=10),
y=rnorm(100, mean=48, sd=10),
group=rep(letters[1:10], 10))
dfx = describe.by(df$x, df$group, mat=TRUE)[,c('group1', 'mean', 'n', 'min', 'max')]
names(dfx) = c('group', 'x', 'x.n', 'x.min', 'x.max')
dfy = describe.by(df$y, df$group, mat=TRUE)[,c('group1', 'mean', 'n', 'min', 'max')]
names(dfy) = c('group', 'y', 'y.n', 'y.min', 'y.max')
df2 = cbind(dfx, dfy[,2:ncol(dfy)])
range = c(0,100)
This will setup the three plots:
p1a = ggplot(df2, aes(x=x, y=y, colour=group)) + geom_point() +
opts(legend.position='none') +
scale_x_continuous(limits=range) + scale_y_continuous(limits=range)
p1 = p1a + coord_equal(ratio=1)
p2 = ggplot(df, aes(x=x, y=group, colour=group)) + geom_point() +
scale_x_continuous(limits=range) + opts(legend.position='none')
p3 = ggplot(df, aes(x=group, y=y, colour=group)) + geom_point() +
scale_y_continuous(limits=range) + opts(legend.position='none')
The alignment top to bottom does not work with coord_equal
grid_layout <- grid.layout(nrow=2, ncol=2, widths=c(1,2), heights=c(2,1))
grid.newpage()
pushViewport( viewport( layout=grid_layout, width=1, height=1 ) )
align.plots(grid_layout, list(p1, 1, 2), list(p3, 1, 1), list(p2, 2, 2))
Broken Plot http://bryer.org/alignplots1.png
The fix is to add respect=TRUE to the grid.layout call:
grid_layout <- grid.layout(nrow=2, ncol=2, widths=c(1,2), heights=c(2,1), respect=TRUE)
But if I don't use coord_equal the alignment works fine:
grid_layout <- grid.layout(nrow=2, ncol=2, widths=c(1,2), heights=c(2,1))
grid.newpage()
pushViewport( viewport( layout=grid_layout, width=1, height=1 ) )
align.plots(grid_layout, list(p1a, 1, 2), list(p3, 1, 1), list(p2, 2, 2))
Working Plot http://bryer.org/alignplots2.png
ggplot2 now has ggplotGrob(), which may help with this.
First, we need to update the code used to generate the plots:
p1a = ggplot(df2, aes(x=x, y=y, colour=group)) + geom_point() +
scale_x_continuous(limits=range) + scale_y_continuous(limits=range)
p1 = p1a + coord_equal(ratio=1) + theme_minimal() + theme(legend.position='none')
p2 = ggplot(df, aes(x=x, y=group, colour=group)) + geom_point() +
scale_x_continuous(limits=range) + theme_minimal() + theme(legend.position='none')
p3 = ggplot(df, aes(x=group, y=y, colour=group)) + geom_point() +
scale_y_continuous(limits=range) + theme_minimal() + theme(legend.position='none')
p4 <- ggplot(df, aes(x = group, y = y)) +
geom_blank() +
theme(line = element_blank(),
rect = element_blank(),
text = element_blank(),
title = element_blank())
p4 will be blank; we just need the grob to draw.
Then we load the grid package and draw the grobs in a list arranged in rows and columns using cbind() and rbind().
library(grid)
grid.newpage()
grid.draw(
cbind(
rbind(ggplotGrob(p3), ggplotGrob(p4), size = "first"),
rbind(ggplotGrob(p1), ggplotGrob(p2), size = "first"),
size = "first"))
I'm not sure if this method will let you plot p3 in a different width and p2 in a different height, as you have them in the original example; I normally need a grid of similarly-sized graphs, and haven't needed to figure out different sizes.
This answer is partially based on partially based on https://stackoverflow.com/a/17463184/393354
Here is an example:
m <- matrix(c(3, 1, 0, 2), 2, byrow = T)
lay <- gglayout(m, widths = c(1, 3), heights = c(3, 1))
ggtable(p1, p2, p3, layout = lay)
you can use this by
install.packages('devtools')
library(devtools)
dev_mode()
install_github("ggplot2", "kohske", "cutting-edge")
library(ggplot2)
note that this branch is experimental, so maybe there are bugs.
To solve the problem using the align.plots method, specify respect=TRUE on the layout call:
grid_layout <- grid.layout(nrow=2, ncol=2, widths=c(1,2), heights=c(2,1), respect=TRUE)

Resources