db0 =
tibble(
S1 = rep(c("BIO","MAT","MED","PHY","SOC"),12),
Model = rep(c("A","B","C"),20),
Unaltered = runif(60),
Altered = runif(60)
) %>%
arrange(S1,Model) |>
mutate(
Similar =
c(rep(c("MED","MAT","PHY","SOC"),3),
rep(c("MED","BIO","PHY","SOC"),3),
rep(c("BIO","MAT","PHY","SOC"),3),
rep(c("MED","BIO","MAT","SOC"),3),
rep(c("MED","BIO","MAT","PHY"),3)
)) |>
arrange(S1,Similar,Model) |>
group_by(S1) |>
mutate(
Similar = factor(Similar),
Model = factor(Model),
y_position = row_number(),
y_position = ifelse(y_position > 3,
y_position + 1,
y_position),
y_position = ifelse(y_position > 7,
y_position + 1,
y_position),
y_position = ifelse(y_position > 11,
y_position + 1,
y_position),
Increase = ifelse(Altered > Unaltered, TRUE, FALSE)
)
I made this vertical grid, with lollipop bars. Over the bar there is the name of the model
.
I want that for the chosen breaks, the associated label in Similar
is shown.
db0 |>
ggplot(aes(y = y_position, group = interaction(Similar, Model))) +
geom_segment(aes(x = Unaltered, xend = Altered, color = Increase)) +
geom_point(aes(x = Unaltered), color = "black") +
geom_point(aes(x = Altered, color = Increase)) +
scale_color_manual(values = c("purple", "gold")) +
scale_x_continuous(name = "Value", limits = c(0, 1)) +
theme_test() +
theme(
axis.text.y = element_text(size = 10),
panel.grid.major.y = element_line(color = "grey90"),
panel.grid.minor.y = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
legend.position = "none"
) +
scale_y_continuous(
breaks = c(2, 6, 10, 14),
#labels = aes(Similar)
) +
geom_text(aes(x = (Altered + Unaltered) / 2,
y = y_position + .5,
label = Model), size = 4,
check_overlap = TRUE) +
facet_grid(S1 ~ ., scales = "free_y")
3
EDIT: Simpler and cleaner approach could be to use ggh4x::facet_nested
.
...
scale_y_continuous(breaks = NULL) +
coord_cartesian(clip = "off") +
ggh4x::facet_nested(S1 + Similar ~ ., scales = "free_y")
(For a subset of the data)
I’m curious if there’s a simpler hack for this. I used tidytext::reorder_within
, which creates facet-specific axis definitions/orderings, and tidytext::scale_y_reordered
to display those as unadorned labels.
db0 |>
ggplot(aes(y = tidytext::reorder_within(
paste(Similar, y_position, sep = "___"), y_position, S1),
group = interaction(Similar, Model))) +
...
tidytext::scale_y_reordered() +
coord_cartesian(clip = "off") + # to keep labels outside facets
geom_text(aes(x = (Altered + Unaltered) / 2,
label = Model), size = 4, vjust = -0.2,
check_overlap = TRUE) +
facet_grid(S1 ~ ., scales = "free_y")
0
Personally I think that the approach by @JonSping using ggh4x::facet_nested
makes your plot much easier to read.
Anyway here is an approach to have the labels on the axis which simply sticks with a discrete scale, i.e. maps Similar
on y
and computes the y_position
s for the geom
s per panel using a split-and-bind approach where to simplify the computation I use scales::rescale
instead of a bunch of ifelse
.
library(ggplot2)
library(dplyr, warn = FALSE)
set.seed(123)
# Dodge width
dw <- .9
db1 <- db0 |>
split(~S1) |>
lapply((x) {
n_models <- n_distinct(x$Model)
dw <- dw / n_models
x |>
mutate(
y_position = as.numeric(factor(Similar)) + dw * scales::rescale(
as.numeric(factor(Model)),
to = c(-1, 1)
),
Increase = ifelse(Altered > Unaltered, TRUE, FALSE)
)
}) |>
bind_rows()
db1 |>
ggplot(aes(y = Similar, group = interaction(Similar, Model))) +
# Keep the discrete y scale
geom_blank() +
geom_segment(aes(
y = y_position, yend = y_position,
x = Unaltered, xend = Altered, color = Increase
)) +
geom_point(aes(y = y_position, x = Unaltered), color = "black") +
geom_point(aes(y = y_position, x = Altered, color = Increase)) +
scale_color_manual(values = c("purple", "gold")) +
scale_x_continuous(name = "Value", limits = c(0, 1)) +
theme_test() +
theme(
axis.text.y = element_text(size = 10),
panel.grid.major.y = element_line(color = "grey90"),
panel.grid.minor.y = element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
legend.position = "none"
) +
geom_text(
aes(
x = (Altered + Unaltered) / 2,
y = y_position + .05,
label = Model
),
check_overlap = TRUE,
vjust = 0,
size = 8 / .pt
) +
facet_grid(S1 ~ ., scales = "free_y") +
coord_cartesian(clip = "off")