From c7e09041a420d7662c313b34eb1058f50cc1081d Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Tue, 24 Mar 2026 03:57:30 +0100 Subject: [PATCH 1/5] tinyframe() returns a zero-column data frame now (and not NULL), if a formula part contains no variables (e.g., just a constant) --- R/tinyformula.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/tinyformula.R b/R/tinyformula.R index 29189061c..552e672ca 100644 --- a/R/tinyformula.R +++ b/R/tinyformula.R @@ -75,7 +75,7 @@ tinyframe = function(formula, data, drop = FALSE) { ## - data: model.frame from full formula if (is.null(formula)) return(NULL) vars = attr(terms(formula), "variables")[-1L] - if (is.null(vars)) return(NULL) + if (is.null(vars)) return(data[, 0L, drop = drop]) names = sapply(vars, deparse, width.cutoff = 500L) data[, names, drop = drop] } From 7b8626b5b5950cd15651073ecc73db8ee36b19d6 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Tue, 24 Mar 2026 03:58:06 +0100 Subject: [PATCH 2/5] handle facet = y ~ 1 or 1 ~ x formulas --- R/tinyplot.R | 48 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/R/tinyplot.R b/R/tinyplot.R index 2ba8f8475..9ebc1ef86 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -1333,45 +1333,73 @@ tinyplot.formula = function( ## extract x (if any) x = tinyframe(tf$x, mf) - if (!is.null(x)) { + if (!is.null(x) && ncol(x) > 0L) { xnam = names(x)[[1L]] if (length(names(x)) > 1L) warning(paste("formula should specify at most one x-variable, using:", xnam), "\nif you want to use arithmetic operators, make sure to wrap them inside I()") x = x[[xnam]] } else { + x = NULL xnam = NULL } ## extract y (if any) y = tinyframe(tf$y, mf) - if (!is.null(y)) { + if (!is.null(y) && ncol(y) > 0L) { ynam = names(y)[[1L]] if (length(names(y)) > 1L) warning(paste("formula should specify at most one y-variable, using:", ynam), "\nif you want to use arithmetic operators, make sure to wrap them inside I()") y = y[[ynam]] } else { + y = NULL ynam = NULL } ## extract by (if any) by = tinyframe(tf$by, mf) - if (!is.null(by)) { + if (!is.null(by) && ncol(by) > 0L) { bynam = names(by) by = if (length(bynam) == 1L) by[[bynam]] else interaction(by, sep = ":") + } else { + by = NULL } ## extract x/y facet (if formula) if (!is.null(tf$xfacet) || !is.null(tf$yfacet)) { + ## xfacet/yfacet can be either: + ## - no formula: NULL + ## - formula without variables: empty (zero-column) data + ## - formula with variable(s): data frame xfacet = tinyframe(tf$xfacet, mf) yfacet = tinyframe(tf$yfacet, mf) - if (!is.null(xfacet)) xfacet = if (ncol(xfacet) == 1L) xfacet[[1L]] else interaction(xfacet, sep = ":") - if (!is.null(yfacet)) yfacet = if (ncol(yfacet) == 1L) yfacet[[1L]] else interaction(yfacet, sep = ":") - if (is.null(yfacet)) { - facet = xfacet + xtype = if (is.null(xfacet)) "none" else if (ncol(xfacet) == 0L) "empty" else "data" + ytype = if (is.null(yfacet)) "none" else if (ncol(yfacet) == 0L) "empty" else "data" + + ## turn data frame (if specified) into a single factor + if (xtype == "data") xfacet = if (ncol(xfacet) == 1L) xfacet[[1L]] else interaction(xfacet, sep = ":") + if (ytype == "data") yfacet = if (ncol(yfacet) == 1L) yfacet[[1L]] else interaction(yfacet, sep = ":") + + ## set up actual facet, potentially with grid information + if (xtype %in% c("none", "empty") && ytype %in% c("none", "empty")) { + facet = NULL } else { - facet = interaction(xfacet, yfacet, sep = "~") - attr(facet, "facet_grid") = TRUE - attr(facet, "facet_nrow") = length(unique(yfacet)) + if (xtype %in% c("none", "empty")) { + facet = yfacet + if (xtype == "empty") { + attr(facet, "facet_grid") = TRUE + attr(facet, "facet_nrow") = length(unique(yfacet)) + } + } else if (ytype %in% c("none", "empty")) { + facet = xfacet + if (ytype == "empty") { + attr(facet, "facet_grid") = TRUE + attr(facet, "facet_nrow") = 1L + } + } else { + facet = interaction(xfacet, yfacet, sep = "~") + attr(facet, "facet_grid") = TRUE + attr(facet, "facet_nrow") = length(unique(yfacet)) + } } } From b2803f4b45946154d21ac087c61dc6c36b48a5c8 Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Fri, 5 Jun 2026 00:49:47 +0200 Subject: [PATCH 3/5] handle facet = 1 ~ x and y ~ 1 via facet.args = list(nrow = ...) rather than via facet_grid/facet_nrow attribute --- R/tinyplot.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/tinyplot.R b/R/tinyplot.R index 9ebc1ef86..b1717b1e7 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -1386,14 +1386,14 @@ tinyplot.formula = function( if (xtype %in% c("none", "empty")) { facet = yfacet if (xtype == "empty") { - attr(facet, "facet_grid") = TRUE - attr(facet, "facet_nrow") = length(unique(yfacet)) + if (is.null(facet.args)) facet.args = list() + if (is.null(facet.args[["nrow"]])) facet.args[["nrow"]] = length(unique(yfacet)) } } else if (ytype %in% c("none", "empty")) { facet = xfacet if (ytype == "empty") { - attr(facet, "facet_grid") = TRUE - attr(facet, "facet_nrow") = 1L + if (is.null(facet.args)) facet.args = list() + if (is.null(facet.args[["nrow"]])) facet.args[["nrow"]] = 1L } } else { facet = interaction(xfacet, yfacet, sep = "~") From 4329aaa74793aada19859ab27618e4cf7581358e Mon Sep 17 00:00:00 2001 From: Achim Zeileis Date: Fri, 5 Jun 2026 00:50:13 +0200 Subject: [PATCH 4/5] add tinytests for facet = 1 ~ x and y ~ 1 --- .../facet_density_formula_1col.svg | 173 +++++++++++++++++ .../facet_density_formula_1row.svg | 177 ++++++++++++++++++ inst/tinytest/test-facet.R | 20 ++ 3 files changed, 370 insertions(+) create mode 100644 inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg create mode 100644 inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg diff --git a/inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg b/inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg new file mode 100644 index 000000000..c730d2747 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg @@ -0,0 +1,173 @@ + + + + + + + + + + + + + +Ozone pollution is worse on hot, calm days +Ozone +Density + + + + + + + + + + + + + + + +0 +50 +100 +150 +200 + + + + + +0.00 +0.02 + +cold:calm + + + + + + + + + + + + + + + + +0 +50 +100 +150 +200 + + + + + +0.00 +0.02 + +hot:calm + + + + + + + + + + + + + + + + +0 +50 +100 +150 +200 + + + + + +0.00 +0.02 + +cold:windy + + + + + + + + + + + + + + + + +0 +50 +100 +150 +200 + + + + + +0.00 +0.02 + +hot:windy + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg b/inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg new file mode 100644 index 000000000..5719eeac9 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + +Ozone pollution is worse on hot, calm days +Ozone +Density + + + + + + + + + + + + + + + +0 +50 +100 +200 + + + + + +0.00 +0.01 +0.02 +0.03 + +cold:calm + + + + + + + + + + + + + + + + +0 +50 +100 +200 + + + + + +0.00 +0.01 +0.02 +0.03 + +hot:calm + + + + + + + + + + + + + + + + +0 +50 +100 +200 + + + + + +0.00 +0.01 +0.02 +0.03 + +cold:windy + + + + + + + + + + + + + + + + +0 +50 +100 +200 + + + + + +0.00 +0.01 +0.02 +0.03 + +hot:windy + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-facet.R b/inst/tinytest/test-facet.R index 8b991e6c7..712a73926 100644 --- a/inst/tinytest/test-facet.R +++ b/inst/tinytest/test-facet.R @@ -468,6 +468,26 @@ f = function() { } expect_snapshot_plot(f, label = "facet_density_grid") +f = function() { + tinyplot( + ~Ozone, aq, + type = "density", + facet = 1 ~ hot:windy, + main = "Ozone pollution is worse on hot, calm days" + ) +} +expect_snapshot_plot(f, label = "facet_density_formula_1row") + +f = function() { + tinyplot( + ~Ozone, aq, + type = "density", + facet = hot:windy ~ 1, + main = "Ozone pollution is worse on hot, calm days" + ) +} +expect_snapshot_plot(f, label = "facet_density_formula_1col") + f = function() { tinyplot( ~wt, From 2621a631394ae8b5216ddbc7af27f500b26f8d51 Mon Sep 17 00:00:00 2001 From: Grant McDermott Date: Thu, 4 Jun 2026 18:06:07 -0700 Subject: [PATCH 5/5] update snapshots --- .../tinytest/_tinysnapshot/facet_density_formula_1col.svg | 8 ++++---- .../tinytest/_tinysnapshot/facet_density_formula_1row.svg | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg b/inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg index c730d2747..c1b4b40ec 100644 --- a/inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg +++ b/inst/tinytest/_tinysnapshot/facet_density_formula_1col.svg @@ -57,7 +57,7 @@ 0.00 0.02 -cold:calm +cold:calm @@ -87,7 +87,7 @@ 0.00 0.02 -hot:calm +hot:calm @@ -117,7 +117,7 @@ 0.00 0.02 -cold:windy +cold:windy @@ -147,7 +147,7 @@ 0.00 0.02 -hot:windy +hot:windy diff --git a/inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg b/inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg index 5719eeac9..2482307af 100644 --- a/inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg +++ b/inst/tinytest/_tinysnapshot/facet_density_formula_1row.svg @@ -58,7 +58,7 @@ 0.02 0.03 -cold:calm +cold:calm @@ -89,7 +89,7 @@ 0.02 0.03 -hot:calm +hot:calm @@ -120,7 +120,7 @@ 0.02 0.03 -cold:windy +cold:windy @@ -151,7 +151,7 @@ 0.02 0.03 -hot:windy +hot:windy