Ggplot2, Facet_Grid, Free Scales

ggplot2, facet_grid, free scales?

Perhaps it's because you have only one y axis, using your way. Did you try something like this?

mt + facet_grid(cyl ~ ., scales="free")

ggplot: create a facet grid with free scales

The dev/github version of ggh4x has facet_grid2() which allows these independent axes within a grid layout. (Disclaimer: I'm the author of ggh4x.) If you find any bugs or unclear documentation, feel free to leave an issue on the github since I don't think this has been field-tested a lot.

library(ggplot2)
library(ggh4x) # devtools::install_github("teunbrand/ggh4x")

# Create data
nX <- 3
nY <- 3
nVal <- 2
df <- data.frame(M = sort(rep(paste0("M",1:nX), nVal * nY)),
n = rep(sort(paste0("n",rep((1:nY)-1, nVal))),nX),
G = rep(1:2,nX * nY),
val = c(100,300,20,10,1,2,10,25,NA,NA,0.1,0.2,25,27,2,5,0.5,0.6))

# Delete observations to resemble my data
df <- subset(df, !is.na(val))

# scales does not work in facet_grid(), thus obscuring trends for low values
ggplot(df, aes(x = G,
y = val)) +
geom_line()+
ylim(0,NA)+
facet_grid2(n ~ M,scales = "free_y", independent = "y")

Sample Image

Created on 2021-06-09 by the reprex package (v1.0.0)

scales = free has no effect on facet_grid

facet_grid enforces all facet plots to have the same y axis per row and the same x axis per column. This is why it's called a grid. You have to use facet_wrap(Season ~ year, scales = "free") instead to allow having separate y axes for each facet.

Free space and scales in ggplot facets

You can use the independent argument in ggh4x::facet_grid2() to achieve this. Note that you cannot have both free axes and independent scales in the same dimension.

library(tidyverse)

sample_data <- data.frame(outcome = rep(letters[1:6], 2),
value = rep(c(1:2, 100*1:4), 2),
units = rep(c("days", "days", "pax", "pax", "pax", "pax"), 2),
year = rep(2020:2021, each = 6))

ggplot(sample_data, aes(x = value, y = outcome)) +
geom_col() +
ggh4x::facet_grid2(
units ~ year,
scales = "free", space = "free_y",
independent = "x"
)

Sample Image

Created on 2022-01-18 by the reprex package (v2.0.1)

Disclaimer: I wrote ggh4x.

Understanding facet_grid scale= free

Instead of facet_grid use facet_wrap for example,

facet_wrap(reformulate("Name","."), scales = 'free', nrow = 1) +

With facet_grid one can not get both x and y scales free; see here https://github.com/tidyverse/ggplot2/issues/1152

Scales = free works for facet_wrap but doesn't for facet_grid

Referring to this link:

facet_grid split the data into facets by one or two variables that vary on the horizontal and/or vertical direction, while facet_wrap places the facets next to each other, wrapping with a certain number of columns or rows. In other words, facet_wrap only has horizontal dimension.

Therefore, using the example from that link, sp + facet_grid(. ~ sex) would behave the same as sp + facet_grid( ~ sex). In your case, facet_grid(. ~ referencia) and facet_wrap( ~ referencia) should produce the same plot.

For two or more dimensional facets, facet_grid produces a grid of plots based on the parameter (vertical ~ horizontal). facet_wrap, on the other hand, would just stack the plots horizontally. User then can set the layout by specifying the number of columns or rows.

Now, when the scales = "free" argument is added, facets in facet_grid would still be bounded by the grid, therefore plots on the same row cannot have different y-axis. Similarly, there can only single x-axis for each column. Using facet_wrap though, each plot is displayed independently, so it can "free" its x-axis and y-axis.

In my opinion, facet_grid is useful when you want to relatively compare the plots within a category, which can be accomplished by setting the same axis scales. Meanwhile, facet_wrap is more useful for plots that more independent between one another.

Adjusting y axis limits in ggplot2 with facet and free scales

First, reproducibility with random data needs a seed. I started using set.seed(42), but that generated negative values which caused completely unrelated warnings. Being a little lazy, I changed the seed to set.seed(2021), finding all positives.

For #1, we can add limits=, where the help for ?scale_y_continuous says that

  limits: One of:

• 'NULL' to use the default scale range

• A numeric vector of length two providing limits of the
scale. Use 'NA' to refer to the existing minimum or
maximum

• A function that accepts the existing (automatic) limits
and returns new limits Note that setting limits on
positional scales will *remove* data outside of the
limits. If the purpose is to zoom, use the limit argument
in the coordinate system (see 'coord_cartesian()').

so we'll use c(0, NA).

For Q2, we'll add expand=, documented in the same place.

data %>%
gather(Gene, Levels, -Patient, -Treatment) %>%
mutate(Treatment = factor(Treatment, levels = c("Pre", "Post"))) %>%
mutate(Patient = as.factor(Patient)) %>%
ggplot(aes(x = Treatment, y = Levels, color = Patient, group = Patient)) +
geom_point() +
geom_line() +
facet_wrap(. ~ Gene, scales = "free") +
theme_bw() +
theme(panel.grid = element_blank()) +
scale_y_continuous(limits = c(0, NA), expand = expansion(mult = c(0, 0.1)))

Sample Image

Use free_y scale on first axis and fixed on second + facet_grid + ggplot2

Secondary axes are implemented in ggplot2 as a decoration that is a transformation of the primary axis, so I don't know an elegant way to do this, since it would require the secondary axis formula to be aware of different scaling factors for each facet.

Here's a hacky approach where I scale each secondary series to its respective primary series, and then add some manual annotations for the secondary series. Another way might be to make the plots separately for each facet like here and use patchwork to combine them.

Given some fake data where the facets have different ranges for the primary series but the same range for the secondary series:

library(tidyverse)
fake <- tibble(facet = rep(1:3, each = 10),
x = rep(1:10, times = 3),
y_prim = (1+sin(x))*facet/2,
y_sec = (1 + sin(x*3))/2)

ggplot(fake, aes(x, y_prim)) +
geom_line() +
geom_line(aes(y= y_sec), color = "green") +
facet_wrap(~facet, ncol = 1)

Sample Image

...we could scale each secondary series to its primary series, and add custom annotations for that secondary series:

fake2 <- fake %>%
group_by(facet) %>%
mutate(y_sec_scaled = y_sec/max(y_sec) * (max(y_prim))) %>%
ungroup()

fake2_labels <- fake %>%
group_by(facet) %>%
summarize(max_prim = max(y_prim), baseline = 0, x_val = 10.5)

ggplot(fake2, aes(x, y_prim)) +
geom_line() +
geom_line(aes(y= y_sec_scaled), color = "green") +
facet_wrap(~facet, ncol = 1, scales = "free_y") +
geom_text(data = fake2_labels, aes(x = x_val, y = max_prim, label = "100%"),
hjust = 0, color = "green") +
geom_text(data = fake2_labels, aes(x = x_val, y = baseline, label = "0%"),
hjust = 0, color = "green") +
coord_cartesian(xlim = c(0, 10), clip = "off") +
theme(plot.margin = unit(c(1,3,1,1), "lines"))

Sample Image

Facet_grid with all panels with a free_y

One suggestion is to use ggh4x::facet_grid2() to set independent y scales. (Disclaimer: I'm the author of ggh4x). Example with standard dataset below:

library(ggplot2)

ggplot(mpg, aes(displ, hwy, colour = as.factor(cyl))) +
geom_point() +
ggh4x::facet_grid2(vars(year), vars(drv),
scales = "free_y", independent = "y")

Sample Image

Created on 2022-03-11 by the reprex package (v0.3.0)

ggplot 2 facet_grid free_y but forcing Y axis to be rounded to nearest whole number

Given that breaks in scales can take a function, I would imagine that you could wrap the basic breaking algorithm in a function that doesn't allow non-integers.

Start with an example:

ggplot(mtcars, aes(wt, mpg)) + 
geom_point() +
facet_grid(am+cyl~., scales="free_y")

Sample Image

Looking at how scales::pretty_breaks is put together, make a function that wraps it and only allows integer breaks through:

library("scales")
integer_breaks <- function(n = 5, ...) {
breaker <- pretty_breaks(n, ...)
function(x) {
breaks <- breaker(x)
breaks[breaks == floor(breaks)]
}
}

Now use the function this returns as the breaks function in the scale

ggplot(mtcars, aes(wt, mpg)) + 
geom_point() +
facet_grid(am+cyl~., scales="free_y") +
scale_y_continuous(breaks = integer_breaks())

Sample Image



Related Topics



Leave a reply



Submit