diff --git a/NAMESPACE b/NAMESPACE index fa189445..59d3cea4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,6 +11,8 @@ export(tpar) export(type_boxplot) export(type_errorbar) export(type_glm) +export(type_hist) +export(type_histogram) export(type_jitter) export(type_lm) export(type_loess) diff --git a/NEWS.md b/NEWS.md index 49507140..0c5068c9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,23 +9,41 @@ where the formatting is also better._ Breaking changes: -* Type-specific arguments are no longer passed through the main function via -`...`. Instead, they are now passed through a dedicated `type_*()` functions in -the `type` argument. +- Type-specific arguments (i.e., affecting the default behaviour of a certain +plot type) should no longer be passed via `...` in the main plotting function. +Instead, these additional arguments must now be passed explicitly as part of the +corresponding `type_*()` function in the `type` argument. So, for example, you +should now use `plt(Nile, type = type_histogram(breaks = 30))` instead of +`plt(Nile, type = "histogram", breaks = 30)` if you wanted to adjust the number +of breaks. More details are provided below and also in the new dedicated +[Plot types vignette](https://grantmcdermott.com/tinyplot/vignettes/types.html). +The essential idea is that shortcut character types (here: `"histogram"`) all +still work, but are deliberately limited to default behaviour. In contrast, the +functional equivalents (here: `type_histogram()`) are where we can customize +behaviour by passing appropriate arguments. We're sorry to introduce a breaking +change, but this new approach should ensure that users have better control of +how their plots behave, and avoids guesswork on our side. New features: +- Alongside the standard character shortcuts (`"p"`, `"l"`, etc.), the `type` +argument now accepts functional `type_*()` equivalents. These functional plot +types enable a variety of additional features. (#222 @vincentarelbundock) + - New model-based plot types: + - `type_glm()` (shortcut: `"glm"`) + - `type_lm()` (shortcut: `"lm"`) + - `type_loess()` (shortcut: `"loess"`) + - Explicit argument passing for modified behaviour type + (e.g., `type_lm(se = FALSE)`). + - Users can define their own custom types by creating `type_()` + functions. + - More details are provided in the dedicated + [Plot types vignette](https://grantmcdermott.com/tinyplot/vignettes/types.html) + on the website. - The new `flip` argument allows for easily flipping (swapping) the orientation of the x and y axes. This should work regardless of plot type, e.g. `plt(~Sepal.Length | Species, data = iris, type = "density", flip = TRUE)`. (#216 @grantmcdermott) -- New plot types added in #222: - - `type_glm()` (shortcut: `"glm"`) - - `type_lm()` (shortcut: `"lm"`) - - `type_loess()` (shortcut: `"loess"`) -- Custom types can now be defined by users by creating `type_typename()` - functions. (#222 @vincentarelbundock) -- Types vignette on the website. (#222 @vincentarelbundock) Bug fixes: diff --git a/R/tinyplot.R b/R/tinyplot.R index 4e0361cd..a5d3dd57 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -290,9 +290,7 @@ #' specified. #' @param xaxt,yaxt character specifying the type of x-axis and y-axis, respectively. #' See `axes` for the possible values. -#' @param ... other graphical parameters (see \code{\link[graphics]{par}}), or -#' arguments passed to the relevant plot type (e.g., `breaks` for -#' `type = "histogram"`, or `varwidth` for `type = "boxplot"`). +#' @param ... other graphical parameters (see \code{\link[graphics]{par}}). #' #' @returns No return value, called for side effect of producing a plot. #' @@ -1216,7 +1214,8 @@ tinyplot.formula = function( } ## nice axis and legend labels - if (!is.null(type) && isTRUE(type %in% c("hist", "histogram"))) { + hist_type = (is.atomic(type) && type %in% c("hist", "histogram")) || (!is.atomic(type) && identical(type$name, "histogram")) + if (!is.null(type) && hist_type) { if (is.null(ylab)) ylab = "Frequency" if (is.null(xlab)) xlab = xnam } else if (is.null(y)) { diff --git a/R/type_histogram.R b/R/type_histogram.R index 0759c883..5a087d43 100644 --- a/R/type_histogram.R +++ b/R/type_histogram.R @@ -1,17 +1,39 @@ -type_histogram = function() { +#' Histogram type +#' +#' @md +#' @description Type function for histogram plots. `type_hist` is an alias for +#' `type_histogram`. +#' @param breaks Passed to \code{\link[graphics]{hist}}. One of: +#' - a vector giving the breakpoints between histogram cells, +#' - a function to compute the vector of breakpoints, +#' - a single number giving the number of cells for the histogram, +#' - a character string naming an algorithm to compute the number of cells (see ‘Details’ of \code{\link[graphics]{hist}}), +#' - a function to compute the number of cells. +#' In the last three cases the number is a suggestion only; as the breakpoints +#' will be set to pretty values, the number is limited to 1e6 (with a warning if +#' it was larger). If breaks is a function, the x vector is supplied to it as +#' the only argument (and the number of breaks is only limited by the amount of +#' available memory). +#' @export +type_histogram = function(breaks = "Sturges") { out = list( - data = data_histogram(), + data = data_histogram(breaks = breaks), draw = draw_rect(), name = "histogram" ) class(out) = "tinyplot_type" return(out) } +#' @export +#' @name type_hist +#' @rdname type_histogram +type_hist = type_histogram -data_histogram = function() { - fun = function(by, facet, ylab, col, bg, ribbon.alpha, datapoints, breaks = NULL, ...) { - hbreaks = ifelse(!is.null(breaks), breaks, "Sturges") +data_histogram = function(breaks = "Sturges") { + hbreaks = breaks + fun = function(by, facet, ylab, col, bg, ribbon.alpha, datapoints, .breaks = hbreaks, ...) { + hbreaks = ifelse(!is.null(.breaks), .breaks, "Sturges") if (is.null(ylab)) ylab = "Frequency" if (is.null(by) && is.null(palette)) { diff --git a/inst/tinytest/test-histogram.R b/inst/tinytest/test-histogram.R index 268131e5..719dc89e 100644 --- a/inst/tinytest/test-histogram.R +++ b/inst/tinytest/test-histogram.R @@ -21,8 +21,9 @@ f = function() { with( iris, plt( - Petal.Length, by = Species, type = "hist", - fill = 0.4, breaks = 30, palette = "classic" + Petal.Length, by = Species, + type = type_histogram(breaks = 30), + fill = 0.4, palette = "classic" ) ) } @@ -42,8 +43,9 @@ f = function() { with( transform(iris, long_sepal = paste("Long sepal:", Sepal.Length > mean(Sepal.Length))), plt( - Petal.Length, by = Species, facet = long_sepal, type = "hist", - fill = 0.4, breaks = 30, palette = "classic", frame = FALSE, grid = TRUE + Petal.Length, by = Species, facet = long_sepal, + type = type_histogram(breaks = 30), + fill = 0.4, palette = "classic", frame = FALSE, grid = TRUE ) ) } @@ -54,8 +56,9 @@ f = function() { ~Petal.Length | Species, data = transform(iris, long_sepal = paste("Long sepal:", Sepal.Length > mean(Sepal.Length))), facet = ~long_sepal, - type = "hist", fill = 0.4, - breaks = 30, palette = "classic", frame = FALSE, grid = TRUE, + type = type_histogram(breaks = 30), + fill = 0.4, + palette = "classic", frame = FALSE, grid = TRUE, ) } expect_snapshot_plot(f, label = "hist_grouped_faceted") diff --git a/man/tinyplot.Rd b/man/tinyplot.Rd index 3427a5da..a2211f12 100644 --- a/man/tinyplot.Rd +++ b/man/tinyplot.Rd @@ -123,9 +123,7 @@ likely the names of existing vectors or columns of data frames. See the \code{\link[grDevices]{xy.coords}} for details. If supplied separately, \code{x} and \code{y} must be of the same length.} -\item{...}{other graphical parameters (see \code{\link[graphics]{par}}), or -arguments passed to the relevant plot type (e.g., \code{breaks} for -\code{type = "histogram"}, or \code{varwidth} for \code{type = "boxplot"}).} +\item{...}{other graphical parameters (see \code{\link[graphics]{par}}).} \item{by}{grouping variable(s). The default behaviour is for groups to be represented in the form of distinct colours, which will also trigger an diff --git a/man/type_histogram.Rd b/man/type_histogram.Rd new file mode 100644 index 00000000..d9c40fe3 --- /dev/null +++ b/man/type_histogram.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/type_histogram.R +\name{type_histogram} +\alias{type_histogram} +\alias{type_hist} +\title{Histogram type} +\usage{ +type_histogram(breaks = "Sturges") + +type_hist(breaks = "Sturges") +} +\arguments{ +\item{breaks}{Passed to \code{\link[graphics]{hist}}. One of: +\itemize{ +\item a vector giving the breakpoints between histogram cells, +\item a function to compute the vector of breakpoints, +\item a single number giving the number of cells for the histogram, +\item a character string naming an algorithm to compute the number of cells (see ‘Details’ of \code{\link[graphics]{hist}}), +\item a function to compute the number of cells. +In the last three cases the number is a suggestion only; as the breakpoints +will be set to pretty values, the number is limited to 1e6 (with a warning if +it was larger). If breaks is a function, the x vector is supplied to it as +the only argument (and the number of breaks is only limited by the amount of +available memory). +}} +} +\description{ +Type function for histogram plots. \code{type_hist} is an alias for +\code{type_histogram}. +}