Skip to content

Commit 523ec0f

Browse files
authored
Implement vec_pall() and vec_pany() (#2092)
* Implement `list_pall()` and `list_pany()` * Change to `vec_pany()` and `vec_pall()` * Change from `NULL` to `NA` Treat `.missing` as "the value used when a missing value is encountered"
1 parent eb83f98 commit 523ec0f

File tree

11 files changed

+1031
-0
lines changed

11 files changed

+1031
-0
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,8 @@ export(vec_math_base)
582582
export(vec_names)
583583
export(vec_names2)
584584
export(vec_order)
585+
export(vec_pall)
586+
export(vec_pany)
585587
export(vec_proxy)
586588
export(vec_proxy_compare)
587589
export(vec_proxy_equal)

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# vctrs (development version)
22

3+
* New `vec_pany()` and `vec_pall()`, parallel variants of `any()` and `all()` (in the same way that `pmin()` and `pmax()` are parallel variants of `min()` and `max()`).
4+
35
* The deprecated C callable for `vec_is_vector()` has been removed.
46

57
* Fixed the C level signature for the `exp_short_init_compact_seq()` callable.

R/parallel.R

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#' Parallel `any()` and `all()`
2+
#'
3+
#' @description
4+
#' These functions are variants of [any()] and [all()] that work in parallel on
5+
#' multiple inputs at once. They work similarly to how [pmin()] and [pmax()] are
6+
#' parallel variants of [min()] and [max()].
7+
#'
8+
#' @inheritParams rlang::args_error_context
9+
#'
10+
#' @param ... Logical vectors of equal size.
11+
#'
12+
#' @param .missing Value to use when a missing value is encountered. One of:
13+
#'
14+
#' - `NA` to propagate missing values. With this, missings are treated the
15+
#' same way as `|` or `&`.
16+
#'
17+
#' - `FALSE` to treat missing values as `FALSE`.
18+
#'
19+
#' - `TRUE` to treat missing values as `TRUE`.
20+
#'
21+
#' @param .size An optional output size. Only useful to specify if it is possible
22+
#' for no inputs to be provided.
23+
#'
24+
#' @param .arg Argument name used in error messages.
25+
#'
26+
#' @returns A logical vector the same size as the vectors in `...`.
27+
#'
28+
#' @details
29+
#' `vec_pany()` and `vec_pall()` are consistent with [any()] and [all()] when
30+
#' there are no inputs to process in parallel:
31+
#'
32+
#' - `any()` returns `FALSE` with no inputs. Similarly, `vec_pany(.size = 1)`
33+
#' returns `FALSE`.
34+
#'
35+
#' - `all()` returns `TRUE` with no inputs. Similarly, `vec_pall(.size = 1)`
36+
#' returns `TRUE`.
37+
#'
38+
#' @name parallel-operators
39+
#'
40+
#' @examples
41+
#' a <- c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, NA, NA, NA)
42+
#' b <- c(TRUE, FALSE, NA, TRUE, FALSE, NA, TRUE, FALSE, NA)
43+
#'
44+
#' # Default behavior treats missings like `|` does
45+
#' vec_pany(a, b)
46+
#' a | b
47+
#'
48+
#' # Default behavior treats missings like `&` does
49+
#' vec_pall(a, b)
50+
#' a & b
51+
#'
52+
#' # Remove missings from the computation, like `na_rm = TRUE`
53+
#' vec_pany(a, b, .missing = FALSE)
54+
#' (a & !is.na(a)) | (b & !is.na(b))
55+
#'
56+
#' vec_pall(a, b, .missing = TRUE)
57+
#' (a | is.na(a)) & (b | is.na(b))
58+
#'
59+
#' # `vec_pall()` can be used to implement a `dplyr::filter()` style API
60+
#' df <- data_frame(id = seq_along(a), a = a, b = b)
61+
#'
62+
#' keep_rows <- function(x, ...) {
63+
#' vec_slice(x, vec_pall(..., .missing = FALSE))
64+
#' }
65+
#' drop_rows <- function(x, ...) {
66+
#' vec_slice(x, !vec_pall(..., .missing = FALSE))
67+
#' }
68+
#'
69+
#' # "Keep / Drop the rows when both a and b are TRUE"
70+
#' # These form complements of one another, even with `NA`s.
71+
#' keep_rows(df, a, b)
72+
#' drop_rows(df, a, b)
73+
#'
74+
#' # Same empty behavior as `any()` and `all()`
75+
#' vec_pany(.size = 1)
76+
#' any()
77+
#'
78+
#' vec_pall(.size = 1)
79+
#' all()
80+
NULL
81+
82+
#' @rdname parallel-operators
83+
#' @export
84+
vec_pany <- function(
85+
...,
86+
.missing = NA,
87+
.size = NULL,
88+
.arg = "",
89+
.error_call = current_env()
90+
) {
91+
.Call(ffi_vec_pany, list2(...), .missing, .size, environment())
92+
}
93+
94+
#' @rdname parallel-operators
95+
#' @export
96+
vec_pall <- function(
97+
...,
98+
.missing = NA,
99+
.size = NULL,
100+
.arg = "",
101+
.error_call = current_env()
102+
) {
103+
.Call(ffi_vec_pall, list2(...), .missing, .size, environment())
104+
}

_pkgdown.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ reference:
103103
- vec_identify_runs
104104
- vec_expand_grid
105105

106+
- title: Reducers
107+
contents:
108+
- vec_pall
109+
106110
- title: New classes
107111
contents:
108112
- list_of

man/parallel-operators.Rd

Lines changed: 104 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/decl/parallel-decl.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
static
2+
r_obj* vec_parallel(
3+
r_obj* xs,
4+
enum vec_parallel_missing missing,
5+
r_ssize size,
6+
struct vctrs_arg* p_xs_arg,
7+
struct r_lazy error_call,
8+
enum vec_parallel_variant parallel
9+
);
10+
11+
static inline
12+
void vec_parallel_init(const int* v_x, enum vec_parallel_missing missing, r_ssize size, int* v_out);
13+
static inline
14+
void vec_parallel_init_missing_as_na(const int* v_x, r_ssize size, int* v_out);
15+
static inline
16+
void vec_parallel_init_missing_as_false(const int* v_x, r_ssize size, int* v_out);
17+
static inline
18+
void vec_parallel_init_missing_as_true(const int* v_x, r_ssize size, int* v_out);
19+
20+
static inline
21+
void vec_pany_fill(const int* v_x, enum vec_parallel_missing missing, r_ssize size, int* v_out);
22+
static inline
23+
void vec_pall_fill(const int* v_x, enum vec_parallel_missing missing, r_ssize size, int* v_out);
24+
static inline
25+
void vec_pany_fill_missing_as_na(const int* v_x, r_ssize size, int* v_out);
26+
static inline
27+
void vec_pall_fill_missing_as_na(const int* v_x, r_ssize size, int* v_out);
28+
static inline
29+
void vec_pany_fill_missing_as_false(const int* v_x, r_ssize size, int* v_out);
30+
static inline
31+
void vec_pall_fill_missing_as_false(const int* v_x, r_ssize size, int* v_out);
32+
static inline
33+
void vec_pany_fill_missing_as_true(const int* v_x, r_ssize size, int* v_out);
34+
static inline
35+
void vec_pall_fill_missing_as_true(const int* v_x, r_ssize size, int* v_out);
36+
37+
static
38+
enum vec_parallel_missing parse_vec_parallel_missing(r_obj* missing, struct r_lazy error_call);
39+
40+
static
41+
r_ssize compute_size(r_ssize size, r_obj* xs);

src/init.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ extern r_obj* ffi_vec_replace_when(r_obj*, r_obj*, r_obj*, r_obj*);
171171
extern r_obj* ffi_vec_recode_values(r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*);
172172
extern r_obj* ffi_vec_replace_values(r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*);
173173
extern r_obj* ffi_vec_if_else(r_obj*, r_obj*, r_obj*, r_obj*, r_obj*, r_obj*);
174+
extern r_obj* ffi_vec_pany(r_obj*, r_obj*, r_obj*, r_obj*);
175+
extern r_obj* ffi_vec_pall(r_obj*, r_obj*, r_obj*, r_obj*);
174176

175177

176178
// Maturing
@@ -372,6 +374,8 @@ static const R_CallMethodDef CallEntries[] = {
372374
{"ffi_vec_recode_values", (DL_FUNC) &ffi_vec_recode_values, 9},
373375
{"ffi_vec_replace_values", (DL_FUNC) &ffi_vec_replace_values, 6},
374376
{"ffi_vec_if_else", (DL_FUNC) &ffi_vec_if_else, 6},
377+
{"ffi_vec_pany", (DL_FUNC) &ffi_vec_pany, 4},
378+
{"ffi_vec_pall", (DL_FUNC) &ffi_vec_pall, 4},
375379
{"ffi_exp_vec_cast", (DL_FUNC) &exp_vec_cast, 2},
376380
{NULL, NULL, 0}
377381
};

0 commit comments

Comments
 (0)