It is possible to insert variable names and their specific values between the last column of facets and the strips of a facet_grid, using the scales_y function provided by Stefan.
However, these inserted texts can sometimes be too close together and overlap, making them unreadable.
Question: Is there a way to space them equidistantly along the secondary axis of each facet in the facet_grid, as shown below? Equidistance should therefore depend on the number of texts.
This might be possible using geom_text_repel(), but I'm struggling to use it in this particular context.
Initial plot:
Desired plot:
Data:
library(tidyverse)
library(stringr)
df <- mpg |>
filter(str_detect(manufacturer, "audi|chevrolet|dodge")) |>
mutate(limit_1=13.5, limit_2=14, limit_3=14.5)
# Create a list of y scales per drv
scales_y <- df |>
distinct(drv, limit_1, limit_2, limit_3) |>
split(~drv) |>
lapply(\(x) {
scale_y_continuous(
sec.axis = dup_axis(
breaks = c(x$limit_1, x$limit_2, x$limit_3),
labels = paste0(c("lim1", "lim2", "lim3"), " (", c(x$limit_1, x$limit_2, x$limit_3), ")")
)
)
})
# plot
ggplot(df, aes(displ, cty)) +
geom_point() +
facet_grid(drv ~ manufacturer, scales = "free") +
geom_hline(aes(yintercept = limit_1, group = drv), linetype = "longdash", colour = "blue") +
geom_hline(aes(yintercept = limit_2, group = drv), linetype = "longdash", colour = "green4") +
geom_hline(aes(yintercept = limit_3, group = drv), linetype = "longdash", colour = "red") +
ggh4x::facetted_pos_scales(y = scales_y) +
theme(
strip.placement = "outside",
axis.title.y.right = element_blank(),
axis.ticks.y.right = element_blank(),
axis.text.y.right = element_text(size = 12, colour = c("blue","green4","red"))) +
theme(
strip.background = element_rect(color="black"),
strip.text.x = element_text(size = 13, color = "black"),
strip.text.y.right = element_text(size = 13, color = "black", angle = -90),
axis.title.x = element_text(vjust = -0.5, size = 13),
axis.title.y = element_text(vjust = 3, size = 13),
axis.text = element_text(size = 10))
One way to achieve your desired result would be to keep the secondary axis labels, but make them transparent to make some room for the 'real' labels. Then, use geom_segment and geom_label to add the labels and connecting lines. Additionally we have to make some more room to account for the nudging of the labels. To this end I use transparent ticks and add some more space by increasing the tick length.
library(tidyverse)
df_hline <- df |>
distinct(drv, manufacturer, limit_1, limit_2, limit_3) |>
pivot_longer(
-c(drv, manufacturer)
) |>
mutate(
color = case_match(
name,
"limit_1" ~ "blue",
"limit_2" ~ "green",
"limit_3" ~ "red"
),
label = case_match(
name,
"limit_1" ~ "lim1",
"limit_2" ~ "lim2",
"limit_3" ~ "lim3"
),
label = paste0(label, " (", value, ")")
)
df_label <- df_hline |>
mutate(
manufacturer = levels(factor(manufacturer))[n_distinct(manufacturer)]
) |>
distinct(
drv, name,
.keep_all = TRUE
) |>
mutate(
yend = scales::rescale(
as.numeric(factor(name)),
from = c(0, n_distinct(name) + 1),
to = c(0, 1)
)
)
# plot
ggplot(df, aes(displ, cty)) +
geom_point() +
facet_grid(drv ~ manufacturer, scales = "free") +
geom_hline(
data = df_hline,
aes(yintercept = value, color = I(color)),
linetype = "longdash"
) +
geom_segment(
data = df_label,
aes(
x = I(1), xend = I(1.15),
y = value, yend = I(yend),
color = I(color)
)
) +
geom_label(
data = df_label,
aes(
label = label, y = I(yend), color = I(color),
x = I(1.15)
),
hjust = 0,
fill = NA,
linewidth = 0,
size = 12 / .pt
) +
ggh4x::facetted_pos_scales(
y = scales_y
) +
theme(
strip.placement = "outside",
axis.title.y.right = element_blank(),
axis.ticks.y.right = element_line(color = NA),
axis.text.y.right = element_text(
size = 12,
# Make sec axis labels transparent
colour = NA
),
# Make some more room to account for the nudgeing of the labels
axis.ticks.length.y.right = unit(20, "pt")
) +
theme(
strip.background = element_rect(color = "black"),
strip.text.x = element_text(size = 13, color = "black"),
strip.text.y.right = element_text(size = 13, color = "black", angle = -90),
axis.title.x = element_text(vjust = -0.5, size = 13),
axis.title.y = element_text(vjust = 3, size = 13),
axis.text = element_text(size = 10)
) +
coord_cartesian(clip = "off")