Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions r/R/dplyr-filter.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ filter.arrow_dplyr_query <- function(.data, ..., .by = NULL, .preserve = FALSE)
call = expr
)
}
out <- set_filters(out, filt)
out <- set_filters(out, filt, exclude = FALSE)
}

if (by$from_by) {
Expand All @@ -59,7 +59,58 @@ filter.arrow_dplyr_query <- function(.data, ..., .by = NULL, .preserve = FALSE)
}
filter.Dataset <- filter.ArrowTabular <- filter.RecordBatchReader <- filter.arrow_dplyr_query

set_filters <- function(.data, expressions) {
filter_out.arrow_dplyr_query <- function(.data, ..., .by = NULL, .preserve = FALSE) {
try_arrow_dplyr({
# TODO something with the .preserve argument
out <- as_adq(.data)

by <- compute_by({{ .by }}, out, by_arg = ".by", data_arg = ".data")

if (by$from_by) {
out$group_by_vars <- by$names
}

expanded_filters <- expand_across(out, quos(...))
if (length(expanded_filters) == 0) {
# Nothing to do
return(as_adq(.data))
}

# tidy-eval the filter expressions inside an Arrow data_mask
mask <- arrow_mask(out)

combined <- NULL

for (expr in expanded_filters) {
filt <- arrow_eval(expr, mask)

if (length(mask$.aggregations)) {
arrow_not_supported(
.actual_msg = "Expression not supported in filter_out() in Arrow",
call = expr
)
}

# arrow_eval() may return either an Expression or a list_of<Expression>
if (is_list_of(filt, "Expression")) {
filt <- Reduce("&", filt)
}

combined <- if (is.null(combined)) filt else (combined & filt)
}

out <- set_filters(out, combined, exclude = TRUE)

if (by$from_by) {
out$group_by_vars <- character()
}

out
})
}
filter_out.Dataset <- filter_out.ArrowTabular <- filter_out.RecordBatchReader <- filter_out.arrow_dplyr_query

set_filters <- function(.data, expressions, exclude = FALSE) {
if (length(expressions)) {
if (is_list_of(expressions, "Expression")) {
# expressions is a list of Expressions. AND them together and set them on .data
Expand All @@ -70,6 +121,12 @@ set_filters <- function(.data, expressions) {
stop("filter expressions must be either an expression or a list of expressions", call. = FALSE)
}

if (isTRUE(exclude)) {
# dplyr::filter_out() semantics: drop rows where predicate is TRUE;
# keep rows where predicate is FALSE or NA.
new_filter <- (!new_filter) | is.na(new_filter)
}

if (isTRUE(.data$filtered_rows)) {
# TRUE is default (i.e. no filter yet), so we don't need to & with it
.data$filtered_rows <- new_filter
Expand Down
41 changes: 41 additions & 0 deletions r/tests/testthat/test-dplyr-filter.R
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,44 @@ test_that("filter() with aggregation expressions errors", {
"not supported in filter"
)
})

test_that("filter_out() basic", {
compare_dplyr_binding(
.input |>
filter_out(chr == "b") |>
select(chr, int, lgl) |>
collect(),
tbl
)
})

test_that("filter_out() keeps NA values in predicate result", {
compare_dplyr_binding(
.input |>
filter_out(lgl) |>
select(chr, int, lgl) |>
collect(),
tbl
)
})

test_that("filter_out() with multiple conditions", {
compare_dplyr_binding(
.input |>
filter_out(dbl > 2, chr %in% c("d", "f")) |>
collect(),
tbl
)
})

test_that("More complex select/filter_out", {
compare_dplyr_binding(
.input |>
filter_out(dbl > 2, chr == "d" | chr == "f") |>
select(chr, int, lgl) |>
filter(int < 5) |>
select(int, chr) |>
collect(),
tbl
)
})
Loading