Konrad
Konrad

Reputation: 18595

Correctly using rlang::as_function formula within .predicate argument of mutate_if

I would like to use ~ . %% 1 == 0 formula within .predicate argument of mutate_if in the following manner:

dta %>%
    mutate_if(.predicate =  ~ . %% 1 == 0,
              .funs = funs(. + 10))

where dta corresponds to the following tibble:

dta <- tibble(colA = c("a", "b"),
              colB = 1:2,
              colC = c(1.1, 2.2))

This currently produces the following error:

Error in .%%1 : non-numeric argument to binary operator

Background

In the documentation of dplyr::mutate_if states:

.predicate A predicate function to be applied to the columns or a logical vector. The variables for which .predicate is or returns TRUE are selected. This argument is passed to rlang::as_function() and thus supports quosure-style lambda functions and strings representing function names.

Conseqnuetly the formula ~ . %% 1 == 0 can be used in the following manner:

f_rlang_check <- rlang::as_function( ~ . %% 1 == 0)

f_rlang_check(c(1.1, 1))
[1] FALSE  TRUE

Clearly the formula is correct; however it cannot be directly passed to the rlang::as_function() as shown above.

dta %>%
    mutate_if(.predicate = f_rlang_check,
              .funs = funs(. + 10))

Produces identical error

Upvotes: 1

Views: 216

Answers (1)

IceCreamToucan
IceCreamToucan

Reputation: 28695

mutate_if will atempt to evaluate the predicate function for all columns, so you get an error when attempting to use the binary operator %% with a non-numeric argument colA. You can fix this with is.numeric and &&.

The predicate should return a single logical value, so you may want to use all or any.

dta %>%
    mutate_if(.predicate = ~ is.numeric(.) && all(. %% 1 == 0),
              .funs = funs(. + 10))

Upvotes: 1

Related Questions