Align x axis with grid.arrange - r

I'm trying to plot two aligned graphics, but one of them has a label and of them doesn't.
Example:
library(dplyr)
library(ggplot2)
df <-
seq(as.Date("2019-01-01"), as.Date("2019-01-31"), by = 1) %>%
as_tibble() %>%
rename(day = value) %>%
mutate(
x = seq(1, 31, by = 1),
y = x * 2 - 20
)
p1 <-
df %>%
gather(key, value, c(x, y)) %>%
ggplot(aes(x = day, y = value, color = key)) +
geom_line(size = 1)
p2 <-
df %>%
ggplot(aes(x = day, y = y / x)) +
geom_line(size = 1)
grid.arrange(
p1, p2
)
Result:
Is there a way to align the axis without using facet_wrap? (I want to add specific label formatters for each plot because they are in different units and facet_wrap doesn't allow me to do that as far as I know)

You can manage them as different plots, with same legend, using cowplot package:
library(cowplot)
legend <- get_legend(p1) # get the legend of the first one plot
# here the plots in a grid
prow <- plot_grid( p1 + theme(legend.position="none"),
# here you add the percentage
p2 + theme(legend.position="none")+ scale_y_continuous(labels = scales::percent),
align = 'v',
labels = c("A", "B"),
hjust = -1,
nrow = 2)
# here you add the legend
p <- plot_grid( prow, legend, rel_widths = c(3, .3))
p

Related

How to add density plot per component in PCA plot in R?

I would like to know how add density to PCA plot .
This is my basic example which i would like to begin
x <- iris[1:4]
pc <- prcomp(x)
df <- cbind(pc$x[,1:2], iris[,5]) %>% as.data.frame()
df$PC1 <- as.numeric(df$PC1) / (pc$sdev[1] * sqrt(nrow(iris)))
df$PC2 <- as.numeric(df$PC2) / (pc$sdev[2] * sqrt(nrow(iris)))
df$V3 <- as.factor(df$V3)
#ggplot method
p1 <- ggplot(df, aes(PC1, PC2, colour = V3)) +
geom_point(size = 3, aes(shape = V3)) +
stat_ellipse(geom = "polygon", aes(fill = after_scale(alpha(colour, 0))),
data = df[df$V3 == "1" | df$V3 == "2",], size = 1)
p1
Now
I would like to add similar to my plot too how to do that? Any suggestion or help would be really appreciated.
You could use the package cowplot by using insert_*axis_grob to insert two geom_density plots at the top x-axis and right y-axis. For the top x-axis density curve you can use the values of PC1 and for the right y-axis density curve you can use the values of PC2 and both color and fill them with V3. Make sure to specify the right axis for both graphs. Here is a reproducible example:
library(cowplot)
library(dplyr)
library(ggplot2)
x <- iris[1:4]
pc <- prcomp(x)
df <- cbind(pc$x[,1:2], iris[,5]) %>% as.data.frame()
df$PC1 <- as.numeric(df$PC1) / (pc$sdev[1] * sqrt(nrow(iris)))
df$PC2 <- as.numeric(df$PC2) / (pc$sdev[2] * sqrt(nrow(iris)))
df$V3 <- as.factor(df$V3)
# plot
p1 <- ggplot(df, aes(PC1, PC2, colour = V3)) +
geom_point(size = 3, aes(shape = V3)) +
stat_ellipse(geom = "polygon", aes(fill = after_scale(alpha(colour, 0))),
data = df[df$V3 == "1" | df$V3 == "2",], size = 1)
# Add density curves to y and x axis
xdens <-
axis_canvas(p1, axis = "x") +
geom_density(data = df, aes(x = PC1, fill = V3, colour = V3), alpha = 0.3)
ydens <-
axis_canvas(p1, axis = "y", coord_flip = TRUE) +
geom_density(data = df, aes(x = PC2, fill = V3, colour = V3), alpha = 0.3) +
coord_flip()
p1 %>%
insert_xaxis_grob(xdens, grid::unit(1, "in"), position = "top") %>%
insert_yaxis_grob(ydens, grid::unit(1, "in"), position = "right") %>%
ggdraw()
Created on 2022-08-31 with reprex v2.0.2

Make text labels span multiple ggplot facet grid panes

How can geom_text_repel() labels be made to span multiple facet_grid() panes? For instance, if there are many long labels that do not fit within the proper dimensions of each grid plot, the label should be positioned as if the facet_grid() were a single plot.
For example:
df <- data.frame(
x = rep(1:3, 5),
y = runif(15),
label = paste0("very long label ", 1:15),
group = do.call(c, lapply(paste0("group ", 1:5), function(x) rep(x, 3)))
)
library(ggplot2)
library(ggrepel)
ggplot(df, aes(x, y, label = label)) +
geom_point() +
facet_grid(cols = vars(group)) +
geom_text_repel()
If there is another way to group samples on the x-axis that would mimic a column-wise facet-grid, that's perfectly fine too. In my case, I need to group samples by a grouping variable (correspondingly labeled), and then within each group order them by a continuous variable.
Use clip = "off" from coord_cartesian:
library(ggplot2)
library(ggrepel)
ggplot(df, aes(x, y, label = label)) +
geom_point() +
facet_grid(cols = vars(group)) +
geom_text_repel() +
coord_cartesian(clip = "off")
If this is not enough, one other option is to use multilining with stringr::str_wrap:
library(stringr)
library(dplyr)
df %>%
mutate(label_wrapped = str_wrap(label, width = 20)) %>%
ggplot(aes(x, y, label = label_wrapped)) +
geom_point() +
facet_grid(cols = vars(group)) +
geom_text_repel() +
coord_cartesian(clip = 'off')
data
set.seed(2)
df <- data.frame(
x = rep(1:3, 5),
y = runif(15),
label = paste0("very very very long label ", 1:15),
group = do.call(c, lapply(paste0("group ", 1:5), function(x) rep(x, 3)))
)

How to change the position of y axis label to be directly on top of the y axis?

I am having trouble placing the y-axis label directly above the y-axis.
I've tried using the margins argument to guide the label. I get the right and left adjustment correctly adjusted, but I cant get the label further to the top of the graph specifying the argument t = xx in the margins.
The code produces the graph below. As you can see the y-axis-label need to be adjusted further upwards b/c I want it to be directly on top of the y-axis.
Cheers,
library(tidyverse)
housing <- txhousing %>% group_by(year, city) %>%
summarise(total = sum(volume, na.rm = T)) %>% filter(city %in% c("El Paso","Dallas", "Houston"))
dat <- housing
yvar <- dat$total
xvar <- dat$year
gruppe <- dat$city
ggplot(data = dat, aes(x = xvar, y = yvar/1e6, colour = gruppe)) + geom_line() + theme_classic() + theme(plot.margin = margin(20,0,0,0), axis.title.y = element_text(angle = 0, margin = margin(t = -20, l = 10, r = -40))) + labs(y = "y-label")
You could pretend like it's just any old text and place it wherever you like.
Fiddle with hjust, vjust, ymin or xmin to get the label exactly where you want it.
library(tidyverse)
library(ggplot2)
library(grid) #grobs come from grid
housing <- txhousing %>% group_by(year, city) %>%
summarise(total = sum(volume, na.rm = T)) %>% filter(city %in% c("El Paso","Dallas", "Houston"))
dat <- housing
yvar <- dat$total
xvar <- dat$year
gruppe <- dat$city
p<-ggplot(data = dat, aes(x = xvar, y = yvar/1e6, colour = gruppe)) +
geom_line() + theme_classic() +
theme(plot.margin = margin(50,0,0,0))+
annotation_custom(
grob = textGrob(label = "y-label", hjust = 0, vjust=-0.9,gp = gpar(cex = 1.0)),
ymin = (max(yvar/1e6)),
xmin = min(xvar)-(0.009*min(xvar)))+
labs(y = NULL)
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip[gt$layout$name == "panel"] <- "off" #this lets you put stuff outside the margins
grid.draw(gt)

create two separate ggplots with comparable y axis but with different y limits

I am trying to plot bar graphs on a map following this example: How to plot barchart onto ggplot2 map. This works fine but in contrast to the example I would like to add x and y axis. The problem is that the y range of the data differs considerably across the regions for which I would like to create the bar graphs.
In order to produce comparable graphs (i.e. where the y axis has an identical dimension), I apply the same y limits to all graphs and adjust the breaks for each graph. This is, however, not a good solution as some of the graphs (p2 in this case) have a large empty area above and below the bars. In order to make it work, I am looking for a method top crop the graphs to remove the empty areas, while at the same time maintaining the dimensions of the graph so that y axis can be compared.
library(dplyr)
library(ggplot2)
df <- data.frame(type = c("A", "B", "A", "B"), country = c("NLD", "NLD", "BEL", "BEL"), value = c(10, -10, 5, 2))
df1 <- filter(df, country == "NLD")
p1 <- ggplot(data = df1) +
geom_col(aes(x = type, y = value)) +
scale_y_continuous(limits = c(min(df$value), max(df$value)), breaks = seq(min(df1$value), max(df1$value), 2)) +
theme_bw()
p1
df2 <- filter(df, country == "BEL")
p2 <- ggplot(data = df2) +
geom_col(aes(x = type, y = value)) +
scale_y_continuous(limits = c(min(df$value), max(df$value)), breaks = seq(min(df2$value), max(df2$value), 2)) +
theme_bw()
p2
Have you tried coord_fixed for each of the plot? If you can control the width of each plot the same, the height of bars will be comparable.
library(dplyr)
library(ggplot2)
library(gridExtra)
df <- data.frame(type = c("A", "B", "A", "B"), country = c("NLD", "NLD", "BEL", "BEL"), value = c(10, -10, 5, 2))
df1 <- filter(df, country == "NLD")
p1 <- ggplot(data = df1) +
geom_col(aes(x = type, y = value)) +
# scale_y_continuous(limits = c(min(df$value), max(df$value)), breaks = seq(min(df1$value), max(df1$value), 2)) +
theme_bw()
df2 <- filter(df, country == "BEL")
p2 <- ggplot(data = df2) +
geom_col(aes(x = type, y = value)) +
# scale_y_continuous(limits = c(min(df$value), max(df$value)), breaks = seq(min(df2$value), max(df2$value), 2)) +
theme_bw()
x_range <- length(unique(df$type))
y_range1 <- max(df1$value) - min(df1$value)
y_range2 <- max(df2$value) - min(df2$value)
g1 <- p1 + coord_fixed(ratio = x_range / y_range1)
g2 <- p2 + coord_fixed(ratio = x_range / y_range1)
# example output
grid.arrange(g1, g2, nrow = 1)

R: grid.arrange marginal plots to ggplot2 "heatmap" (geom_tile)

I want to add two bar charts to the top and right of a heatmap representing the marginal distributions along the two dimensions of the bivariate distribution that the heatmap represents.
Here is some code:
library(gridExtra)
library(ggExtra)
library(cowplot)
# generate some data
df_hm = cbind(
expand.grid(
rows = sample(letters, 10),
cols = sample(LETTERS, 10)
),
value = rnorm(100)
)
# plot the heatmap
gg_hm = df_hm %>%
ggplot(aes(x = rows, y = cols, fill = value)) +
geom_tile() +
theme(legend.position = "bottom")
gg_rows = df_hm %>%
group_by(rows) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = rows,y = value)) +
geom_bar(stat = "identity", position = "dodge")
gg_cols = df_hm %>%
group_by(cols) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = cols, y = value))+
geom_bar(stat = "identity", position = "dodge") +
coord_flip()
gg_empty = df_hm %>%
ggplot(aes(x = cols, y = value)) +
geom_blank() +
theme(axis.text = element_blank(),
axis.title = element_blank(),
line = element_blank(),
panel.background = element_blank())
# try this with grid.arrange
grid.arrange(gg_rows, gg_empty, gg_hm, gg_cols,
ncol = 2, nrow = 2, widths = c(3, 1), heights = c(1, 3))
which produces this:
What I want to be able to do is to move the graphs to align as indicated by the red arrows:
- the y-axis of (1, 1) should line up with the y-axis of (2, 1)
- the x-axis of (2, 1) should line up with the x-axis of (2, 2)
I tried the accepted answer by renato vitolo and the alignments didn't work on my machine. But I subsequently discoverd a much easier solution: the egg package (available on CRAN). egg provides a version of grid.arrange called ggarrange which takes similar arguments but aligns the axes nicely. In the OP's code I just had to add library(egg), library(dplyr), and then replace grid.arrange with ggarrange (having installed egg with install.packages("egg")).
Full code:
library(gridExtra)
library(cowplot)
library(egg)
library(dplyr)
# generate some data
df_hm = cbind(
expand.grid(
rows = sample(letters, 10),
cols = sample(LETTERS, 10)
),
value = rnorm(100)
)
# plot the heatmap
gg_hm = df_hm %>%
ggplot(aes(x = rows, y = cols, fill = value)) +
geom_tile() +
theme(legend.position = "bottom")
gg_rows = df_hm %>%
group_by(rows) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = rows,y = value)) +
geom_bar(stat = "identity", position = "dodge")
gg_cols = df_hm %>%
group_by(cols) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = cols, y = value))+
geom_bar(stat = "identity", position = "dodge") +
coord_flip()
gg_empty = df_hm %>%
ggplot(aes(x = cols, y = value)) +
geom_blank() +
theme(axis.text = element_blank(),
axis.title = element_blank(),
line = element_blank(),
panel.background = element_blank())
ggarrange(
gg_rows, gg_empty, gg_hm, gg_cols,
nrow = 2, ncol = 2, widths = c(3, 1), heights = c(1, 3)
)
Output:
gtable is extremely useful. scales provides tools to format the axis ticks, to achieve alignment between the text y.ticks of the heatmap (X, F, ...) and the numeric y.ticks of the barplot on top, by formatting the former to a fixed width of 5 chars (to be adapted for your specific barplot).
require(ggplot2)
require(gtable)
require(grid)
library(dplyr)
library(scales)
## To format heatmap y.ticks with appropriate width (5 chars),
## to align with gg_rows y.tics
ytickform <- function(x){
lab <- sprintf("%05s",x)
}
set.seed(123)
## generate some data
df_hm = cbind(
expand.grid(
rows = sample(letters, 10),
cols = sample(LETTERS, 10)
),
value = rnorm(100)
)
# plot the heatmap
gg_hm = df_hm %>%
ggplot(aes(x = rows, y = cols, fill = value)) +
geom_tile() +
scale_y_discrete(label=ytickform) +
theme(legend.position = "bottom",
plot.margin = unit(c(3,3,3,3), "mm"))
gg_rows = df_hm %>%
group_by(rows) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = rows,y = value)) +
geom_bar(stat = "identity", position = "dodge") +
theme(plot.margin = unit(c(3,3,3,3), "mm"))
gg_cols = df_hm %>%
group_by(cols) %>%
summarize(value = mean(value)) %>%
ggplot(aes(x = cols, y = value))+
geom_bar(stat = "identity", position = "dodge") +
coord_flip() +
theme(plot.margin = unit(c(3,3,3,3), "mm"))
## extract legend from heatmap
g <- ggplotGrob(gg_hm)$grobs
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
## plot heatmap without legend
g <- ggplotGrob(gg_hm + theme(legend.position="none"))
## add column and put column barplot within
g <- gtable_add_cols(g, unit(5,"cm"))
g <- gtable_add_grob(g, ggplotGrob(gg_cols),
t = 1, l=ncol(g), b=nrow(g), r=ncol(g))
## add row and put legend within
g <- gtable_add_rows(g, unit(1,"cm"))
g <- gtable_add_grob(g, legend,
t = nrow(g), l=1, b=nrow(g), r=ncol(g)-1)
## add row on top and put row barplot within
g <- gtable_add_rows(g, unit(5,"cm"), 0)
g <- gtable_add_grob(g, ggplotGrob(gg_rows),
t = 1, l=1, b=1, r=5)
grid.newpage()
grid.draw(g)
References:
Align ggplot2 plots vertically
http://www.cookbook-r.com/Graphs/Axes_(ggplot2)/#tick-mark-label-text-formatters
https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs

Resources