Reputation: 187
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
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()
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()
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()
Upvotes: 2