facet_wrap equal axis per panel - r

I would like to make a plot using facet_wrap where the axes can vary for each panel but within a panel the x and y axes should be the same scale.
e.g. see the following plots
df <- read.table(text = "
x y g
1 5 a
2 6 a
3 7 a
4 8 a
5 9 b
6 10 b
7 11 b
8 12 b", header = TRUE)
library(ggplot2)
ggplot(df, aes(x=x,y=y,g=g)) +
geom_point() +
facet_wrap(~ g) # all axes 1-12
ggplot(df, aes(x=x,y=y,g=g)) +
geom_point() +
facet_wrap(~ g, scales = "free")
# fee axes, y & y axes don't match per panel
What i want is for panel a the x and why axes both to be 1-8 and for panel b the x and y axes both to range from 5 - 12.
Is this possible?

Using this answer you could try the following:
dummy <- data.frame(x = c(1, 8, 5, 12), y = c(1, 8, 5, 12), g = c("a", "a", "b", "b"))
ggplot(df, aes(x=x,y=y)) +
geom_point() +
facet_wrap(~ g, scales = "free") +
geom_blank(data = dummy)

Another solution is trick the axes for individual facet_wrap() plots by adding invisible points to the plots with x and y reversed so that the plotted data is "square", e.g.,
library(ggplot2)
p <- ggplot(data = df) +
geom_point(mapping = aes(x = x, y = y)) +
geom_point(mapping = aes(x = y, y = x), alpha = 0) +
facet_wrap( ~ g, scales = "free")
print(p)
You could also use geom_blank(). You don't need dummy data.

This wasn't an option when the question was asked, but these days I would highly recommend patchwork for combining plots.

Related

r ggplot - change x-axis tick labels without losing geom_smooth stats

I make a scatter plot and fit a line on it; now I need to change the tick-level labels of my x-axis, which is continuous. The data:
y <- data.frame(a = 1:5, b = 5:9)
> y
a b
1 1 5
2 2 6
3 3 7
4 4 8
5 5 9
And I plot it:
ggplot(y, aes(a, b))+
geom_jitter()+
stat_summary(fun.data = mean_cl_normal)+
geom_smooth(method = 'lm', formula = y~x)
This is not bad, but I need to change the labels at the x-axis tick level, to make them meaningful, so I try with this:
ggplot(y, aes(a, b))+
geom_jitter()+
stat_summary(fun.data = mean_cl_normal)+
geom_smooth(method = 'lm', formula = y~x)+
scale_x_discrete("A_Variable", c("a","b","c","d","f"))
It doesn't work. I learn from another SO question that I need to change the x variable to factor, but when I do this, I lose my geom_smooth line. How do I change the tick-level labels of my x-axis without losing my geom_smooth stats?
You have to use the scale_x_continuous, since your a is a continuous variable. In order to define names for the breaks, you can use the labels parameter.
data.frame(a = 1:5, b = 5:9) %>%
ggplot(aes(a, b))+
geom_jitter()+
stat_summary(fun.data = mean_cl_normal)+
geom_smooth(method = 'lm', formula = y~x)+
scale_x_continuous(breaks=1:5, labels=c("a", "b", "c", "d", "f")) +
labs(x="A_Variable", y="B_Variable", title="A plot")

in R: create scatter plots using ggplot2 inside a for-loop inside a function, differ continuous/discrete variables [duplicate]

I want to use ggplot to loop over several columns to create multiple plots, but using the placeholder in the for loop changes the behavior of ggplot.
If I have this:
t <- data.frame(w = c(1, 2, 3, 4), x = c(23,45,23, 34),
y = c(23,34,54, 23), z = c(23,12,54, 32))
This works fine:
ggplot(data=t, aes(w, x)) + geom_line()
But this does not:
i <- 'x'
ggplot(data=t, aes(w, i)) + geom_line()
Which is a problem if I want to eventually loop over x, y and z.
Any help?
You just need to use aes_string instead of aes, like this:
ggplot(data=t, aes_string(x = "w", y = i)) + geom_line()
Note that w then needs to be specified as a string, too.
ggplot2 > 3.0.0 supports tidy evaluation pronoun .data. So we can do the following:
Build a function that takes x- & y- column names as inputs. Note the use of .data[[]].
Then loop through every column using purrr::map.
library(rlang)
library(tidyverse)
dt <- data.frame(
w = c(1, 2, 3, 4), x = c(23, 45, 23, 34),
y = c(23, 34, 54, 23), z = c(23, 12, 54, 32)
)
Define a function that accept strings as input
plot_for_loop <- function(df, x_var, y_var) {
ggplot(df, aes(x = .data[[x_var]], y = .data[[y_var]])) +
geom_point() +
geom_line() +
labs(x = x_var, y = y_var) +
theme_classic(base_size = 12)
}
Loop through every column
plot_list <- colnames(dt)[-1] %>%
map( ~ plot_for_loop(dt, colnames(dt)[1], .x))
# view all plots individually (not shown)
plot_list
# Combine all plots
library(cowplot)
plot_grid(plotlist = plot_list,
ncol = 3)
Edit: the above function can also be written w/ rlang::sym & !! (bang bang).
plot_for_loop2 <- function(df, .x_var, .y_var) {
# convert strings to variable
x_var <- sym(.x_var)
y_var <- sym(.y_var)
# unquote variables using !!
ggplot(df, aes(x = !! x_var, y = !! y_var)) +
geom_point() +
geom_line() +
labs(x = x_var, y = y_var) +
theme_classic(base_size = 12)
}
Or we can just use facet_grid/facet_wrap after convert the data frame from wide to long format (tidyr::gather)
dt_long <- dt %>%
tidyr::gather(key, value, -w)
dt_long
#> w key value
#> 1 1 x 23
#> 2 2 x 45
#> 3 3 x 23
#> 4 4 x 34
#> 5 1 y 23
#> 6 2 y 34
#> 7 3 y 54
#> 8 4 y 23
#> 9 1 z 23
#> 10 2 z 12
#> 11 3 z 54
#> 12 4 z 32
### facet_grid
ggp1 <- ggplot(dt_long,
aes(x = w, y = value, color = key, group = key)) +
facet_grid(. ~ key, scales = "free", space = "free") +
geom_point() +
geom_line() +
theme_bw(base_size = 14)
ggp1
### facet_wrap
ggp2 <- ggplot(dt_long,
aes(x = w, y = value, color = key, group = key)) +
facet_wrap(. ~ key, nrow = 2, ncol = 2) +
geom_point() +
geom_line() +
theme_bw(base_size = 14)
ggp2
### bonus: reposition legend
# https://cran.r-project.org/web/packages/lemon/vignettes/legends.html
library(lemon)
reposition_legend(ggp2 + theme(legend.direction = 'horizontal'),
'center', panel = 'panel-2-2')
The problem is how you access the data frame t. As you probably know, there are several ways of doing so but unfortunately using a character is obviously not one of them in ggplot.
One way that could work is using the numerical position of the column in your example, e.g., you could try i <- 2. However, if this works rests on ggplot which I have never used (but I know other work by Hadley and I guess it should work)
Another way of circumventing this is by creating a new temporary data frame every time you call ggplot. e.g.:
tmp <- data.frame(a = t[['w']], b = t[[i]])
ggplot(data=tmp, aes(a, b)) + geom_line()
Depending on what you are trying to do, I find facet_wrap or facet_grid to work well for creating multiple plots with the same basic structure. Something like this should get you in the right ballpark:
t.m = melt(t, id="w")
ggplot(t.m, aes(w, value)) + facet_wrap(~ variable) + geom_line()

Remove Factors with no data in facet grouping variable

I have the following data :
data <- data.frame(x = letters[1:6],
group = rep(letters[1:2], each = 3),
y = 1:6)
x group y
1 a a 1
2 b a 2
3 c a 3
4 d b 4
5 e b 5
6 f b 6
And I would like to plot y ~ x and split into facets by groups with ggplot2.
ggplot(data, aes(x, y)) +
geom_bar(stat = "identity") +
facet_grid(group ~ .)
The problem is that some tuples (x; group) don't exist in my data(for example there is no data for x = a && group = b) , but they are kept in the x-axis of both facets so I would like to remove them and then remove white spaces in the facets when factors are missing in respective groups.
I thought scales = "free_x" or drop = TRUE could do the trick but I couldn't manage to do it.
Any help would be appreciated, Thanks !
Use facet_wrap instead
ggplot(data, aes(x, y)) +
geom_col() +
facet_wrap(~group, scales = 'free', nrow = 2, strip.position = 'right')
also note geom_col as an alternative to using identity

Level-dependent axis vales using facet_wrap [duplicate]

I am trying to figure out a neat way to remove unused factors from a facet in ggplot2. Here is a minimal example
# DUMMY DATA
mydf = data.frame(
x = rpois(6, 25),
y = LETTERS[1:6],
cat = c(rep('AA', 3), rep('BB', 3)))
# PLOT IT!
p0 = ggplot(mydf, aes(x = x, y = y)) +
geom_point() +
facet_wrap(~ cat, ncol = 1)
From the plot below, you can see that factors D, E and F are plotted in facet AA despite the fact that there is no corresponding data. What I want is for a way to eliminate {D, E, F} from facet AA and similarly {A, B, C} from facet BB.
Is there a neat way to do this, or even a hack would be acceptable.
I think all you need is scales = "free_y":
p0 = ggplot(mydf, aes(x = x, y = y)) +
geom_point() +
facet_wrap(~ cat, ncol = 1,scales = "free_y")
p0

Looping over variables in ggplot

I want to use ggplot to loop over several columns to create multiple plots, but using the placeholder in the for loop changes the behavior of ggplot.
If I have this:
t <- data.frame(w = c(1, 2, 3, 4), x = c(23,45,23, 34),
y = c(23,34,54, 23), z = c(23,12,54, 32))
This works fine:
ggplot(data=t, aes(w, x)) + geom_line()
But this does not:
i <- 'x'
ggplot(data=t, aes(w, i)) + geom_line()
Which is a problem if I want to eventually loop over x, y and z.
Any help?
You just need to use aes_string instead of aes, like this:
ggplot(data=t, aes_string(x = "w", y = i)) + geom_line()
Note that w then needs to be specified as a string, too.
ggplot2 > 3.0.0 supports tidy evaluation pronoun .data. So we can do the following:
Build a function that takes x- & y- column names as inputs. Note the use of .data[[]].
Then loop through every column using purrr::map.
library(rlang)
library(tidyverse)
dt <- data.frame(
w = c(1, 2, 3, 4), x = c(23, 45, 23, 34),
y = c(23, 34, 54, 23), z = c(23, 12, 54, 32)
)
Define a function that accept strings as input
plot_for_loop <- function(df, x_var, y_var) {
ggplot(df, aes(x = .data[[x_var]], y = .data[[y_var]])) +
geom_point() +
geom_line() +
labs(x = x_var, y = y_var) +
theme_classic(base_size = 12)
}
Loop through every column
plot_list <- colnames(dt)[-1] %>%
map( ~ plot_for_loop(dt, colnames(dt)[1], .x))
# view all plots individually (not shown)
plot_list
# Combine all plots
library(cowplot)
plot_grid(plotlist = plot_list,
ncol = 3)
Edit: the above function can also be written w/ rlang::sym & !! (bang bang).
plot_for_loop2 <- function(df, .x_var, .y_var) {
# convert strings to variable
x_var <- sym(.x_var)
y_var <- sym(.y_var)
# unquote variables using !!
ggplot(df, aes(x = !! x_var, y = !! y_var)) +
geom_point() +
geom_line() +
labs(x = x_var, y = y_var) +
theme_classic(base_size = 12)
}
Or we can just use facet_grid/facet_wrap after convert the data frame from wide to long format (tidyr::gather)
dt_long <- dt %>%
tidyr::gather(key, value, -w)
dt_long
#> w key value
#> 1 1 x 23
#> 2 2 x 45
#> 3 3 x 23
#> 4 4 x 34
#> 5 1 y 23
#> 6 2 y 34
#> 7 3 y 54
#> 8 4 y 23
#> 9 1 z 23
#> 10 2 z 12
#> 11 3 z 54
#> 12 4 z 32
### facet_grid
ggp1 <- ggplot(dt_long,
aes(x = w, y = value, color = key, group = key)) +
facet_grid(. ~ key, scales = "free", space = "free") +
geom_point() +
geom_line() +
theme_bw(base_size = 14)
ggp1
### facet_wrap
ggp2 <- ggplot(dt_long,
aes(x = w, y = value, color = key, group = key)) +
facet_wrap(. ~ key, nrow = 2, ncol = 2) +
geom_point() +
geom_line() +
theme_bw(base_size = 14)
ggp2
### bonus: reposition legend
# https://cran.r-project.org/web/packages/lemon/vignettes/legends.html
library(lemon)
reposition_legend(ggp2 + theme(legend.direction = 'horizontal'),
'center', panel = 'panel-2-2')
The problem is how you access the data frame t. As you probably know, there are several ways of doing so but unfortunately using a character is obviously not one of them in ggplot.
One way that could work is using the numerical position of the column in your example, e.g., you could try i <- 2. However, if this works rests on ggplot which I have never used (but I know other work by Hadley and I guess it should work)
Another way of circumventing this is by creating a new temporary data frame every time you call ggplot. e.g.:
tmp <- data.frame(a = t[['w']], b = t[[i]])
ggplot(data=tmp, aes(a, b)) + geom_line()
Depending on what you are trying to do, I find facet_wrap or facet_grid to work well for creating multiple plots with the same basic structure. Something like this should get you in the right ballpark:
t.m = melt(t, id="w")
ggplot(t.m, aes(w, value)) + facet_wrap(~ variable) + geom_line()

Resources