I am trying to combine two FACETED ggplot objects with coord_equal() using cowplot::plot_grid() or egg::ggarrange() and vertically align them.
The egg::ggarrange() approach works fine for UNFACETED plots, with the solution posted here.
However, the egg::ggarrange() solution breaks down when faceting is included. The plots are correctly aligned, but the units of the y-axes are twice as large as those of the x-axes. Any suggestions for how to generalize this for faceting?
dat1 <- data.frame(x = rep(1:10, 2), y = 1:20, z = rep(c("A", "B"), 10))
dat2 <- data.frame(x = 1:10, y = 1:10, z = rep(c("A", "B"), 5))
plot1 <- ggplot(dat1, aes(x=x, y=y)) +
geom_point() + coord_equal() + facet_wrap(~z)
plot2 <- ggplot(dat2, aes(x=x, y=y)) +
geom_point() + coord_equal() + facet_wrap(~z)
egg::ggarrange(plot1, plot2, ncol = 1)
it seems to be a simple fix,
library(egg)
b <- body(gtable_frame)
b[6] <- parse(text="if (fixed_ar) {
ar <- as.numeric(g$heights[tt[1]]) / as.numeric(g$widths[ll[1]])
height <- width * (ar / length(ll))
g$respect <- FALSE
}")
body(gtable_frame) <- b
assignInNamespace("gtable_frame", gtable_frame, ns = 'egg')
The main problem is that plot1 and plot2 have different aspect ratios.
This is plot1:
And this plot2:
You can try to keep the aspect ratio using, i.e. theme(aspect.ratio=1) instead of coord_equal():
require(ggplot2)
dat1 <- data.frame(x = rep(1:10, 2), y = 1:20, z = rep(c("A", "B"), 10))
dat2 <- data.frame(x = 1:10, y = 1:10, z = rep(c("A", "B"), 5))
plot1 <- ggplot(dat1, aes(x=x, y=y)) + geom_point() + theme(aspect.ratio=1)+
facet_wrap(~z)
plot2 <- ggplot(dat2, aes(x=x, y=y)) + geom_point() + theme(aspect.ratio=1)+
facet_wrap(~z)
egg::ggarrange(plot1, plot2, ncol = 1,heights = c(1,10))
Hope it serves.
Related
I would like to have a chart that connects missing points by a line, but not show a symbol at the missing point. Here is some code that creates some test data and produces a chart, but I would like a (id=B) green line joining points 4 and 6 with a straight line, but no green triangle at time 5. I did try geom_path instead of geom_line, but nothing much changed. Thanks
library(ggplot2)
set.seed(1)
x <- rep(seq(1:10), 3)
y <- c(runif(10), runif(10) + 1, runif(10) + 2)
group <- c(rep("A", 10), rep("B", 10), rep("C", 10))
df <- data.frame(cbind(group, x, y))
df$x <- as.numeric(df$x)
df$y <- as.numeric(df$y)
df[15,]$y <- NA
ggplot(data=df, aes(x=x, y=y, group=group)) +
geom_line(aes(colour=group))+
geom_point(aes(colour=group, shape=group))+
scale_shape_manual(values = c(1:3)) +
theme(legend.position="bottom")
You can do this by removing rows where y is NA:
df2 <- df[!is.na(df$y), ]
ggplot(data=df2, aes(x=x, y=y, group=group)) +
geom_line(aes(colour=group))+
geom_point(aes(colour=group, shape=group))+
scale_shape_manual(values = c(1:3)) +
theme(legend.position="bottom")
library(tidyverse)
# form some data
set.seed(1)
x <- rep(seq(1:10), 3)
y <- c(runif(10), runif(10) + 1, runif(10) + 2)
group <- c(rep("A", 10), rep("B", 10), rep("C", 10))
df <- tibble(group=group, x = x, y = y)
# add an NA
df[15,]$y <- NA
# plot with a gap where the NA is
p1 <- # p1 fails because of NA
ggplot(data=df, aes(x=x, y=y, group=group)) +
geom_line(aes(colour=group))+
geom_point(aes(colour=group, shape=group))+
scale_shape_manual(values = c(1:3)) +
theme(legend.position="bottom")
# plot with no gap
p2 <- p1 %+% {df %>% na.omit()} # p2 does what you want
p1
p2
I have made two figures in ggplot that I now want to stack on top of each other. I can get them to stack using grid.arrange(p1, p2, ncol =1) function in the gridExtra package however, both figures get horizontally stretched out becoming the shape of rectangles. Any idea on how to keep both figures square (x and y-axes the same overall length).
library(ggplot2)
library(gridExtra)
x1 <- rnorm(20)
y1 <- rnorm(20)
x2 <- rnorm(20)
y2 <- rnorm(20)
dat1 <- data.frame(x1, y1)
dat2 <- data.frame(x2, y2)
p1 <- ggplot(data = dat1, aes(x=x1, y=y1)) + geom_point()
p2 <- ggplot(data = dat2, aes(x=x2, y=y2)) + geom_point()
grid.arrange(p1,p2, ncol=1)
I have tried adjusting the width by using the widths argument but I keep getting the error message Error in arrangeGrob(...) : length(widths) == ncol is not TRUE.
grid.arrange(p1,p2, ncol=1, widths = c(1,1))
The patchwork package is built for this.
Plot1 + Plot2 # side by side
Plot1/Plot2 # top over bottom
I'm a big fan of patchwork for arranging plots. You get square plots by setting the dimensions as you export the figure.
library(patchwork)
p3 <- p1/p2 + plot_layout(ncol = 1, heights = c(1,1))
ggsave("test.png", p3, width = 10, height = 20, units = c("cm"))
Thanks for the responses but I actually figured it out using the packages I mentioned. What I had to do was add theme(aspect.ratio = 1) to both figures.
p1 <- ggplot(data = dat1, aes(x=x1, y=y1)) + geom_point() + theme(aspect.ratio = 1)
p2 <- ggplot(data = dat2, aes(x=x2, y=y2)) + geom_point() + theme(aspect.ratio = 1)
grid.arrange(p1,p2, ncol=1)
How about this:
library(ggplot2)
library(gridExtra)
x1 <- rnorm(20)
y1 <- rnorm(20)
x2 <- rnorm(20)
y2 <- rnorm(20)
dat1 <- data.frame(x1, y1)
dat2 <- data.frame(x2, y2)
p1 <- ggplot(data = dat1, aes(x=x1, y=y1)) + geom_point()
p2 <- ggplot(data = dat2, aes(x=x2, y=y2)) + geom_point()
grid.arrange(p1,p2, ncol=1, widths = unit(10, c("cm")), heights = c(unit(10, c("cm")), unit(10, c("cm"))))
I want that the two plots on top of each other have the same size. The problem is that the length of tick labels are different and therefore the plots are not aligned. Just making the font smaller is not a solution because it's not reader friendly. There must be a way to say how big a plot should be just within ggplot.
I just want to control the size of the plot manually regardless whether I have to bar plots or a bar plot and boxplot etc. How do I accomplish this?
library(ggplot2)
library(grid)
library(gridExtra)
df1 <- data.frame(a = as.factor(1:20), b = runif(20, 500000, 900000))
df2 <- data.frame(a = as.factor(1:20), c = rnorm(2000))
plot1 <- ggplot(df1, aes(x = a, y =b)) + geom_bar(stat = "identity")
plot2 <- ggplot(df2, aes(x = a, y =c)) + geom_boxplot()
grid.arrange(plot1,
plot2,
ncol = 1)
Here is my example:
As one of the comments mentioned, package cowplot has the functionality you need.
library(ggplot2)
library(cowplot)
df1 <- data.frame(a = as.factor(1:20), b = runif(20, 5, 9))
df2 <- data.frame(a = as.factor(1:20), b = runif(20, 50000, 90000))
plot1 <- ggplot(df1, aes(x = a, y =b)) + geom_bar(stat = "identity")
plot2 <- ggplot(df2, aes(x = a, y =b)) + geom_bar(stat = "identity")
#see ?plot_grid for more details
plot_grid(plot1,plot2,ncol = 1, align = "v", rel_heights = c(.7, .3))
Created on 2019-01-12 by the reprex package (v0.2.1)
Suppose I have the following chart
dat <- data.frame(x = 1:10, y = 1:10)
ggplot(dat, aes(x=x, y=y)) + geom_point()
but I'm not actually interested in the values 2.5, 5, 7.5, 10 on either axis. All I want to mark is something like "Low to High". I know I can mark + xlab("Low to High") but I would prefer "Low" to be at the far left of the axis (under the origin) and "High" to be at the far right (under 10.0) with perhaps an arrow from Low ---> High. I could specify the breaks manually, but that seems too much of a kludge.
Something like this might help,
dat <- data.frame(x = 1:10, y = 1:10)
p <- ggplot(dat, aes(x=x, y=y)) + geom_point() +
scale_x_continuous('', breaks=NULL)+
scale_y_continuous('', breaks=NULL)
g <- ggplotGrob(p)
library(gtable)
library(grid)
my_axis <- function(low="low", high="high", axis=c("x", "y"), ...){
axis <- match.arg(axis)
if(axis == "x"){
g1 <- textGrob(low, x=unit(0,"npc"), hjust=0)
g3 <- textGrob(high, x=unit(1,"npc"), hjust=1)
g2 <- segmentsGrob(grobWidth(g1) + unit(2,"mm"), unit(0.5,"npc"),
unit(1,"npc") - grobWidth(g3)- unit(2,"mm"),
unit(0.5,"npc"), ...)
} else if(axis == "y"){
g1 <- textGrob(low, y=unit(0,"npc"), rot=90, hjust=0)
g3 <- textGrob(high, y=unit(1,"npc"), rot=90, hjust=1)
g2 <- segmentsGrob(unit(0.5,"npc"),grobHeight(g1) + unit(2,"mm"),
unit(0.5,"npc"),
unit(1,"npc") - grobHeight(g3)- unit(2,"mm"),
...)
}
grobTree(g1,g2,g3)
}
g <- gtable_add_grob(g, my_axis(arrow=arrow(length=unit(2,"mm"))),
t=nrow(g)-2, b=nrow(g)-1, l=4)
g <- gtable_add_grob(g, my_axis(axis="y", arrow=arrow(length=unit(2,"mm"))),
t=3, l=1,r=3)
grid.newpage()
grid.draw(g)
As an alternative, and since you did ask for "Low ---> High", here's an option that doesn't involve disassembling the plot
dat <- data.frame(x = 1:10, y = 1:10)
low_to_high <- paste0("low ", paste0(rep("-", 35), collapse = ""), "> high")
library(ggplot2)
ggplot(dat, aes(x, y)) +
geom_point() +
labs(x = low_to_high, y = low_to_high) +
coord_equal() +
theme_bw() +
theme(axis.title = element_text(size=20),
axis.text = element_blank(),
axis.ticks = element_blank())
Admittedly, the number of - required will vary and needs to be adjusted per your plot size, and it's not as pretty as the properly rendered arrow. Still, this is quick to make from scratch.
I am new to ggplot2 and cannot figure out how to draw vertical dotted grey lines between
the points/dots along the x-axis. Here's my example code:
d1 <- runif(10,10,15)
d2 <- runif(10,25,30)
d3 <- rep(1:10,2)
df <- data.frame(x = d3, y = c(d1,d2))
ggplot(df, aes(x=x, y=y)) +
geom_point()
If your actual data is structured like the one used for your example, just add geom_line(aes(group = d3)) to the plot.
ggplot(df, aes(x=x, y=y)) +
geom_point() + geom_line(aes(group = d3))
There's definitely better ways than this but:
d1 <- runif(10,10,15)
d2 <- runif(10,25,30)
d3 <- rep(1:10,2)
df <- data.frame(x = d3, y = c(d1,d2))
df$place <- rep(c("min", "max") , each=10)
df_wide <- reshape(df, direction = "wide", v.names="y", timevar="place", idvar="x")
ggplot(df, aes(x=x, y=y)) +
geom_segment(aes(x=x, xend=x, y=y.min, yend=y.max),
size=1, data=df_wide, colour="grey70", linetype="dotted") +
geom_point()
Though I'm not sure what you mean by "along the x axis", maybe you want it to extend top to bottom not just between the points.
You should be using geom_vline() to do this.
d1 <- runif(10,10,15)
d2 <- runif(10,25,30)
d3 <- rep(1:10,2)
df <- data.frame(x = d3, y = c(d1,d2))
ggplot(df, aes(x=x, y=y)) + geom_point() +
geom_vline(xintercept = df$x, linetype= 3, colour = "#919191")