Skip to content

Commit cc98fd1

Browse files
authored
Enable 2D structures as aesthetics (#6076)
* replace `lengths()` with `list_sizes()` * unname via `vec_set_names()` * add test * add news bullet * use slicing as subsetting * deduplicate label validation efforts * comparise sizes * use `is_finite()` helper in `check_transformation()`
1 parent 4c5bb24 commit cc98fd1

File tree

8 files changed

+40
-9
lines changed

8 files changed

+40
-9
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@
219219
`labs()` and several guides (@teunbrand, #3196).
220220
* `stat_summary_bin()` no longer ignores `width` parameter (@teunbrand, #4647).
221221
* Added `keep.zeroes` argument to `stat_bin()` (@teunbrand, #3449)
222+
* (internal) removed barriers for using 2D structures as aesthetics
223+
(@teunbrand, #4189).
222224
* `coord_sf()` no longer errors when dealing with empty graticules (@teunbrand, #6052)
223225
* Added `theme_transparent()` with transparent backgrounds (@topepo).
224226
* New theme elements `palette.{aes}.discrete` and `palette.{aes}.continuous`.

R/geom-.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ NULL
270270
.stroke <- 96 / 25.4
271271

272272
check_aesthetics <- function(x, n) {
273-
ns <- lengths(x)
273+
ns <- list_sizes(x)
274274
good <- ns == 1L | ns == n
275275

276276
if (all(good)) {

R/guide-.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,11 +525,12 @@ opposite_position <- function(position) {
525525

526526
# Ensure that labels aren't a list of expressions, but proper expressions
527527
validate_labels <- function(labels) {
528-
if (!is.list(labels)) {
528+
if (!obj_is_list(labels)) {
529529
return(labels)
530530
}
531+
labels[lengths(labels) == 0L] <- ""
531532
if (any(vapply(labels, is.language, logical(1)))) {
532-
do.call(expression, labels)
533+
inject(expression(!!!labels))
533534
} else {
534535
unlist(labels)
535536
}

R/layer.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ Layer <- ggproto("Layer", NULL,
327327
}
328328

329329
n <- nrow(data)
330-
aes_n <- lengths(evaled)
330+
aes_n <- list_sizes(evaled)
331331
if (n == 0) {
332332
# No data, so look at longest evaluated aesthetic
333333
if (length(evaled) == 0) {
@@ -352,7 +352,7 @@ Layer <- ggproto("Layer", NULL,
352352
} else {
353353
evaled$PANEL <- data$PANEL
354354
}
355-
evaled <- lapply(evaled, unname)
355+
evaled <- lapply(evaled, vec_set_names, names = NULL)
356356
evaled <- as_gg_data_frame(evaled)
357357
evaled <- add_group(evaled)
358358
evaled

R/layout.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ scale_apply <- function(data, vars, method, scale_id, scales) {
318318

319319
lapply(vars, function(var) {
320320
pieces <- lapply(seq_along(scales), function(i) {
321-
scales[[i]][[method]](data[[var]][scale_index[[i]]])
321+
scales[[i]][[method]](vec_slice(data[[var]], scale_index[[i]]))
322322
})
323323
# Remove empty vectors to avoid coercion issues with vctrs
324324
pieces[lengths(pieces) == 0] <- NULL

R/scale-.R

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -851,13 +851,14 @@ ScaleContinuous <- ggproto("ScaleContinuous", Scale,
851851
labels <- self$labels
852852
}
853853

854-
if (length(labels) != length(breaks)) {
854+
if (!identical(size0(labels), size0(breaks))) {
855855
cli::cli_abort(
856856
"{.arg breaks} and {.arg labels} have different lengths.",
857857
call = self$call
858858
)
859859
}
860-
if (is.list(labels)) {
860+
861+
if (obj_is_list(labels)) {
861862
# Guard against list with empty elements
862863
labels[lengths(labels) == 0] <- ""
863864
# Make sure each element is scalar
@@ -1386,7 +1387,7 @@ scale_flip_position <- function(scale) {
13861387
}
13871388

13881389
check_transformation <- function(x, transformed, name, arg = NULL, call = NULL) {
1389-
if (!any(is.finite(x) != is.finite(transformed))) {
1390+
if (!any(is_finite(x) != is_finite(transformed))) {
13901391
return(invisible())
13911392
}
13921393
if (is.null(arg)) {

R/utilities.R

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,16 @@ as_unordered_factor <- function(x) {
781781
x
782782
}
783783

784+
size0 <- function(x) {
785+
if (obj_is_vector(x)) {
786+
vec_size(x)
787+
} else if (is.vector(x)) {
788+
length(x)
789+
} else {
790+
NULL
791+
}
792+
}
793+
784794
fallback_palette <- function(scale) {
785795
aes <- scale$aesthetics[1]
786796
discrete <- scale$is_discrete()

tests/testthat/test-layer.R

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,20 @@ test_that("layer_data returns a data.frame", {
182182
l <- geom_point(data = nrow)
183183
expect_snapshot_error(l$layer_data(mtcars))
184184
})
185+
186+
test_that("data.frames and matrix aesthetics survive the build stage", {
187+
df <- data_frame0(
188+
x = 1:2,
189+
g = matrix(1:4, 2),
190+
f = data_frame0(a = 1:2, b = c("c", "d"))
191+
)
192+
193+
p <- layer_data(
194+
ggplot(df, aes(x, x, colour = g, shape = f)) +
195+
geom_point() +
196+
scale_colour_identity() +
197+
scale_shape_identity()
198+
)
199+
expect_vector(p$colour, matrix(NA_integer_, nrow = 0, ncol = 2), size = 2)
200+
expect_vector(p$shape, data_frame0(a = integer(), b = character()), size = 2)
201+
})

0 commit comments

Comments
 (0)