I'm trying to reproduce the following plot:
So far, I have produced the following code in R:
library(tidyverse)
tribble(~attribute, ~level, ~importance_score,
"Price", "$70 per month", -0.18,
"Price", "$50 per month", 0,
"Price", "$30 per month", 0.18,
"Data included", "500MB", -0.25,
"Data included", "1GB", -0.10,
"Data included", "10GB", 0.11,
"Data included", "Unlimited", 0.23,
"International minutes included", "0 min", -0.01,
"International minutes included", "90 min", -0.01,
"International minutes included", "300 min", 0.02,
"SMS included", "300 messages", -0.06,
"SMS included", "Unlimited text", 0.06) %>%
mutate(id = 1:nrow(.)) %>%
arrange(desc(id)) %>%
mutate(attribute = as_factor(attribute),
level = as_factor(level)) %>%
ggplot(aes(attribute, importance_score, fill = level)) +
geom_col(width = 0.5, position = position_dodge(0.75)) +
coord_flip() +
scale_y_continuous(breaks = seq(-0.4, 0.4, by = 0.1), limits = c(-0.35, 0.35)) +
scale_fill_manual(values = c("$70 per month" = "#721817",
"$50 per month" = "#721817",
"$30 per month" = "#721817",
"500 MB" = "#fa9f42",
"1GB" = "#fa9f42",
"10GB" = "#fa9f42",
"Unlimited" = "#fa9f42",
"0 min" = "#2b4162",
"90 min" = "#2b4162",
"300 min" = "#2b4162",
"300 messages" = "#0b6e4f",
"Unlimited text" = "#0b6e4f")) +
labs(y = "Relative value", x = "Levels by attribute") +
theme(panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.major.x = element_line(color = "gray95", linetype = 1, size = 1),
panel.grid.major = element_blank(),
panel.background = element_blank(),
legend.position = "none",
text = element_text(size = 15))
which outputs the following plot:
My question is, how would you go about controlling the width
argument in geom_col()
without messing up the position = position_dodge()
argument, which allows the plot to group by the "attribute" variable? What I want is to have the same width for every bar in this plot and also, to have the same distance between each grouping variable.
When I use position = position_dodge(0.75, preserve = "single")
, I get:
In this case, it is not as clear to see in this plot, but: the length is not the same among the two green brackets I drew on top of the plot. How do I solve this?
One option to achieve your desired result would be to make use of facet_grid
like so:
factor(id)
on y
instead of atrribute
attribute
. Add scales="free_y"
and space=free_y
theme
options.y
scaleNote: As far as I get it you could map attribute
on fill
which would simplify your scale_fill_manual
as you only need to set four colors.
library(tidyverse)
dd <- dd %>%
mutate(id = 1:nrow(.)) %>%
arrange(desc(id)) %>%
mutate(attribute = forcats::fct_rev(as_factor(attribute)),
level = as_factor(level))
ggplot(dd, aes(importance_score, factor(id), fill = attribute)) +
geom_col() +
scale_x_continuous(breaks = seq(-0.4, 0.4, by = 0.1), limits = c(-0.35, 0.35), expand = c(0, 0)) +
scale_y_discrete(expand = expansion(add = c(1, 1))) +
scale_fill_manual(values = c("Price" = "#721817",
"Data included" = "#fa9f42",
"International minutes included" = "#2b4162",
"SMS included" = "#0b6e4f")) +
labs(y = "Relative value", x = "Levels by attribute") +
facet_grid(attribute ~ ., scales = "free_y", space = "free_y", switch = "y") +
theme(panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.major.x = element_line(color = "gray95", linetype = 1, size = 1),
panel.grid.major = element_blank(),
panel.background = element_blank(),
legend.position = "none",
text = element_text(size = 15),
strip.text.y.left = element_text(angle = 360, hjust = 1),
strip.background.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.ticks.length.y = unit(0, "pt"),
panel.spacing.y = unit(0, "pt")
)
DATA
dd <- tribble(~attribute, ~level, ~importance_score,
"Price", "$70 per month", -0.18,
"Price", "$50 per month", 0,
"Price", "$30 per month", 0.18,
"Data included", "500MB", -0.25,
"Data included", "1GB", -0.10,
"Data included", "10GB", 0.11,
"Data included", "Unlimited", 0.23,
"International minutes included", "0 min", -0.01,
"International minutes included", "90 min", -0.01,
"International minutes included", "300 min", 0.02,
"SMS included", "300 messages", -0.06,
"SMS included", "Unlimited text", 0.06)