0

I have the following ggplot:

enter image description here

that is generated using the following script:

df_long <- melt(df)

ggplot(df_long, aes(x = variable, y = value)) + 
  geom_boxplot() +
  facet_wrap(~variable, scales = 'free', ncol = 6)

I would like to change the y-axis limits to start with 0 and end with the maximum value for each variable, and also to have the same breaks for all the variables. Currently, the AC and ND are fine since they have same breaks (their labels are even aligned) but the other variables are different. For the NV and IC, they don't end with the maximum value and they don't have the same breaks as with AC and ND. For the PIC and DBI, they don't have the same breaks as with AC and ND. In other woeds, I want all y axes to be aligned and look elegant.

Do you have any idea how to fix that?

mnist
  • 6,571
  • 1
  • 18
  • 41
Nasser
  • 2,118
  • 6
  • 33
  • 56
  • change `scales = 'free'` to `scales = 'free_x'`? – bouncyball Dec 10 '19 at 15:28
  • @bouncyball that doesn't what I want. I want each variable to have its own y-axis as the maximum value differs between variables. – Nasser Dec 10 '19 at 15:31
  • @Nasser then your initial plot is good no? It's actually scaling the axis to each facet of your plot. The axes *are* from min to max of each variable. – RoB Dec 10 '19 at 15:34
  • @RoB Yes, the plot is good except scaling the y-axis of each facet. I don't know how to show the maximum and make all the labels aligned with other facets' lables. – Nasser Dec 10 '19 at 15:48
  • @Nasser Ahh ! you want the *ticks* of the axes to be at the same positions ? – RoB Dec 10 '19 at 15:49
  • @RoB Exactly. This is what I want. – Nasser Dec 10 '19 at 15:49
  • @Nasser Unfortunately, I don't think this is easily possible as of yet ( [see this post](https://stackoverflow.com/q/35654080/6478701) ) The linked posts may lead you to a workaround, but keep in mind you may have to plot each facet separately to do this. – RoB Dec 10 '19 at 15:51
  • 1
    @Nasser Scratch my previous comment, I found out how to do it. I'll post an answer in a few. – RoB Dec 10 '19 at 15:57

1 Answers1

3

You need to specify your requirements for the y axis and set it up with the scale_y_continuous statement.

The breaks argument can be a function returning breaks from the given data, so you can set up a function to give a sequence of set length between its min and max values. Note that these axis values may not make much sense (eg. 2.09, 2.83, ...) but they will be at the same y positions on the graph.

The selected break values are pretty unreadable so we can also specify a function for the labels argument that takes the breaks as input and returns the rounded labels.

library(ggplot2)
# generate dummy data
set.seed(2)
df <- data.frame(var = sample(LETTERS[1:4], 1000, replace = T),
                 val = abs(rnorm(1000)))
df$val[df$var%in%c("B", "D")] <- df$val[df$var%in%c("B", "D")] / 2 
head(df)


# actual plotting
my_breaks <- function(x){seq(min(x), max(x), length.out = 5)}
my_labels <- function(x){round(x, digits = 2)}

ggplot(df, aes(x=var,y=val)) + geom_boxplot() + facet_wrap(~var, scales = 'free', ncol = 4) +
  scale_y_continuous(breaks = my_breaks, labels = my_labels)

This outputs the following graph

enter image description here

EDIT : adding some constraints to the axes

If you want to restrain your axes to specific ranges, you have to play around with the my_breaks() function definition. I'll give you a few examples below as per your comments.

  • Start the axes on 0 : my_breaks <- function(x){seq(0, max(x), length.out = 5)}
  • End the on 1 or max value if smaller : my_breaks <- function(x){seq(min(x), min(1, max(x)), length.out = 5)}

I'm sure you can figure out the specific requirements to your needs ;)

RoB
  • 1,833
  • 11
  • 23
  • 1
    Great answer - just, breaks should go from 0 to max(value) according to the request: `my_breaks <- function(x){seq(0, max(x), length.out = 5)}` – Davide ND Dec 10 '19 at 16:16
  • Thanks, both for the great answer. However, how can I set the maximum to not exceed 1? – Nasser Dec 10 '19 at 16:18
  • 2
    Here: `my_breaks <- function(x){seq(0, min(max(x),1), length.out = 5)}` – Davide ND Dec 10 '19 at 16:22
  • 2
    @DavideDN beat me to it ! @Nasser, see my updated answer with a few examples of adjustments to `my_breaks()` – RoB Dec 10 '19 at 16:24
  • @Nasser Just be careful when constraining the axes as it makes the points outside the scope of your plot 'disappear'. It may skew one's view of the data this way, especially with a lot of outliers like it appears in your data. – RoB Dec 10 '19 at 16:28
  • @RoB Thanks again. This is the plot [links](https://imgur.com/PJkz2O9) based on your answer. It looks great except the position of the last tick in the first two plots are different from the other plots. Do you have an idea how to make them all at the same position? (prefer to be at the exact position of 1 in the first 2 plots) – Nasser Dec 10 '19 at 16:41
  • 1
    @Nasser try adding `expand = c(0, 0)` to the `scale_y_continuous` function – RoB Dec 10 '19 at 16:45