How do I fix the size of multiple ggplot plots? - r

I noticed when combining multiple ggplots, where the values in one are much larger than the other, because the y axis ticks are larger in the former (due to being bigger numbers), it makes the plot itself smaller. To illustrate:
df <- data.frame('name'=1:30,'v1'=rpois(30, 1),'v2'=rpois(30, 100))
p1 <- ggplot(data=df,aes(x=name,y=v1)) + geom_bar(stat = 'identity')
p2 <- ggplot(data=df,aes(x=name,y=v2)) + geom_bar(stat = 'identity')
combined <- ggarrange(p1, p2, ncol=1, nrow=2)
print(combined)
Note that the bottom plot is slightly smaller than the top plot, because the the numbers in the y-ticks are larger. Is there anyway to make it so that the plots themselves are exactly the same size? (That doesn't involve manually changing the tick sizes until the plots look approximately the same). I tried using ggplotGrob(), which was suggested elsewhere, but it didn't seem to help. Thanks.

The {patchwork} library does this nicely by default.
library(tidyverse)
library(patchwork)
df <- data.frame('name'=1:30,'v1'=rpois(30, 1),'v2'=rpois(30, 100))
p1 <- ggplot(data=df,aes(x=name,y=v1)) + geom_bar(stat = 'identity')
p2 <- ggplot(data=df,aes(x=name,y=v2)) + geom_bar(stat = 'identity')
p1/p2
Created on 2022-01-27 by the reprex package (v2.0.1)

Just add library(egg)
It seems that you have loaded library(ggpubr) that also have a ggarrange function:
library(egg)
df <- data.frame('name'=1:30,'v1'=rpois(30, 1),'v2'=rpois(30, 100))
p1 <- ggplot(data=df,aes(x=name,y=v1)) + geom_bar(stat = 'identity')
p2 <- ggplot(data=df,aes(x=name,y=v2)) + geom_bar(stat = 'identity')
combined <- ggarrange(p1, p2, ncol=1, nrow=2)
print(combined)

Related

Reduce distance in plot X labels (R: ggplot2)

This is my dataframe:
df = data.frame(info=1:30, type=c(replicate(5,'A'), replicate(5,'B')), group= c(replicate(10,'D1'), replicate(10,'D2'), replicate(10,'D3')))
I want to make a jitter plot of my data distinguished by group (X-label) and type (colour):
ggplot()+
theme(panel.background=element_rect(colour="grey", size=0.2, fill='grey100'))+
geom_jitter(data=df, aes(x=group, y=info, color=type, shape=type), position=position_dodge(0.2), cex=2)+
scale_shape_manual(values=c(17,15,19))+
scale_color_manual(values=c(A="mediumvioletred", B="blue"))
How can I reduce the distance between the X-labels (D1, D2, D3) in the representation?
P.D. I want to do it even if I left a blank space in the graphic
Here are a few options.
# Setting up the plot
library(ggplot2)
df <- data.frame(
info=1:30,
type=c(replicate(5,'A'), replicate(5,'B')),
group= c(replicate(10,'D1'), replicate(10,'D2'), replicate(10,'D3'))
)
p <- ggplot(df, aes(group, info, colour = type, shape = type))
Option 1: increase the dodge distance. This won't put the labels closer, but it makes better use of the space available so that the labels appear less isolated.
p +
geom_point(position = position_dodge(width = 0.9))
Option 2: Expand the x-axis. Increasing the expansion factor from the default 0.5 to >0.5 increases the space at the ends of the axis, putting the labels closer.
p +
geom_point(position = position_dodge(0.2)) +
scale_x_discrete(expand = c(2, 0))
Option 3: change the aspect ratio. Depending on the plotting window size, this also visually puts the x-axis labels closer together.
p +
geom_point(position = position_dodge(0.2)) +
theme(aspect.ratio = 2)
Created on 2021-06-25 by the reprex package (v1.0.0)
Try adding coord_fixed(ratio = 0.2) and play around with the ratio.
ggplot()+
theme(panel.background=element_rect(colour="grey", size=0.2, fill='grey100'))+
geom_jitter(data=df, aes(x=group, y=info, color=type, shape=type), position=position_dodge(0.2))+
scale_shape_manual(values=c(17,15,19))+
scale_color_manual(values=c(A="mediumvioletred", B="blue")) + coord_fixed(ratio = 0.2)
The simplest solution is to resize the plot. For example if you follow your command with ggsave("my_plot.pdf", width = 3, height = 4.5) it looks like this:
Or in an Rmd file you can control the dimensions by various means: see this link.

Horizontal alignment of colour scales in patchwork

I'm using the package patchwork to combine multiple ggplot2 plots vertically. I'd like the scales for each plot to be directly above one another, regardless of the length of the scale name. At the moment, the scales are not aligned above one another.
I'm open to using ggpubr or facet_grid() if they would make it possible, but I've seen that facets doesn't allow multiple scales, and I haven't found any solution using ggpubr
library(ggplot2)
library(patchwork)
set.seed(0)
testdata <- data.frame(x=1:10, y=1:10, col=runif(10))
g1 <- ggplot(testdata, aes(x=x,y=y,col=col)) + geom_point() +
scale_color_gradient(name="Short")
g2 <- ggplot(testdata, aes(x=x,y=y,col=col)) + geom_point() +
scale_color_gradient(name="A rather longer name")
g1/g2
ggsave("testfile.tiff", units = "mm", device="tiff",
width=100, height=100, dpi = 100)
Ideal output:
With plot_layout you can "collect" the legends. This uses as default theme(legend.position = 'right'). You can add this after plot_layout with & theme(legend.position = 'right') and adjust the position if you want to change the location of the legends.
g1/g2 + plot_layout(guides = 'collect') # & theme(legend.position = 'right') <- adjust position here!
ggsave("testfile.tiff", units = "mm", device="tiff",
width=100, height=100, dpi = 100)
I'd also be curious to learn of a patchwork parameter than could fix this, but I don't think there is one (please correct me if I'm wrong). You may have noticed that Hadley's answer is more than 10 years old and people have been working on ggplot2 since then. The ggnewscale package solves the problem of having multiple scales per plot. Here is a facetted approach using multiple colourscales:
library(ggplot2)
library(ggnewscale)
set.seed(0)
testdata <- data.frame(x=1:10, y=1:10, col=runif(10))
ggplot(mapping = aes(x = x, y, y)) +
geom_point(data = transform(testdata,
facet = factor("Top", c("Top", "Bottom"))),
aes(colour = col)) +
scale_colour_continuous(name = "Short") +
new_scale_colour() +
geom_point(data = transform(testdata,
facet = factor("Bottom", c("Top", "Bottom"))),
aes(colour = col)) +
scale_colour_continuous(name = "A rather longer name") +
facet_wrap(~ facet, ncol = 1)

Smart association of graphs where one is faceted - ggplot2

I want to combine these two graphs :
p1 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
facet_wrap(~ Species)
p2 <- ggplot(iris, aes(Sepal.Length)) +
geom_density()
To combine, I do :
multiplot(p1, p2, cols = 2)
But it is not the desired shape.
I would like the graph p2 has the same dimensions than others and is situated just next to the last faceted graph.
Thanks for help
Not sure if this is applicable in you generic case, but with facet_grid instead of facet_wrap, you can use the margins argument:
library(ggplot2)
ggplot(iris, aes(Sepal.Length)) +
geom_density() +
facet_grid(. ~ Species, margins = T)
If you question is more generic the answer probably lies in grid.arrange.
Something like this could be a start:
library(gridExtra)
grid.arrange(arrangeGrob(p1, p2,
widths = c(3,1),
heights = c(1,20),
layout_matrix = matrix(c(1,1,NA,2),2)))
As you can see there are several problems (different axes, top strip), but working with grid could gets complicated quickly.
This code should work:
p1 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
ylim(limits = c(0, 1.25))+
facet_wrap(~ Species)
p2 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
ggtitle("") + # ad empty title as place holder
labs(y = "", x = "") + # hide axis labels
ylim(limits = c(0, 1.25)) + # y axis values should be fixed in both plots
coord_fixed(ratio=20/1) + # ratio of x- and y-axis to reduce width of plot
theme(axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.line.y = element_blank(),
plot.margin=unit(c(0,0,0.65,-10), "lines")) # margin between plots = "0.65"
I fiddled a bit and used just different styling options to produce this result. If you have more plots than this one I would recommend to use one theme for all.
You can use either the multiplot function that you are already using
multiplot(p1, p2, cols = 2)
or you install the packages gridExtra and grid and use that one:
grid.arrange(p1, p2, ncol=2)
Hope this helps!

ggplot2, applying two scales to the same plot? Top down barplot

See plot here:
(from here)
How do I reproduce both the upper and lower portion of the barplot using ggplot2?
For example, I can produce the upper portion with
ggplot(data.frame(x=rnorm(1000, 5)), aes(x=x)) + geom_bar() + scale_y_reverse()
However now if I add any other geom_, such as another geom_bar() the scale for y is reversed. Is it possible to apply the scale_y_reverse() to only a specific geom_?
Another option is to make two separate plots and combine them with arrangeGrob from the gridExtra package. After playing with the plot margins, you can arrive at something that looks decent.
library(gridExtra)
library(ggplot2)
set.seed(100)
p2 <- ggplot(data.frame(x=rnorm(1000, 5)), aes(x=x)) + geom_bar() + theme(plot.margin=unit(c(0,0,0,0), 'lines'))
p1 <- p2 + scale_y_reverse() +
theme(plot.margin=unit(c(0, 0, -.8, 0), 'lines'), axis.title.x=element_blank(),
axis.text.x=element_blank(), axis.ticks.x=element_blank())
p <- arrangeGrob(p1, p2)
print(p)
ggplot only like to have one y-axis scale. The easiest thing would be to basically reshape your data yourself. Here we can use geom_rect to draw the data where ever we like and we can condition it on group time. Here's an example
#sample data
dd<-data.frame(
year=rep(2000:2014, 2),
group=rep(letters[1:2], each=15),
count=rpois(30, 20)
)
And now we can plot it. But first, let's define the offset to the top bars by finding the maxima height at a year and adding a bit of space
height <- ceiling(max(tapply(dd$count, dd$year, sum))*1.10)
And here's how we plot
ggplot(dd) +
geom_rect(aes(xmin=year-.4, xmax=year+.4,
ymin=ifelse(group=="a", 0, height-count),
ymax=ifelse(group=="a", count, height), fill=group)) +
scale_y_continuous(expand=c(0,0))
And that will give us

How to use scale from previous plot in current plot with ggplot2?

I am using ggplot2 to produce a plot that has 3 facets. Because I am comparing two different data sets, I would like to then be able to plot a second data set using the same y scale for the facets as in the first plot. However, I cannot find a simple way to save the settings of the first plot to then re-use them with the second plot. Since each facet has its own y scale, it will be a pain to specify them by hand for the second plot. Does anyone know of a quick way of re-using scales? To make this concrete, here is how I am generating first my plot:
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p + facet_wrap(~ cyl, scales = "free_y")
EDIT
When applying one of the suggestions below, I found out that my problem was more specific than described in the original post, and it had to do specifically with scaling of the error bars. Concretely, the error bars look weird when I rescale the second plot as suggested. Does anyone have any suggestions on how to keep the same scale for both plots and dtill display the error bars correctly? I am attaching example below for concreteness:
#Create sample data
d1 <- data.frame(fixtype=c('ff','ff','fp','fp'), detype=c('det','pro','det','pro'),
diffscore=c(-1,-15,3,-17),se=c(2,3,1,2))
d2 <- data.frame(fixtype=c('ff','ff','fp','fp'), detype=c('det','pro','det','pro'),
diffscore=c(-1,-3,-2,-1),se=c(4,3,5,3))
#Plot for data frame 1, this is the scale I want to keep
lim_d1 <- aes(ymax = diffscore + se, ymin=diffscore - se)
ggplot(d1, aes(colour=detype, y=diffscore, x=detype)) +
geom_point(aes(size=1), shape=15) +
geom_errorbar(lim_d1, width=0.2,size=1) +
facet_wrap(~fixtype, nrow=2, ncol=2, scales = "free_y")
#Plot for data frame 2 original scale
lim_d2 <- aes(ymax = diffscore + se, ymin=diffscore - se)
ggplot(d2, aes(colour=detype, y=diffscore, x=detype)) +
geom_point(aes(size=1), shape=15) +
geom_errorbar(lim_d2, width=0.2,size=1) +
facet_wrap(~fixtype, nrow=2, ncol=2, scales = "free_y")
#Plot for data frame 2 adjusted scale. This is where things go wrong!
#As suggested below, first I plot the first plot, then I draw a blank screen and try
#to plot the second data frame on top.
lim_d2 <- aes(ymax = diffscore + se, ymin=diffscore - se)
ggplot(d1, aes(colour=detype, y=diffscore, x=detype)) +
geom_blank() +
geom_point(data=d2, aes(size=1), shape=15) +
geom_errorbar(lim_d2, width=0.2,size=1) +
facet_wrap(~fixtype, nrow=2, ncol=2, scales = "free_y")
#If the error bars are fixed, by adding data=d2 to geom_errorbar(), then
#the error bars are displayed correctly but the scale gets distorted again
lim_d2 <- aes(ymax = diffscore + se, ymin=diffscore - se)
ggplot(d1, aes(colour=detype, y=diffscore, x=detype)) +
geom_blank() +
geom_point(data=d2, aes(size=1), shape=15) +
geom_errorbar(data=d2,lim_d2, width=0.2,size=1) +
facet_wrap(~fixtype, nrow=2, ncol=2, scales = "free_y")
You may first call ggplot on your original data where you add a geom_blank as a first layer. This sets up a plot area, with axes and legends based on the data provided in ggplot.
Then add geoms which use data other than the original data. In the example, I use a simple subset of the original data.
From ?geom_blank: "The blank geom draws nothing, but can be a useful way of ensuring common scales between different plots.".
ggplot(data = mtcars, aes(mpg, wt)) +
geom_blank() +
geom_point(data = subset(mtcars, wt < 3)) +
facet_wrap(~ cyl, scales = "free_y")
Here is an ugly hack that assumes you have an identical facetting layout in both plots.
It replaces the panel element of the ggplot build.
p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p1 <- p + facet_wrap(~ cyl, scales = "free_y") + labs(title = 'original')
# create "other" data.frame
n <- nrow(mtcars)
set.seed(201405)
mtcars2 <- mtcars[sample(seq_len(n ),n-15),]
# create this second plot
p2 <- p1 %+% mtcars2 + labs(title = 'new data')
# and a copy so we can attempt to fix
p3 <- p2 + labs(title = 'new data original scale')
# use ggplot_build to construct the plots for rendering
p1b <- ggplot_build(p1)
p3b <- ggplot_build(p3)
# replace the 'panel' information in plot 2 with that
# from plot 1
p3b[['panel']] <- p1b[['panel']]
# render the revised plot
# for comparison
library(gridExtra)
grid.arrange(p1 , p2, ggplot_gtable(p3b))

Resources