# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. library(dplyr, warn.conflicts = FALSE) test_that("expand_across correctly expands quosures", { # single unnamed function expect_across_equal( quos(across(c(dbl, dbl2), round)), quos( dbl = round(dbl), dbl2 = round(dbl2) ), example_data ) # multiple unnamed functions expect_across_equal( quos(across(c(dbl, dbl2), list(exp, sqrt))), quos( dbl_1 = exp(dbl), dbl_2 = sqrt(dbl), dbl2_1 = exp(dbl2), dbl2_2 = sqrt(dbl2) ), example_data ) # single named function expect_across_equal( quos(across(c(dbl, dbl2), list("fun1" = round))), quos( dbl_fun1 = round(dbl), dbl2_fun1 = round(dbl2) ), example_data ) # multiple named functions expect_across_equal( quos(across(c(dbl, dbl2), list("fun1" = round, "fun2" = sqrt))), quos( dbl_fun1 = round(dbl), dbl_fun2 = sqrt(dbl), dbl2_fun1 = round(dbl2), dbl2_fun2 = sqrt(dbl2) ), example_data ) # mix of named and unnamed functions expect_across_equal( quos(across(c(dbl, dbl2), list(round, "fun2" = sqrt))), quos( dbl_1 = round(dbl), dbl_fun2 = sqrt(dbl), dbl2_1 = round(dbl2), dbl2_fun2 = sqrt(dbl2) ), example_data ) # across() with no functions returns columns unchanged expect_across_equal( quos(across(starts_with("dbl"))), quos( dbl = dbl, dbl2 = dbl2 ), example_data ) # across() arguments not in default order expect_across_equal( quos(across(.fns = round, c(dbl, dbl2))), quos( dbl = round(dbl), dbl2 = round(dbl2) ), example_data ) # across() with no columns named expect_across_equal( quos(across(.fns = round)), quos( int = round(int), dbl = round(dbl), dbl2 = round(dbl2) ), example_data %>% select(int, dbl, dbl2) ) # column selection via dynamic variable name int <- c("dbl", "dbl2") expect_across_equal( quos(across(all_of(int), sqrt)), quos( dbl = sqrt(dbl), dbl2 = sqrt(dbl2) ), example_data ) # alternative ways of specifying .fns - as a list expect_across_equal( quos(across(1:dbl2, list(round))), quos( int_1 = round(int), dbl_1 = round(dbl), dbl2_1 = round(dbl2) ), example_data ) # supply .fns as a one-item vector expect_across_equal( quos(across(1:dbl2, c(round))), quos( int_1 = round(int), dbl_1 = round(dbl), dbl2_1 = round(dbl2) ), example_data ) # .names argument expect_across_equal( quos(across(c(dbl, dbl2), round, .names = "{.col}.{.fn}")), quos( dbl.1 = round(dbl), dbl2.1 = round(dbl2) ), example_data ) # names argument with custom text expect_across_equal( quos(across(c(dbl, dbl2), round, .names = "round_{.col}")), quos( round_dbl = round(dbl), round_dbl2 = round(dbl2) ), example_data ) # names argument supplied but no functions expect_across_equal( quos(across(starts_with("dbl"), .names = "new_{.col}")), quos( new_dbl = dbl, new_dbl2 = dbl2 ), example_data ) # .names argument and functions named expect_across_equal( quos(across(c(dbl, dbl2), list("my_round" = round, "my_exp" = exp), .names = "{.col}.{.fn}")), quos( dbl.my_round = round(dbl), dbl.my_exp = exp(dbl), dbl2.my_round = round(dbl2), dbl2.my_exp = exp(dbl2) ), example_data ) # .names argument and mix of named and unnamed functions expect_across_equal( quos(across(c(dbl, dbl2), list(round, "my_exp" = exp), .names = "{.col}.{.fn}")), quos( dbl.1 = round(dbl), dbl.my_exp = exp(dbl), dbl2.1 = round(dbl2), dbl2.my_exp = exp(dbl2) ), example_data ) # dodgy .names specification expect_error( expand_across( as_adq(example_data), quos(across(c(dbl, dbl2), list(round, "my_exp" = exp), .names = "zarg")) ), regexp = "`.names` specification must produce (number of columns * number of functions) names.", fixed = TRUE ) # Using package name prefix (ARROW-17724) expect_across_equal( quos(across(c(dbl, dbl2), base::round)), quos( dbl = base::round(dbl), dbl2 = base::round(dbl2) ), example_data ) expect_across_equal( quos(across(c(dbl, dbl2), c(base::round, base::sqrt))), quos( dbl_1 = base::round(dbl), dbl_2 = base::sqrt(dbl), dbl2_1 = base::round(dbl2), dbl2_2 = base::sqrt(dbl2) ), example_data ) skip_if_not_available("dataset") # ellipses (...) are a deprecated argument # abandon_ship message offers multiple suggestions expect_snapshot( InMemoryDataset$create(example_data) %>% mutate(across(c(dbl, dbl2), round, digits = -1)), error = TRUE ) }) test_that("purrr-style lambda functions are supported", { # using `.x` inside lambda functions expect_across_equal( quos(across(c(dbl, dbl2), ~ round(.x, digits = 0))), quos( dbl = round(dbl, digits = 0), dbl2 = round(dbl2, digits = 0) ), example_data ) # using `.` inside lambda functions expect_across_equal( quos(across(c(dbl, dbl2), ~ round(., digits = 0))), quos( dbl = round(dbl, digits = 0), dbl2 = round(dbl2, digits = 0) ), example_data ) # testing both `.` and `.x` in lambda functions expect_across_equal( quos(across(c(dbl, dbl2), c(~ round(.x, digits = 0), ~ . * 2))), quos( dbl_1 = round(dbl, digits = 0), dbl_2 = dbl * 2, dbl2_1 = round(dbl2, digits = 0), dbl2_2 = dbl2 * 2 ), example_data ) # internal function for lambda functions expect_identical( arrow:::expr_substitute( quote(~ round(.x * 2, digits = 0)), sym(".x"), sym("dbl2") ), quote(~ round(dbl2 * 2, digits = 0)) ) }) test_that("ARROW-14071 - R functions from a user's environment", { makeWhole <- function(x) round(x, digits = 0) compare_dplyr_binding( .input %>% mutate(across(c(int, dbl), makeWhole)) %>% collect(), example_data ) }) test_that("function(x)-style lambda functions are not supported", { expect_error( expand_across(as_adq(example_data), quos(across(.cols = c(dbl, dbl2), list(function(x) { head(x, 1) }, function(x) { head(x, 1) })))), regexp = "Anonymous functions are not yet supported in Arrow" ) expect_error( expand_across( as_adq(example_data), quos(across(.cols = c(dbl, dbl2), function(x) { head(x, 1) })) ), regexp = "Anonymous functions are not yet supported in Arrow" ) }) test_that("if_all() and if_any() are supported", { expect_across_equal( quos(if_any(everything(), ~ is.na(.x))), quos(is.na(int) | is.na(dbl) | is.na(dbl2) | is.na(lgl) | is.na(false) | is.na(chr) | is.na(fct)), example_data ) expect_across_equal( quos(if_all(everything(), ~ is.na(.x))), quos(is.na(int) & is.na(dbl) & is.na(dbl2) & is.na(lgl) & is.na(false) & is.na(chr) & is.na(fct)), example_data ) })