Skip to content

Commit

Permalink
Add a ptype argument to between() (#7073)
Browse files Browse the repository at this point in the history
* add ptype argument to between Fixes #6906

* update NEWS with ptype argument

* document() and add tests

* add back see also

* add back see also

* Redocument

* Trim trailing whitespace

* Remove extra line

* Minor docs tweaks

* require names ptype, update tests, update function documentation

* remove unnecessary if else blocks

* Tweak NEWS bullet

* A few more tweaks

* Few more tweaks

---------

Co-authored-by: Davis Vaughan <davis@posit.co>
  • Loading branch information
JamesHWade and DavisVaughan authored Aug 27, 2024
1 parent 173b423 commit e055527
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 10 deletions.
9 changes: 7 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# dplyr (development version)

* `between()` gains a new `ptype` argument, allowing users to specify the
desired output type. This is particularly useful for ordered factors and other
complex types where the default common type behavior might not be ideal
(#6906, @JamesHWade).

* Fixed an edge case when coercing data frames to matrices (#7004).

* Fixed an issue where duckplyr's ALTREP data frames were being materialized
early due to internal usage of `ncol()` (#7049).

* R >=3.6.0 is now explicitly required (#7026).

* `if_any()` and `if_all()` are now fully consistent with `any()` and `all()`.
In particular, when called with empty inputs `if_any()` returns `FALSE` and
* `if_any()` and `if_all()` are now fully consistent with `any()` and `all()`.
In particular, when called with empty inputs `if_any()` returns `FALSE` and
`if_all()` returns `TRUE` (#7059, @jrwinget).

# dplyr 1.1.4
Expand Down
26 changes: 21 additions & 5 deletions R/funs.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
#'
#' @details
#' `x`, `left`, and `right` are all cast to their common type before the
#' comparison is made.
#' comparison is made. Use the `ptype` argument to specify the type manually.
#'
#' @inheritParams rlang::args_dots_empty
#'
#' @param x A vector
#' @param left,right Boundary values. Both `left` and `right` are recycled to
#' the size of `x`.
#' @param ptype An optional prototype giving the desired output type. The
#' default is to compute the common type of `x`, `left`, and `right` using
#' [vctrs::vec_cast_common()].
#'
#' @returns
#' A logical vector the same size as `x`.
#' A logical vector the same size as `x` with a type determined by `ptype`.
#'
#' @seealso
#' [join_by()] if you are looking for documentation for the `between()` overlap
Expand All @@ -27,15 +32,26 @@
#'
#' # On a tibble using `filter()`
#' filter(starwars, between(height, 100, 150))
between <- function(x, left, right) {
#'
#' # Using the `ptype` argument with ordered factors, where otherwise everything
#' # is cast to the common type of character before the comparison
#' x <- ordered(
#' c("low", "medium", "high", "medium"),
#' levels = c("low", "medium", "high")
#' )
#' between(x, "medium", "high")
#' between(x, "medium", "high", ptype = x)
between <- function(x, left, right, ..., ptype = NULL) {
check_dots_empty0(...)

args <- list(x = x, left = left, right = right)

# Common type of all inputs
args <- vec_cast_common(!!!args)
args <- vec_cast_common(!!!args, .to = ptype)
x <- args$x
args$x <- NULL

# But recycle to size of `x`
# Recycle to size of `x`
args <- vec_recycle_common(!!!args, .size = vec_size(x))
left <- args$left
right <- args$right
Expand Down
21 changes: 18 additions & 3 deletions man/between.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions tests/testthat/_snaps/funs.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,12 @@
Error in `between()`:
! Can't recycle `right` (size 2) to size 3.

# ptype argument affects type casting

Code
between(x, 1.5, 3.5, ptype = integer())
Condition
Error in `between()`:
! Can't convert from `left` <double> to <integer> due to loss of precision.
* Locations: 1

27 changes: 27 additions & 0 deletions tests/testthat/test-funs.R
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,33 @@ test_that("recycles `left` and `right` to the size of `x`", {
})
})

test_that("ptype argument works as expected with non-alphabetical ordered factors", {
# Create an ordered factor with non-alphabetical order
x <- factor(c("b", "c", "a", "d"), levels = c("d", "c", "b", "a"), ordered = TRUE)

# Test with ptype specified (uses factor order)
expect_identical(
between(x, "c", "a", ptype = x),
c(TRUE, TRUE, TRUE, FALSE)
)

# Test without ptype (uses alphabetical order)
expect_identical(
between(x, "c", "a"),
c(FALSE, FALSE, FALSE, FALSE)
)
})

test_that("ptype argument affects type casting", {
x <- 1:5
expect_identical(
between(x, 1.5, 3.5),
c(FALSE, TRUE, TRUE, FALSE, FALSE)
)
expect_snapshot(error = TRUE, {
between(x, 1.5, 3.5, ptype = integer())
})
})

# cum* --------------------------------------------------------------------

Expand Down

0 comments on commit e055527

Please sign in to comment.