2
votes

Problem

I am trying to understand how to provide y-axis breaks manually to facets, generated by the facet_wrap() function in ggplot2 in R.

Minimal reproducible example

Disclaimer: I have borrowed the following example from the R studio community

With the following code, you can specify the y-axis breaks based on the y-axis values in de dataset.

library(ggplot2)
diamonds2 <- subset(diamonds, !(cut == "Fair" & price > 5000))

my_breaks <- function(x) { if (max(x) < 6000) seq(0, 5000, 1000) else seq(0, 15000, 5000) }

ggplot(data = diamonds2, aes(x = carat, y = price)) +
  facet_wrap(~ cut, scales = "free_y") +
  geom_point() +
  scale_y_continuous(breaks = my_breaks)

enter image description here

Question

I would like to be able to specify the breaks manually, based on the facet (in this case: based on the 'cut' of the diamonds). For example, I would like to set the breaks for the 'Fair' cut to seq(1000, 2500, 5000), the breaks for the 'Good' cut to seq(1500, 3000, 4500, 6000, ..., 15000) and the rest to seq(0,15000, 5000).

Attempt

I thought that adapting the my_breaks function with an if-else-ladder, specifying the 'cut' would solve the problem, however, it doesn't:

my_breaks <- function(x) { 
  if (cut == "Fair") {seq(0, 5000, 1000) }
  else if (cut == "Good") {seq(1500,15000, 1500)}
  else { seq(0, 15000, 5000) }
}

It provides the error:

Error in cut == "Fair" : comparison (1) is possible only for atomic and list types

Is there another approach to manually provide the breaks to different facets?

1
I assume, the error occurs, because in the function you've created cut refers to a function in base r (rdocumentation.org/packages/base/versions/3.6.2/topics/cut). Even, if you pass cut somehow as argument to the function, I'm not sure, whether it would work properly. Wouldn't facet_wrap(~ cut, scales = "free") + (i.e. also keeping x-axis free) work for you?Wolfgang Arnold
I don't think my_breaks function has any idea of which cut it is in. That information is not passed to it.Ronak Shah
@WolfgangArnold No, I would like to be able to specify the breaks of the y-axis manually.user213544
@RonakShah Ah, that is a problem, yes! Do you have an idea, how to solve that problem? Thanks!user213544

1 Answers

4
votes

Try defining each facet manually by filtering the data and then "patch" the plot up using patchwork package

library(ggplot2)
library(patchwork)

my_plot <- function(df, var, sc) {
  ggplot(data = df, aes(x = carat, y = price)) +
  facet_grid(~ {{var}}) +
  geom_point() +
  coord_cartesian(ylim = {{sc}})
}

diamonds2 <- subset(diamonds, !(cut == "Fair" & price > 5000))

p1 <- diamonds2 %>% filter(cut == "Fair") %>% my_plot(var = "Fair",  sc = c(0, 5000))
p2 <- diamonds2 %>% filter(cut == "Good") %>% my_plot(var = "Good",  sc = c(0, 15000))
p3 <- diamonds2 %>% filter(cut == "Very Good") %>% my_plot(var = "Very Good",  sc = c(0, 1000))
p4 <- diamonds2 %>% filter(cut == "Premium") %>% my_plot(var = "Premium",  sc = c(0, 500))
p5 <- diamonds2 %>% filter(cut == "Ideal") %>% my_plot(var = "Ideal",  sc = c(0, 20000))

p1+p2+p3+p3+p4+plot_layout(ncol = 3)

enter image description here

edit: I realized it was the y-axis breaks to be set pr. facet. In that case

my_plot <- function(df, var, my_breaks) {
  ggplot(data = df, aes(x = carat, y = price)) +
  facet_grid(~ {{var}}) +
  geom_point() +
  scale_y_continuous(breaks = {{my_breaks}})
}

library(ggplot2)
diamonds2 <- subset(diamonds, !(cut == "Fair" & price > 5000))

p1 <- diamonds2 %>% filter(cut == "Fair") %>% my_plot(var = "Fair",  my_breaks = seq(0, 5000, 1000))
p2 <- diamonds2 %>% filter(cut == "Good") %>% my_plot(var = "Good",  my_breaks = seq(1500,15000, 1500))
p3 <- diamonds2 %>% filter(cut == "Very Good") %>% my_plot(var = "Very Good",  my_breaks =seq(1500,15000, 5000))
p4 <- diamonds2 %>% filter(cut == "Premium") %>% my_plot(var = "Premium",  my_breaks = seq(1500,15000, 5000))
p5 <- diamonds2 %>% filter(cut == "Ideal") %>% my_plot(var = "Ideal",  my_breaks = seq(1500,15000, 5000))

p1+p2+p3+p3+p4+plot_layout(ncol = 3)

enter image description here

Edit2: A leaner way to do this using purrr

library(ggplot2)
library(patchwork)
library(purrr)
facets = diamonds2 %>% distinct(cut) %>% pull()
my_breaks = list(seq(0, 5000, 1000), seq(1500,15000, 1500), seq(1500,15000, 5000),
                 seq(1500,15000, 5000), seq(1500,15000, 5000))

plt_list <- purrr::map2(facets, my_breaks, ~my_plot(diamonds2, .x, .y))

Reduce('+', plt_list) + plot_layout(ncol = 3)