Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a ptype argument to between() #7073

Merged
merged 14 commits into from
Aug 27, 2024
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
Loading