From 03760232c9aa86e95715e028381e31404dd3f7d5 Mon Sep 17 00:00:00 2001 From: Matthew Kay Date: Sat, 2 Mar 2024 22:34:52 -0600 Subject: [PATCH] allow optional arguments to curried functions to be waived --- NAMESPACE | 4 +- R/abstract_geom.R | 6 ++- R/auto_partial.R | 80 +++++++++++++++++++++++------- R/density.R | 8 +-- R/point_interval.R | 2 +- R/stat_slabinterval.R | 5 +- R/util.R | 6 ++- man/auto_partial.Rd | 16 +++++- man/density_histogram.Rd | 8 +-- man/reexports.Rd | 16 ++++++ man/stat_ccdfinterval.Rd | 15 +++--- man/stat_cdfinterval.Rd | 15 +++--- man/stat_eye.Rd | 15 +++--- man/stat_gradientinterval.Rd | 15 +++--- man/stat_halfeye.Rd | 15 +++--- man/stat_histinterval.Rd | 15 +++--- man/stat_slab.Rd | 15 +++--- man/stat_slabinterval.Rd | 15 +++--- man/stat_spike.Rd | 15 +++--- tests/testthat/test.auto_partial.R | 22 +++++++- 20 files changed, 223 insertions(+), 85 deletions(-) create mode 100755 man/reexports.Rd diff --git a/NAMESPACE b/NAMESPACE index df947b19..715254fd 100755 --- a/NAMESPACE +++ b/NAMESPACE @@ -274,6 +274,7 @@ export(thickness) export(to_broom_names) export(to_ggmcmc_names) export(ul) +export(waiver) export(weighted_ecdf) export(weighted_quantile) export(weighted_quantile_fun) @@ -299,6 +300,7 @@ importFrom(ggplot2,.stroke) importFrom(ggplot2,GeomPolygon) importFrom(ggplot2,GeomSegment) importFrom(ggplot2,has_flipped_aes) +importFrom(ggplot2,waiver) importFrom(glue,glue) importFrom(glue,glue_collapse) importFrom(grDevices,nclass.FD) @@ -315,7 +317,7 @@ importFrom(rlang,caller_env) importFrom(rlang,enexpr) importFrom(rlang,enexprs) importFrom(rlang,enquo) -importFrom(rlang,enquos) +importFrom(rlang,enquos0) importFrom(rlang,eval_tidy) importFrom(rlang,expr) importFrom(rlang,get_expr) diff --git a/R/abstract_geom.R b/R/abstract_geom.R index 6dda6bd9..9dfc31c3 100755 --- a/R/abstract_geom.R +++ b/R/abstract_geom.R @@ -235,5 +235,9 @@ make_geom = function(geom, #' (which will be expressions) match the formals of the generated code. #' @noRd to_expression = function(x) { - parse(text = deparse(x), keep.source = FALSE)[[1L]] + if (inherits(x, "waiver")) { + quote(waiver()) + } else { + parse(text = deparse(x), keep.source = FALSE)[[1L]] + } } diff --git a/R/auto_partial.R b/R/auto_partial.R index d6817d26..863490d7 100755 --- a/R/auto_partial.R +++ b/R/auto_partial.R @@ -48,6 +48,11 @@ #' Many other common arguments for \pkg{ggdist} functions work similarly; e.g. #' `density`, `align`, `breaks`, `bandwidth`, and `point_interval` arguments. #' +#' These function families (except [point_interval()]) also support passing +#' [waiver]s to their optional arguments: if [waiver()] is passed to any +#' of these arguments, their default value (or the most +#' recently-partially-applied non-`waiver` value) is used instead. +#' #' Use the [auto_partial()] function to create new functions that support #' automatic partial application. #' @@ -83,21 +88,35 @@ NULL #' function with the arguments that were supplied replacing the defaults. #' Can be called multiple times #' @noRd -#' @importFrom rlang as_quosure enquos eval_tidy expr get_expr -partial_self = function(name = NULL) { +#' @importFrom rlang as_quosure enquos0 eval_tidy expr get_expr +partial_self = function(name = NULL, waivable = TRUE) { f = sys.function(-1L) call_expr = match.call(f, sys.call(-1L), TRUE, parent.frame(2L)) f_quo = as_quosure(call_expr[[1]], parent.frame(2L)) - default_args = lapply(call_expr[-1], as_quosure, env = parent.frame(2L)) + provided_args = lapply(call_expr[-1], as_quosure, env = parent.frame(2L)) name = name %||% deparse0(get_expr(call_expr[[1]])) + waivable_arg_names = if (waivable) { + f_args = formals(f) + is_required_arg = vapply(f_args, rlang::is_missing, FUN.VALUE = logical(1)) + names(f_args)[!is_required_arg] + } + partial_f = function(...) { - new_args = enquos(...) - all_args = defaults(new_args, default_args) + new_args = enquos0(...) + if (waivable) { + is_waivable = rlang::names2(new_args) %in% waivable_arg_names + is_waived = is_waivable + is_waived[is_waivable] = map_lgl_(new_args[is_waivable], function(arg_expr) { + inherits(eval_tidy(arg_expr), "waiver") + }) + new_args = new_args[!is_waived] + } + all_args = defaults(new_args, provided_args) eval_tidy(expr((!!f_quo)(!!!all_args))) } - attr(partial_f, "default_args") = default_args + attr(partial_f, "provided_args") = provided_args attr(partial_f, "name") = name class(partial_f) = c("ggdist_partial_function", "function") partial_f @@ -107,10 +126,12 @@ partial_self = function(name = NULL) { #' @param f A function #' @param name A character string giving the name of the function, to be used #' when printing. +#' @param waivable logical: if `TRUE`, optional arguments that get +#' passed a [waiver()] will keep their default value (or whatever +#' non-`waiver` value has been most recently partially applied for that +#' argument). #' @returns A modified version of `f` that will automatically be partially #' applied if all of its required arguments are not given. -#' @export -#' @importFrom rlang new_function expr #' @examples #' # create a custom automatically partially applied function #' f = auto_partial(function(x, y, z = 3) (x + y) * z) @@ -119,7 +140,13 @@ partial_self = function(name = NULL) { #' g = f(y = 2)(z = 4) #' g #' g(1) -auto_partial = function(f, name = NULL) { +#' +#' # pass waiver() to optional arguments to use existing values +#' f(z = waiver())(1, 2) # uses default z = 3 +#' f(z = 4)(z = waiver())(1, 2) # uses z = 4 +#' @export +#' @importFrom rlang new_function expr +auto_partial = function(f, name = NULL, waivable = TRUE) { f_body = body(f) # must ensure the function body is a { ... } block, not a single expression, # so we can splice it in later with !!!f_body @@ -131,17 +158,25 @@ auto_partial = function(f, name = NULL) { f_args = formals(f) # find the required arguments - required_args = f_args[vapply(f_args, rlang::is_missing, FUN.VALUE = logical(1))] - required_arg_names = names(required_args) + is_required_arg = vapply(f_args, rlang::is_missing, FUN.VALUE = logical(1)) + required_arg_names = names(f_args)[is_required_arg] required_arg_names = required_arg_names[required_arg_names != "..."] - # no required arguments => function will always fully evaluate when called - if (length(required_arg_names) == 0) return(f) + # build an expression to apply waivers to optional args + if (waivable) { + optional_args = f_args[!is_required_arg] + process_waivers = map2_(optional_args, names(optional_args), function(arg_expr, arg_name) { + arg_sym = as.symbol(arg_name) + expr(if (inherits(!!arg_sym, "waiver")) !!arg_sym = !!arg_expr) + }) + } else { + process_waivers = list() + } # build a logical expression testing to see if any required args are missing any_required_args_missing = Reduce( function(x, y) expr(!!x || !!y), - lapply(required_arg_names, function(arg_name) expr(missing(!!as.name(arg_name)))) + lapply(required_arg_names, function(arg_name) expr(missing(!!as.symbol(arg_name)))) ) partial_self_f = if (identical(environment(f), environment(partial_self))) { @@ -154,11 +189,19 @@ auto_partial = function(f, name = NULL) { partial_self } + if (length(required_arg_names) == 0) { + partial_self_if_missing_args = list() + } else { + partial_self_if_missing_args = list(expr( + if (!!any_required_args_missing) return((!!partial_self_f)(!!name, waivable = !!waivable)) + )) + } + new_f = new_function( f_args, expr({ - if (!!any_required_args_missing) return((!!partial_self_f)(!!name)) - + !!!process_waivers + !!!partial_self_if_missing_args !!!f_body }), env = environment(f) @@ -172,7 +215,7 @@ auto_partial = function(f, name = NULL) { #' @export print.ggdist_partial_function = function(x, ...) { f_sym = as.name(attr(x, "name")) - f_args = lapply(attr(x, "default_args"), get_expr) + f_args = lapply(attr(x, "provided_args"), get_expr) cat(sep = "\n", ": ", @@ -184,3 +227,6 @@ print.ggdist_partial_function = function(x, ...) { invisible(x) } + +#' @export +ggplot2::waiver diff --git a/R/density.R b/R/density.R index 93c7d243..3719171c 100755 --- a/R/density.R +++ b/R/density.R @@ -261,8 +261,9 @@ density_bounded = auto_partial(name = "density_bounded", function( #' #' @param x numeric vector containing a sample to compute a density estimate for. #' @param weights optional numeric vector of weights to apply to `x`. -#' @param breaks Determines the breakpoints defining bins. Similar to (but not -#' exactly the same as) the `breaks` argument to [graphics::hist()]. One of: +#' @param breaks Determines the breakpoints defining bins. Defaults to `"Scott"`. +#' Similar to (but not exactly the same as) the `breaks` argument to [graphics::hist()]. +#' One of: #' - A scalar (length-1) numeric giving the number of bins #' - A vector numeric giving the breakpoints between histogram bins #' - A function taking `x` and `weights` and returning either the @@ -276,7 +277,8 @@ density_bounded = auto_partial(name = "density_bounded", function( #' For example, `breaks = "Sturges"` will use the [breaks_Sturges()] algorithm, #' `breaks = 9` will create 9 bins, and `breaks = breaks_fixed(width = 1)` will #' set the bin width to `1`. -#' @param align Determines how to align the breakpoints defining bins. One of: +#' @param align Determines how to align the breakpoints defining bins. Default +#' (`"none"`) performs no alignment. One of: #' - A scalar (length-1) numeric giving an offset that is subtracted from the breaks. #' The offset must be between `0` and the bin width. #' - A function taking a sorted vector of `breaks` (bin edges) and returning diff --git a/R/point_interval.R b/R/point_interval.R index 82041025..6fcf31c9 100644 --- a/R/point_interval.R +++ b/R/point_interval.R @@ -155,7 +155,7 @@ point_interval = function( .data, ..., .width = 0.95, .point = median, .interval = qi, .simple_names = TRUE, na.rm = FALSE, .exclude = c(".chain", ".iteration", ".draw", ".row"), .prob ) { - if (missing(.data)) return(partial_self("point_interval")) + if (missing(.data)) return(partial_self("point_interval", waivable = FALSE)) UseMethod("point_interval") } diff --git a/R/stat_slabinterval.R b/R/stat_slabinterval.R index 0bcb7bfe..90478c0e 100755 --- a/R/stat_slabinterval.R +++ b/R/stat_slabinterval.R @@ -336,6 +336,7 @@ compute_interval_slabinterval = function( #' the data and then uses a bounded density estimator based on the reflection method. #' @param adjust Passed to `density`: the bandwidth for the density estimator for sample data #' is adjusted by multiplying it by this value. See e.g. [density_bounded()] for more information. +#' Default (`waiver()`) defers to the default of the density estimator, which is usually `1`. #' @param trim For sample data, should the density estimate be trimmed to the range of the #' data? Passed on to the density estimator; see the `density` parameter. Default `TRUE`. #' @param expand For sample data, should the slab be expanded to the limits of the scale? Default `FALSE`. @@ -492,10 +493,10 @@ StatSlabinterval = ggproto("StatSlabinterval", AbstractStatSlabinterval, default_params = defaults(list( p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, diff --git a/R/util.R b/R/util.R index 576ba37c..86ebd86e 100644 --- a/R/util.R +++ b/R/util.R @@ -241,8 +241,12 @@ map_lgl_ = function(.x, .f, ...) { vapply(.x, .f, FUN.VALUE = logical(1), ...) } +map2_ = function(.x, .y, .f) { + .mapply(.f, list(.x, .y), NULL) +} + map2_chr_ = function(.x, .y, .f) { - vctrs::list_unchop(.mapply(.f, list(.x, .y), NULL), ptype = character()) + vctrs::list_unchop(map2_(.x, .y, .f), ptype = character()) } fct_rev_ = function(x) { diff --git a/man/auto_partial.Rd b/man/auto_partial.Rd index 1bb95e22..b09fe3ca 100755 --- a/man/auto_partial.Rd +++ b/man/auto_partial.Rd @@ -5,13 +5,18 @@ \alias{automatic-partial-functions} \title{Automatic partial function application in ggdist} \usage{ -auto_partial(f, name = NULL) +auto_partial(f, name = NULL, waivable = TRUE) } \arguments{ \item{f}{A function} \item{name}{A character string giving the name of the function, to be used when printing.} + +\item{waivable}{logical: if \code{TRUE}, optional arguments that get +passed a \code{\link[=waiver]{waiver()}} will keep their default value (or whatever +non-\code{waiver} value has been most recently partially applied for that +argument).} } \value{ A modified version of \code{f} that will automatically be partially @@ -53,6 +58,11 @@ ways: Many other common arguments for \pkg{ggdist} functions work similarly; e.g. \code{density}, \code{align}, \code{breaks}, \code{bandwidth}, and \code{point_interval} arguments. +These function families (except \code{\link[=point_interval]{point_interval()}}) also support passing +\link{waiver}s to their optional arguments: if \code{\link[=waiver]{waiver()}} is passed to any +of these arguments, their default value (or the most +recently-partially-applied non-\code{waiver} value) is used instead. + Use the \code{\link[=auto_partial]{auto_partial()}} function to create new functions that support automatic partial application. } @@ -84,4 +94,8 @@ f(1) g = f(y = 2)(z = 4) g g(1) + +# pass waiver() to optional arguments to use existing values +f(z = waiver())(1, 2) # uses default z = 3 +f(z = 4)(z = waiver())(1, 2) # uses z = 4 } diff --git a/man/density_histogram.Rd b/man/density_histogram.Rd index f0eff1df..97c00352 100755 --- a/man/density_histogram.Rd +++ b/man/density_histogram.Rd @@ -20,8 +20,9 @@ density_histogram( \item{weights}{optional numeric vector of weights to apply to \code{x}.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -38,7 +39,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/reexports.Rd b/man/reexports.Rd new file mode 100755 index 00000000..b3c8242c --- /dev/null +++ b/man/reexports.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/auto_partial.R +\docType{import} +\name{reexports} +\alias{reexports} +\alias{waiver} +\title{Objects exported from other packages} +\keyword{internal} +\description{ +These objects are imported from other packages. Follow the links +below to see their documentation. + +\describe{ + \item{ggplot2}{\code{\link[ggplot2]{waiver}}} +}} + diff --git a/man/stat_ccdfinterval.Rd b/man/stat_ccdfinterval.Rd index ce632c03..143b224e 100755 --- a/man/stat_ccdfinterval.Rd +++ b/man/stat_ccdfinterval.Rd @@ -14,9 +14,9 @@ stat_ccdfinterval( expand = TRUE, p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, point_interval = "median_qi", @@ -154,13 +154,15 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -177,7 +179,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_cdfinterval.Rd b/man/stat_cdfinterval.Rd index 66831f3b..51b2a016 100755 --- a/man/stat_cdfinterval.Rd +++ b/man/stat_cdfinterval.Rd @@ -14,9 +14,9 @@ stat_cdfinterval( expand = TRUE, p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, point_interval = "median_qi", @@ -154,13 +154,15 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -177,7 +179,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_eye.Rd b/man/stat_eye.Rd index c6697bdc..56e0d0c9 100755 --- a/man/stat_eye.Rd +++ b/man/stat_eye.Rd @@ -12,10 +12,10 @@ stat_eye( ..., p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, point_interval = "median_qi", @@ -149,7 +149,8 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} @@ -157,8 +158,9 @@ data? Passed on to the density estimator; see the \code{density} parameter. Defa \item{expand}{For sample data, should the slab be expanded to the limits of the scale? Default \code{FALSE}. Can be length two to control expansion to the lower and upper limit respectively.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -175,7 +177,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_gradientinterval.Rd b/man/stat_gradientinterval.Rd index b7a421b4..fabb1342 100755 --- a/man/stat_gradientinterval.Rd +++ b/man/stat_gradientinterval.Rd @@ -13,10 +13,10 @@ stat_gradientinterval( fill_type = "auto", p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, point_interval = "median_qi", @@ -151,7 +151,8 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} @@ -159,8 +160,9 @@ data? Passed on to the density estimator; see the \code{density} parameter. Defa \item{expand}{For sample data, should the slab be expanded to the limits of the scale? Default \code{FALSE}. Can be length two to control expansion to the lower and upper limit respectively.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -177,7 +179,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_halfeye.Rd b/man/stat_halfeye.Rd index 56427cde..1e76d3c0 100755 --- a/man/stat_halfeye.Rd +++ b/man/stat_halfeye.Rd @@ -12,10 +12,10 @@ stat_halfeye( ..., p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, point_interval = "median_qi", @@ -149,7 +149,8 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} @@ -157,8 +158,9 @@ data? Passed on to the density estimator; see the \code{density} parameter. Defa \item{expand}{For sample data, should the slab be expanded to the limits of the scale? Default \code{FALSE}. Can be length two to control expansion to the lower and upper limit respectively.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -175,7 +177,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_histinterval.Rd b/man/stat_histinterval.Rd index dff29961..4d977d66 100755 --- a/man/stat_histinterval.Rd +++ b/man/stat_histinterval.Rd @@ -12,10 +12,10 @@ stat_histinterval( ..., density = "histogram", p_limits = c(NA, NA), - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, point_interval = "median_qi", @@ -149,7 +149,8 @@ distribution's support if it is finite, and \code{0.001} (\code{0.999}) if it is it would be equivalent to \code{c(.001, .999)} since the normal distribution is defined on \verb{(-Inf, Inf)}.} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} @@ -157,8 +158,9 @@ data? Passed on to the density estimator; see the \code{density} parameter. Defa \item{expand}{For sample data, should the slab be expanded to the limits of the scale? Default \code{FALSE}. Can be length two to control expansion to the lower and upper limit respectively.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -175,7 +177,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_slab.Rd b/man/stat_slab.Rd index 3285abf0..5132d339 100755 --- a/man/stat_slab.Rd +++ b/man/stat_slab.Rd @@ -12,10 +12,10 @@ stat_slab( ..., p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, slab_type = NULL, @@ -129,7 +129,8 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} @@ -137,8 +138,9 @@ data? Passed on to the density estimator; see the \code{density} parameter. Defa \item{expand}{For sample data, should the slab be expanded to the limits of the scale? Default \code{FALSE}. Can be length two to control expansion to the lower and upper limit respectively.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -155,7 +157,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_slabinterval.Rd b/man/stat_slabinterval.Rd index f2db7196..58f1dd91 100755 --- a/man/stat_slabinterval.Rd +++ b/man/stat_slabinterval.Rd @@ -12,10 +12,10 @@ stat_slabinterval( ..., p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, point_interval = "median_qi", @@ -149,7 +149,8 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} @@ -157,8 +158,9 @@ data? Passed on to the density estimator; see the \code{density} parameter. Defa \item{expand}{For sample data, should the slab be expanded to the limits of the scale? Default \code{FALSE}. Can be length two to control expansion to the lower and upper limit respectively.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -175,7 +177,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/man/stat_spike.Rd b/man/stat_spike.Rd index 03547148..0b2a1d03 100755 --- a/man/stat_spike.Rd +++ b/man/stat_spike.Rd @@ -13,10 +13,10 @@ stat_spike( at = "median", p_limits = c(NA, NA), density = "bounded", - adjust = 1, + adjust = waiver(), trim = TRUE, expand = FALSE, - breaks = "Scott", + breaks = waiver(), align = "none", outline_bars = FALSE, slab_type = NULL, @@ -131,7 +131,8 @@ the data and then uses a bounded density estimator based on the reflection metho }} \item{adjust}{Passed to \code{density}: the bandwidth for the density estimator for sample data -is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information.} +is adjusted by multiplying it by this value. See e.g. \code{\link[=density_bounded]{density_bounded()}} for more information. +Default (\code{waiver()}) defers to the default of the density estimator, which is usually \code{1}.} \item{trim}{For sample data, should the density estimate be trimmed to the range of the data? Passed on to the density estimator; see the \code{density} parameter. Default \code{TRUE}.} @@ -139,8 +140,9 @@ data? Passed on to the density estimator; see the \code{density} parameter. Defa \item{expand}{For sample data, should the slab be expanded to the limits of the scale? Default \code{FALSE}. Can be length two to control expansion to the lower and upper limit respectively.} -\item{breaks}{Determines the breakpoints defining bins. Similar to (but not -exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. One of: +\item{breaks}{Determines the breakpoints defining bins. Defaults to \code{"Scott"}. +Similar to (but not exactly the same as) the \code{breaks} argument to \code{\link[graphics:hist]{graphics::hist()}}. +One of: \itemize{ \item A scalar (length-1) numeric giving the number of bins \item A vector numeric giving the breakpoints between histogram bins @@ -157,7 +159,8 @@ For example, \code{breaks = "Sturges"} will use the \code{\link[=breaks_Sturges] \code{breaks = 9} will create 9 bins, and \code{breaks = breaks_fixed(width = 1)} will set the bin width to \code{1}.} -\item{align}{Determines how to align the breakpoints defining bins. One of: +\item{align}{Determines how to align the breakpoints defining bins. Default +(\code{"none"}) performs no alignment. One of: \itemize{ \item A scalar (length-1) numeric giving an offset that is subtracted from the breaks. The offset must be between \code{0} and the bin width. diff --git a/tests/testthat/test.auto_partial.R b/tests/testthat/test.auto_partial.R index f53e5287..891888f0 100755 --- a/tests/testthat/test.auto_partial.R +++ b/tests/testthat/test.auto_partial.R @@ -38,7 +38,7 @@ test_that("functions inside the ggdist namespace do not inline partial_self", { x + 1 } environment(f) = asNamespace("ggdist") - expect_match(deparse0(body(auto_partial(f, name = "f"))), 'partial_self("f")', fixed = TRUE) + expect_match(deparse0(body(auto_partial(f, name = "f"))), 'partial_self("f", waivable = TRUE)', fixed = TRUE) }) test_that("wrapper functions work", { @@ -51,3 +51,23 @@ test_that("wrapper functions work", { expect_equal(g(1), f(1, y = 2)) expect_equal(g(1, y = 3, z = 4), f(1, y = 3, z = 4)) }) + +test_that("original function names are preserved in match.call after multiple partial applications", { + foo = auto_partial(function(x) as.call(lapply(match.call(), get_expr))) + + expect_equal(foo()()(1), quote(foo(x = 1))) +}) + +test_that("waivers work", { + foo = auto_partial(function(x, a = 2) c(x, a)) + + expect_equal(foo(a = waiver())(1), c(1, 2)) + expect_equal(foo(1, a = waiver()), c(1, 2)) + expect_equal(foo(a = waiver())(x = 1), c(1, 2)) + expect_equal(foo(a = waiver())(a = 4)(x = 1), c(1, 4)) + expect_equal(foo(a = 4)(a = waiver())(x = 1), c(1, 4)) + + foo = auto_partial(function(x, y, a = 3, b = 4) c(x, y, a, b)) + + expect_equal(foo(a = waiver(), b = 5)(1)(y = -2, b = waiver()), c(1, -2, 3, 5)) +})