First, here's the structure of my data:
# first 20 rows of data for reprex
df_long <- structure(
list(
DV = c(2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 2, 1, 1, 1, 1, 2, 1, 1),
comp_name = structure(c(10L, 12L, 4L, 3L, 11L, 6L, 7L, 2L, 9L, 1L, 5L, 8L, 13L, 10L, 12L, 4L, 3L, 11L, 6L, 7L),
levels = c("Less Smart Person vs.\n5 Less Smart Monkeys",
"Less Smart Person vs.\n5 Really Smart Monkeys",
"Less Smart Person vs.\nLess Smart Monkey",
"Less Smart Person vs.\nReally Smart Monkey",
"Really Smart Monkey vs.\n5 Less Smart Monkeys",
"Really Smart Monkey vs.\nLess Smart Monkey",
"Really Smart Person vs.\n5 Less Smart Monkeys",
"Really Smart Person vs.\n5 Less Smart People",
"Really Smart Person vs.\n5 Really Smart Monkeys",
"Really Smart Person vs.\nLess Smart Monkey",
"Really Smart Person vs.\nLess Smart Person",
"Really Smart Person vs.\nReally Smart Monkey",
"Really Smart Person vs.\nSuper Smart Monkey"), class = "factor")),
row.names = c(NA, -20L), class = c("tbl_df", "tbl", "data.frame"))
I'm trying to visualize the results of several multiple choice questions simultaneously. Here's the code for a plot I already have - I want to adjust the y-axis labels so that one choice is on the left and one choice is on the right.
ggplot(df_long, aes(y = comp_name, x = DV, fill = as.factor(DV))) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(
name = "",
values = c("cornflowerblue", "grey", "sandybrown"),
labels = c("Person/Smarter", "Can't Decide", "Monkey/Dumber")
) +
labs(
title = "Are Children More Speciesist Than Adults?",
y = NULL,
x = NULL,
legend = NULL
) +
scale_x_continuous(
breaks = c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1),
labels = c("0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"),
) +
theme_minimal() +
theme(
text = element_text(size = 15),
axis.text.y = element_text(size = 13, color = "grey40"),
axis.text = element_text(color = "black"),
axis.line.x = element_line(color = "black"),
axis.text.x = element_text(color = "black", size = 14),
legend.position = "bottom",
legend.spacing.y = unit(0.2, "cm"),
plot.title = element_text(hjust = 0.5, size = 16),
plot.margin = unit(c(5,5,5,5),"mm")
) +
guides(fill = guide_legend(reverse = TRUE))
The above code is very similar to what I want, but has all the labels on the left:
One way I tried to fix this is by adding a second Y axis like so:
vlabs1 <- c(
"Really Smart Person",
"Really Smart Person",
"Really Smart Person",
"Really Smart Person",
"Really Smart Person",
"Really Smart Person",
"Really Smart Person",
"Really Smart Monkey",
"Really Smart Monkey",
"Less Smart Person",
"Less Smart Person",
"Less Smart Person",
"Less Smart Person"
)
vlabs2 <- c(
"Super Smart Monkey",
"Really Smart Monkey",
"Less Smart Person",
"Less Smart Monkey",
"5 Really Smart Monkeys",
"5 Less Smart People",
"5 Less Smart Monkeys",
"Less Smart Monkey",
"5 Less Smart Monkeys",
"Really Smart Monkey",
"Less Smart Monkey",
"5 Really Smart Monkeys",
"5 Less Smart Monkeys"
)
# make comp_name numeric and use this value to assign axis labels
ggplot(df_long, aes(y = as.numeric(comp_name), x = DV, fill = as.factor(DV))) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(
name = "",
values = c("cornflowerblue", "grey", "sandybrown"),
labels = c("Person/Smarter", "Can't Decide", "Monkey/Dumber")
) +
labs(
title = "Are Children More Speciesist Than Adults?",
y = NULL,
x = NULL,
legend = NULL
) +
scale_x_continuous(
breaks = c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1),
labels = c("0%", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"),
) +
scale_y_continuous(limits = c(0, length(vlabs1)),
breaks = 1:length(vlabs1),
labels = vlabs1,
sec.axis = sec_axis(~.,
breaks = 1:length(vlabs2),
labels = vlabs2)) +
theme_minimal() +
theme(
text = element_text(size = 15),
axis.text.y = element_text(size = 13, color = "grey40"),
axis.text = element_text(color = "black"),
axis.line.x = element_line(color = "black"),
axis.text.x = element_text(color = "black", size = 14),
legend.position = "bottom",
legend.spacing.y = unit(0.2, "cm"),
plot.title = element_text(hjust = 0.5, size = 16),
plot.margin = unit(c(5,5,5,5),"mm")
) +
guides(fill = guide_legend(reverse = TRUE))
But for some reason adding another axis this way messes up the whole plot and I get this:
While your approach is right, the issue is that when converting comp_name
to a numeric ggplot2
will default to drawing a vertical bar chart, i.e. when both variables mapped on x
and y
are numerics it defaults to orientation="x"
. Instead, to fix your issue you have to explicitly set the orientation, i.e set orientation="y"
. Additionally, you can simplify your code a bit:
library(ggplot2)
library(dplyr, warn = FALSE)
library(tidyr)
df_long <- df_long |>
# Question identifier
mutate(
qid = as.numeric(comp_name),
comp_name = gsub("vs\\.", "", comp_name)
) |>
separate_wider_delim(
comp_name,
delim = "\n", names = c("choice1", "choice2")
)
labels_y1 <- df_long |>
distinct(qid, choice1) |>
tibble::deframe()
labels_y2 <- df_long |>
distinct(qid, choice2) |>
tibble::deframe()
ggplot(df_long, aes(y = qid, x = DV, fill = factor(DV))) +
geom_bar(stat = "identity", position = "fill", orientation = "y") +
scale_y_reverse(
breaks = unique(df_long$qid),
labels = labels_y1,
sec.axis = dup_axis(
labels = labels_y2
)
) +
scale_x_continuous(
breaks = seq(0, 1, .1),
labels = scales::percent,
) +
scale_fill_manual(
name = "",
values = c("cornflowerblue", "grey", "sandybrown"),
labels = c("Person/Smarter", "Can't Decide", "Monkey/Dumber")
) +
labs(
title = "Are Children More Speciesist Than Adults?",
y = NULL,
x = NULL,
legend = NULL
) +
theme_minimal() +
theme(
text = element_text(size = 15),
axis.text.y = element_text(size = 13, color = "grey40"),
axis.text = element_text(color = "black"),
axis.line.x = element_line(color = "black"),
axis.text.x = element_text(color = "black", size = 14),
legend.position = "bottom",
legend.spacing.y = unit(0.2, "cm"),
plot.title = element_text(hjust = 0.5, size = 16),
plot.margin = unit(c(5, 5, 5, 5), "mm")
) +
guides(fill = guide_legend(reverse = TRUE))