Skip to content

Commit e4c7009

Browse files
fix: ensure consistent legend across groups with different factor levels
When using grouped_ggbarstats() or grouped_ggpiestats() with groups that have different observed levels of a factor, patchwork could not collect legends because the fill scales differed across panels. Changes: - Add drop = FALSE to scale_fill_paletteer_d() in ggbarstats and ggpiestats so all factor levels appear in each panel's legend - In descriptive_data(), conditionally use tidyr::complete() to add zero-count rows for missing factor levels (only when levels are actually missing, to avoid unnecessary row reordering that would affect geom_label_repel layout) - Preserve original descending level order using rev(all_lvls) - Add edge-case tests for grouped plots with differing factor levels Closes #868
1 parent f9c993a commit e4c7009

8 files changed

+659
-76
lines changed

R/ggbarstats.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ ggbarstats <- function(
143143
ggtheme +
144144
theme(panel.grid.major.x = element_blank()) +
145145
guides(fill = guide_legend(title = legend.title %||% as_name(x))) +
146-
paletteer::scale_fill_paletteer_d(paste0(package, "::", palette), name = "")
146+
paletteer::scale_fill_paletteer_d(paste0(package, "::", palette), name = "", drop = FALSE)
147147

148148
# proportion test ------------------------------------------
149149

R/ggpiestats-ggbarstats-helpers.R

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,25 @@ descriptive_data <- function(
99
digits.perc = 1L,
1010
...
1111
) {
12-
.cat_counter(data, {{ x }}, {{ y }}) %>%
12+
# retain all original factor levels so grouped plots share a single legend;
13+
# without this, patchwork::guides("collect") can't merge legends across
14+
15+
# grouped sub-plots when groups have different observed levels (#868)
16+
all_lvls <- levels(dplyr::pull(data, {{ x }}))
17+
18+
result <- .cat_counter(data, {{ x }}, {{ y }}) %>%
1319
mutate(
1420
.label = if (grepl("perc|prop", label.content)) {
1521
paste0(round(perc, digits.perc), "%")
1622
} else if (grepl("count|n|N", label.content)) {
1723
.prettyNum(counts)
1824
} else {
1925
paste0(.prettyNum(counts), "\n", "(", round(perc, digits.perc), "%)")
20-
}, # reorder the category factor levels to order the legend
21-
{{ x }} := factor({{ x }}, unique({{ x }}))
26+
},
27+
{{ x }} := factor({{ x }}, union(rev(all_lvls), unique({{ x }})))
2228
)
29+
30+
result
2331
}
2432

2533

R/ggpiestats.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ ggpiestats <- function(
192192
plotPie <- plotPie +
193193
coord_polar(theta = "y") +
194194
scale_y_continuous(breaks = NULL) +
195-
paletteer::scale_fill_paletteer_d(paste0(package, "::", palette), name = "") +
195+
paletteer::scale_fill_paletteer_d(paste0(package, "::", palette), name = "", drop = FALSE) +
196196
ggtheme +
197197
theme(panel.grid = element_blank(), axis.ticks = element_blank()) +
198198
guides(fill = guide_legend(override.aes = list(color = NA)))

tests/testthat/_snaps/ggbarstats/groups-with-different-x-levels-share-single-legend.svg

Lines changed: 259 additions & 0 deletions
Loading

tests/testthat/_snaps/ggpiestats/empty-groups-in-factors-not-dropped.svg

Lines changed: 73 additions & 71 deletions
Loading

tests/testthat/_snaps/ggpiestats/groups-with-different-x-levels-share-single-legend.svg

Lines changed: 232 additions & 0 deletions
Loading

tests/testthat/test-ggbarstats.R

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,5 +260,46 @@ test_that(
260260
proportion.test = FALSE
261261
)
262262
)
263+
264+
# Issue #868: groups with different observed x-levels produce double legend
265+
dt_issue <- data.frame(
266+
school = factor(
267+
c(rep("School 1", 27L), rep("School 2", 153L)),
268+
levels = c("School 1", "School 2")
269+
),
270+
subject = factor(
271+
c(
272+
rep("Chemistry", 27L),
273+
rep("Biology", 50L), rep("Chemistry", 51L), rep("Physics", 52L)
274+
),
275+
levels = c("Biology", "Chemistry", "Physics")
276+
),
277+
GenderIdentity = factor(
278+
c(
279+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 2L),
280+
rep("Not listed", 2L), rep(NA, 3L),
281+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 10L),
282+
rep("Prefer not to answer", 10L), rep(NA, 10L),
283+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 10L),
284+
rep("Prefer not to answer", 10L), rep(NA, 11L),
285+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 10L),
286+
rep("Not listed", 3L), rep("Prefer not to answer", 10L), rep(NA, 9L)
287+
),
288+
levels = c("Man", "Woman", "Non-binary", "Not listed", "Prefer not to answer")
289+
)
290+
)
291+
292+
set.seed(123)
293+
expect_doppelganger(
294+
title = "groups with different x-levels share single legend",
295+
fig = grouped_ggbarstats(
296+
data = dt_issue,
297+
x = GenderIdentity,
298+
y = subject,
299+
grouping.var = school,
300+
results.subtitle = FALSE,
301+
proportion.test = FALSE
302+
)
303+
)
263304
}
264305
)

tests/testthat/test-ggpiestats.R

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,5 +268,46 @@ test_that(
268268
title = "empty groups in factors not dropped",
269269
fig = ggpiestats(smokers, smoker)
270270
)
271+
272+
# Issue #868: groups with different observed x-levels produce double legend
273+
dt_issue <- data.frame(
274+
school = factor(
275+
c(rep("School 1", 27L), rep("School 2", 153L)),
276+
levels = c("School 1", "School 2")
277+
),
278+
subject = factor(
279+
c(
280+
rep("Chemistry", 27L),
281+
rep("Biology", 50L), rep("Chemistry", 51L), rep("Physics", 52L)
282+
),
283+
levels = c("Biology", "Chemistry", "Physics")
284+
),
285+
GenderIdentity = factor(
286+
c(
287+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 2L),
288+
rep("Not listed", 2L), rep(NA, 3L),
289+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 10L),
290+
rep("Prefer not to answer", 10L), rep(NA, 10L),
291+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 10L),
292+
rep("Prefer not to answer", 10L), rep(NA, 11L),
293+
rep("Man", 10L), rep("Woman", 10L), rep("Non-binary", 10L),
294+
rep("Not listed", 3L), rep("Prefer not to answer", 10L), rep(NA, 9L)
295+
),
296+
levels = c("Man", "Woman", "Non-binary", "Not listed", "Prefer not to answer")
297+
)
298+
)
299+
300+
set.seed(123)
301+
expect_doppelganger(
302+
title = "groups with different x-levels share single legend",
303+
fig = grouped_ggpiestats(
304+
data = dt_issue,
305+
x = GenderIdentity,
306+
y = subject,
307+
grouping.var = school,
308+
results.subtitle = FALSE,
309+
proportion.test = FALSE
310+
)
311+
)
271312
}
272313
)

0 commit comments

Comments
 (0)