I am trying to plot data from nine survey items (q1...q9) that all measure dis/agreement on given statements on an 11-point scale. I rearranged the data into long format which helped me visualize it.
head(df)
# A tibble: 5 × 4
name value left right
<chr> <dbl> <chr> <chr>
1 q1 8 increase social spending decrease social spending
2 q2 1 invest in climate invest in economy
3 q3 3 increase help for ukraine maintain neutrality
4 q4 9 facilitate migration restrict migration
5 q5 4 more equity and diversity social equity already achieved
What I would like to produce is a faceted density plot for each 'variable' or rather group in the name
variable (q1
to q9
), much like ggridges
. I am not actually using ggridges
however, since it proved less versatile.
Problem: I would like to plot the agree and disagree options on either side of the respective density plots (such as 'decrease social spending' ... 'increase social spending'). I think that two more steps are necessary to get there:
Current code:
theme_set(theme_minimal())
ggplot(df, aes(x = value, y = ..count.., fill = name)) +
geom_density(color = "transparent") +
facet_grid(name ~ ., switch = "y") +
scale_fill_viridis_d(direction = -1, guide = "none") +
labs(x="", y="") +
xlim(-1, 13) +
theme(panel.grid.minor = element_blank(),
axis.text.y = element_blank(),
strip.text.y.left = element_text(angle = 0, vjust = 0),
strip.text.y.right = element_text(angle = 0, vjust = 0))
Example data: Here's the code that reproduces (a sample of) my df.
structure(list(name = c("q1", "q2", "q3", "q4", "q5", "q6", "q7",
"q8", "q9", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9",
"q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q1", "q2",
"q3", "q4", "q5", "q6", "q7", "q8", "q9", "q1", "q2", "q3", "q4",
"q5", "q6", "q7", "q8", "q9", "q1", "q2", "q3", "q4", "q5", "q6",
"q7", "q8", "q9", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
"q9", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q1",
"q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q1", "q2", "q3",
"q4", "q5", "q6", "q7", "q8", "q9"),
value = structure(c(8, 1, 3, 9, 4, 11, 3, 10, 6, 5, 1, 11, 8, 1, 7, 8, 9, 5, 4, 8, 6, 6,
8, 7, 4, 6, 5, 1, 3, 11, 9, 9, 6, 4, 10, 4, 7, 3, 5, 9, 6, 9,
6, 10, 8, 1, 3, 6, 8, 6, 11, 11, 11, 11, 3, 3, 1, 6, 9, 11, 6,
11, 8, 3, 9, 11, 1, 3, 11, 11, 3, 11, 5, 4, 11, 2, 8, 10, 6,
6, 9, 6, 10, 5, 6, 11, 10, 3, 8, 1)),
left = c("increase social spending", "invest in climate",
"increase help for ukraine", "facilitate migration", "more equity and diversity",
"more family aid for rich", "more surveillance more security",
"further european integration", "maintain mask mandate",
"increase social spending", "invest in climate", "increase help for ukraine",
"facilitate migration", "more equity and diversity", "more family aid for rich",
"more surveillance more security", "further european integration",
"maintain mask mandate", "increase social spending", "invest in climate",
"increase help for ukraine", "facilitate migration", "more equity and diversity",
"more family aid for rich", "more surveillance more security",
"further european integration", "maintain mask mandate",
"increase social spending", "invest in climate", "increase help for ukraine",
"facilitate migration", "more equity and diversity", "more family aid for rich",
"more surveillance more security", "further european integration",
"maintain mask mandate", "increase social spending", "invest in climate",
"increase help for ukraine", "facilitate migration", "more equity and diversity",
"more family aid for rich", "more surveillance more security",
"further european integration", "maintain mask mandate",
"increase social spending", "invest in climate", "increase help for ukraine",
"facilitate migration", "more equity and diversity", "more family aid for rich",
"more surveillance more security", "further european integration",
"maintain mask mandate", "increase social spending", "invest in climate",
"increase help for ukraine", "facilitate migration", "more equity and diversity",
"more family aid for rich", "more surveillance more security",
"further european integration", "maintain mask mandate",
"increase social spending", "invest in climate", "increase help for ukraine",
"facilitate migration", "more equity and diversity", "more family aid for rich",
"more surveillance more security", "further european integration",
"maintain mask mandate", "increase social spending", "invest in climate",
"increase help for ukraine", "facilitate migration", "more equity and diversity",
"more family aid for rich", "more surveillance more security",
"further european integration", "maintain mask mandate",
"increase social spending", "invest in climate", "increase help for ukraine",
"facilitate migration", "more equity and diversity", "more family aid for rich",
"more surveillance more security", "further european integration",
"maintain mask mandate"),
right = c("decrease social spending",
"invest in economy", "maintain neutrality", "restrict migration",
"social equity already achieved", "less family aid for rich",
"less surveillance more freedom", "european integration gone too far",
"abolish mask mandate", "decrease social spending", "invest in economy",
"maintain neutrality", "restrict migration", "social equity already achieved",
"less family aid for rich", "less surveillance more freedom",
"european integration gone too far", "abolish mask mandate",
"decrease social spending", "invest in economy", "maintain neutrality",
"restrict migration", "social equity already achieved", "less family aid for rich",
"less surveillance more freedom", "european integration gone too far",
"abolish mask mandate", "decrease social spending", "invest in economy",
"maintain neutrality", "restrict migration", "social equity already achieved",
"less family aid for rich", "less surveillance more freedom",
"european integration gone too far", "abolish mask mandate",
"decrease social spending", "invest in economy", "maintain neutrality",
"restrict migration", "social equity already achieved", "less family aid for rich",
"less surveillance more freedom", "european integration gone too far",
"abolish mask mandate", "decrease social spending", "invest in economy",
"maintain neutrality", "restrict migration", "social equity already achieved",
"less family aid for rich", "less surveillance more freedom",
"european integration gone too far", "abolish mask mandate",
"decrease social spending", "invest in economy", "maintain neutrality",
"restrict migration", "social equity already achieved", "less family aid for rich",
"less surveillance more freedom", "european integration gone too far",
"abolish mask mandate", "decrease social spending", "invest in economy",
"maintain neutrality", "restrict migration", "social equity already achieved",
"less family aid for rich", "less surveillance more freedom",
"european integration gone too far", "abolish mask mandate",
"decrease social spending", "invest in economy", "maintain neutrality",
"restrict migration", "social equity already achieved", "less family aid for rich",
"less surveillance more freedom", "european integration gone too far",
"abolish mask mandate", "decrease social spending", "invest in economy",
"maintain neutrality", "restrict migration", "social equity already achieved",
"less family aid for rich", "less surveillance more freedom",
"european integration gone too far", "abolish mask mandate")),
row.names = c(NA, -90L), class = c("tbl_df", "tbl", "data.frame"))
Easiest is to use custom annotation instead.
First, create a data frame for your annotation, then use this for labelling. Using x = -Inf/Inf and hjust accordingly and turn off clipping will allow you to place the labels on the edges of the plot (Plot 1). You will notice, this "sticks" pretty close to the plot. You could use hjust of -0.i
and +1.j
, but this gives very weird results with the use of stringr::str_wrap
. I therefore am suggesting a second alternative with plotting the labels separately and adding them to the plot with {patchwork} (Plot 2).
library(tidyverse)
## make your labelling data frame
df_lab <-
df %>%
distinct(name, right, left) %>%
## add the coordinates and hjust for a neater look
pivot_longer(-name, names_to = "pos", values_to = "label") %>%
mutate(
x = ifelse(pos == "right", Inf, -Inf),
hjust = ifelse(pos == "right", 0, 1)
)
## use after_stat
ggplot(mapping = aes(x = value, y = after_stat(count), fill = name)) +
geom_density(data = df, color = "transparent") +
## add your labels. I am wrapping them in order to limit the width
geom_text(
data = df_lab, size = 8 * 5 / 14,
aes(x,
y = 1, label = stringr::str_wrap(label, 20),
hjust = hjust
)
) +
facet_grid(name ~ ., switch = "y") +
scale_fill_viridis_d(direction = -1, guide = "none") +
## use coord_cartesion(xlim = c(...,...)) instead of xlim()
## but even better is:
scale_x_continuous(expand = c(0, 0)) +
## use NULL!!!
labs(x = NULL, y = NULL) +
## clip off so that you can see the annotation fully
coord_cartesian(clip = "off") +
theme(
panel.grid.minor = element_blank(),
axis.text.y = element_blank(),
## you will need to add a margin to both sides
plot.margin = margin(r = 1, l = 1, unit = "in"),
## now you could remove the strips altogether
strip.background = element_blank(),
strip.text = element_blank(),
## the axis ticks look ugly, so I'm removing them
axis.ticks.y = element_blank()
)
Using patchwork will give you more flexibility regarding label positioning.
library(patchwork)
p_main <-
ggplot(mapping = aes(x = value, y = after_stat(count), fill = name)) +
geom_density(data = df, color = "transparent") +
facet_grid(name ~ ., switch = "y") +
scale_fill_viridis_d(direction = -1, guide = "none") +
scale_x_continuous(expand = c(0, 0)) +
labs(x = NULL, y = NULL) +
theme(
##NB I am removing the plot margin completely
plot.margin = margin(),
panel.grid.minor = element_blank(),
axis.text.y = element_blank(),
strip.background = element_blank(),
strip.text = element_blank(),
axis.ticks.y = element_blank()
)
## annotating with separate plots
p_labs <-
lapply(c("right", "left"), function(side) {
ggplot() +
geom_text(
data = filter(df_lab, pos == side), size = 8 * 5 / 14,
aes(
x = 1,
y = 1, label = stringr::str_wrap(label, 19),
hjust = hjust
)
) +
facet_grid(name ~ .) +
coord_cartesian(clip = "off") +
theme_void() +
theme(
strip.background = element_blank(),
strip.text = element_blank()
)
})
## sadly, you will need to add individual plot margins
(p_labs[[1]] + theme(plot.margin = margin(r = 35))) +
p_main +
(p_labs[[2]] + theme(plot.margin = margin(l = 35))) +
plot_layout(widths = c(.2, .6, .2))
Created on 2023-03-06 with reprex v2.0.2