Matiasko
Matiasko

Reputation: 187

Bottom and top plot labels facet ggplot2

Data structure: Assume you have 2 groups of variables with 3 sublevels each. Let's name them A1, A2, A3 (A-group), B1, B2, B3 (B-group). All integer-type ranging 5-25. Each variable has description in form of 'A1Lower / A1Upper' and so on.

Dataset & Packages: You can generate dataset with the code below:

library(dplyr)
library(ggplot2)

# We will need subscales later
subscales <- paste0(rep(c('A', 'B'), each = 3), 1:3)
# Dataset
data <- replicate(6, sample(5:25, 500, replace = T)) %>%
  as_tibble() %>%
  setNames(subscales)

I've created a facet_wrap of geom_violion with geom_boxplot() and labels (custom labeller) based on description, with the code below:

subscales_labeller <- paste0(subscales, 'Lower / ', subscales, 'Upper') %>%
  setNames(subscales) %>% #as a named vector
  as_labeller() #as a ggplot2 labeller

data %>%
  gather(subscale, value, factor_key = T) %>%
  ggplot(aes(x = '', y = value)) +
  geom_violin() +
  geom_boxplot(width = 0.1) +
  facet_wrap(~subscale, ncol = 3, labeller = subscales_labeller) +
  theme_minimal() +
  labs(x = '', y = '')

Goal: The problem is that the whole label comes on top. I want the Upper description above the violin plot and Lower description below. So far I managed to set x scale as character with the Lower description and labeller with just Upper desciption. It is not perfect as it complicates many things for me (for example I would like to use color = Sex aesthetic) that is why I would prefer to set something like bottom and top labels at once. Not to mention a side lebels with 'A group' and 'B group' desciptions (this would be like heaven but not a must for this post)

Upvotes: 1

Views: 1087

Answers (1)

teunbrand
teunbrand

Reputation: 37903

This answer is posted as a reponse to OP's comment for a version of the facetted plot that uses geom_text() to label upper/lower bounds.

I've made two versions, one static code and one adaptive one. I've also taken the liberty to switch to facet_grid() to accommodate the side labels that OP expressed some interest in. Below is the static version, assume data comes from the code you posted:

df <- data %>% gather(subscale, value, factor_key = T) %>%
  mutate(letter = paste("Group", substr(subscale, 1, 1)),
         number = substr(subscale, 2, 2))

ggplot(df, aes(x = '', y = value)) +
  geom_violin() +
  geom_boxplot(width = 0.1) +
  geom_text(data = data.frame(value = c(max(df$value), min(df$value)),
                              label = c("Upper", "Lower")),
            aes(label = label),
            nudge_y = c(1, -1)) +
  facet_grid(letter ~ number) +
  labs(x = "", y = "") +
  theme_minimal()

enter image description here

In this case, "Upper" and "Lower" will always be be one y-axis unit above/below the extremes of the entire data. You might want to adjust the nudge_y to be appropriate for your size of the plot.

The following piece of code will place the labels adaptively with respect to the group's maximum/minimum. To illustrate this, I've thrown out some observations to show that the labels are placed correctly.

df <- df[!(df$subscale == "A2" & df$value > 15),]

ggplot(df, aes(x = '', y = value)) +
  geom_violin() +
  geom_boxplot(width = 0.1) +
  geom_text(aes(label = "Upper"), stat = "summary", fun.y = "max", 
            position = position_nudge(y = 1)) +
  geom_text(aes(label = "Lower"), stat = "summary", fun.y = "min", 
            position = position_nudge(y = -1)) +
  facet_grid(letter ~ number) +
  labs(x = "", y = "") +
  theme_minimal()

enter image description here

EDIT: with custom labels per facet, sex and age:

labs <- expand.grid(value = range(df$value),
                    letter = unique(df$letter),
                    number = unique(df$number))
labs$label <- c("Sweet", "Sour", "Introvert", "Extrovert",
                "Idiot", "Genius", "Dark", "Light",
                "Small", "Big", "Tom-eh-to", "Tom-ah-to")

df$sex <- sample(c("F", "M"), size = nrow(df), replace = TRUE)
df$age <- sample(c("<20", "20-30", ">30"), size = nrow(df), replace = TRUE)

ggplot(df, aes(x = sex, y = value)) +
  geom_violin(aes(fill = age)) +
  geom_boxplot(aes(fill = age), width = 0.1,
               position = position_dodge(0.9)) +
  geom_text(data = labs,
            aes(x = 1.5, label = label),
            nudge_y = c(-1, 1)) +
  facet_grid(letter ~ number) +
  labs(x = "", y = "") +
  theme_minimal()

enter image description here

Upvotes: 2

Related Questions