Skip to content
Merged
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ where the formatting is also better._

## Development version

### New features

- Support for univariate formulas, e.g., `y ~ 1`, `~ x`, and `~ 0`. These are
translated to `x = NULL` or `y = NULL` in the default method call, with
automatic type inference: `y ~ 1` (numeric) produces a histogram, `y ~ 1`
(factor) produces a barplot, `~ x` (factor) produces a barplot, and `~ x`
(numeric) produces a scatterplot against the index. The `~ 0` form is useful
for types that don't require x/y, such as `segments` and `rect`. Thanks to
@brock for suggestion and discussion. (#534 @zeileis, @grantmcdermott)

### Aesthetic changes

- The legend plot characters for the `"pointrange"` and `"errorbar"` types now
Expand Down
17 changes: 15 additions & 2 deletions R/sanitize_type.R
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,23 @@ sanitize_type = function(settings) {
assert_choice(type, known_types, null.ok = TRUE)

if (is.null(type)) {
if (!is.null(x) && (is.factor(x) || is.character(x)) && !(is.factor(y) || is.character(y))) {
if (is.null(x) && !(is.factor(y) || is.character(y))) {
# enforce histogram type for y ~ 1
settings$x = y
settings$y = NULL
type = type_hist
} else if (is.null(x) && (is.factor(y) || is.character(y))) {
# enforce barplot type for factor(y) ~ 1
settings$x = y
settings$y = NULL
type = type_barplot
} else if ((is.factor(x) || is.character(x)) && is.null(y)) {
# enforce barplot type for ~ factor(y)
type = type_barplot
} else if (!is.null(x) && (is.factor(x) || is.character(x)) && !(is.factor(y) || is.character(y))) {
# enforce boxplot type for y ~ factor(x)
type = type_boxplot
} else if (is.factor(y) || is.character(y)) {
} else if (!is.null(x) && (is.factor(y) || is.character(y))) {
# enforce spineplot type for factor(y) ~ x
type = type_spineplot
} else {
Expand Down
2 changes: 1 addition & 1 deletion R/sanitize_xylab.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ sanitize_xylab = function(settings) {
if (!is.null(ylab)) {
out_ylab = ylab
} else if (is_frequency && is.null(y) && !is.null(x)) {
out_ylab = "Frequency"
out_ylab = if (type == "barplot") "Count" else "Frequency"
} else if (is_density && is.null(y) && !is.null(x)) {
out_ylab = "Density"
} else if (is_ribbon) {
Expand Down
4 changes: 3 additions & 1 deletion R/tinyformula.R
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ tinyframe = function(formula, data, drop = FALSE) {
## - formula: (sub-)formula
## - data: model.frame from full formula
if (is.null(formula)) return(NULL)
names = sapply(attr(terms(formula), "variables")[-1L], deparse, width.cutoff = 500L)
vars = attr(terms(formula), "variables")[-1L]
if (is.null(vars)) return(NULL)
names = sapply(vars, deparse, width.cutoff = 500L)
data[, names, drop = drop]
}
39 changes: 29 additions & 10 deletions R/tinyplot.R
Original file line number Diff line number Diff line change
Expand Up @@ -1326,13 +1326,16 @@ tinyplot.formula = function(
m[[1L]] = quote(stats::model.frame)
mf = eval.parent(m)

## extract x
## extract x (if any)
x = tinyframe(tf$x, mf)
xnam = names(x)[[1L]]
if (length(names(x)) != 1L) warning(
paste("formula should specify exactly one x-variable, using:", xnam),
if (!is.null(x)) {
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]]
x = x[[xnam]]
} else {
xnam = NULL
}

## extract y (if any)
y = tinyframe(tf$y, mf)
Expand All @@ -1341,6 +1344,8 @@ tinyplot.formula = function(
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 {
ynam = NULL
}

## extract by (if any)
Expand Down Expand Up @@ -1369,19 +1374,33 @@ tinyplot.formula = function(
dens_type = !is.null(type) && (is.atomic(type) && identical(type, "density")) || (!is.atomic(type) && identical(type$name, "density"))
hist_type = !is.null(type) && (is.atomic(type) && type %in% c("hist", "histogram")) || (!is.atomic(type) && identical(type$name, "histogram"))
barp_type = !is.null(type) && (is.atomic(type) && identical(type, "barplot")) || (!is.atomic(type) && identical(type$name, "barplot"))
if (dens_type) {
if (is.null(x) && is.null(y)) {
# Exception: both x and y NULL (e.g., ~ 0 with type = "segments").
# Build labels from xmin/xmax/ymin/ymax names in the original call (m),
# since deparse(substitute()) in the default method would see mf[["..."]].
if (is.null(xlab) && !is.null(m[["xmin"]]) && !is.null(m[["xmax"]])) {
xlab = sprintf("[%s, %s]", deparse1(m[["xmin"]]), deparse1(m[["xmax"]]))
}
if (is.null(ylab) && !is.null(m[["ymin"]]) && !is.null(m[["ymax"]])) {
ylab = sprintf("[%s, %s]", deparse1(m[["ymin"]]), deparse1(m[["ymax"]]))
}
} else if (is.null(x) && !is.null(y)) {
# Exception: univariate y ~ 1 formulas. sanitize_type() will swap x/y and
# infer the type (histogram or barplot). Set xlab from the variable name
# and let sanitize_xylab() determine ylab after the type is known.
if (is.null(xlab)) xlab = ynam
} else if (dens_type) {
# if (is.null(ylab)) ylab = "Density" ## rather assign ylab as part of internal type_density() logic
if (is.null(xlab)) xlab = xnam
} else if (hist_type) {
# if (is.null(ylab)) ylab = "Frequency" ## rather assign ylab as part of internal type_histogram() logic
if (is.null(xlab)) xlab = xnam
} else if (is.null(y)) {
if (!barp_type) {
if (is.factor(x) || is.character(x) || barp_type) {
if (is.null(xlab)) xlab = xnam
} else {
if (is.null(ylab)) ylab = xnam
if (is.null(xlab)) xlab = "Index"
} else {
if (is.null(ylab)) ylab = "Count"
if (is.null(xlab)) xlab = xnam
}
} else {
if (is.null(ylab)) ylab = ynam
Expand Down
59 changes: 59 additions & 0 deletions inst/tinytest/_tinysnapshot/barplot_formula_univariate.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions inst/tinytest/_tinysnapshot/barplot_formula_y1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading