function args for mutate_if in dplyr work for soft-depreciated funs() but not for list()

Viewed 67

I am trying to update my following code because funs( MY_FUN ) is soft depreciated. I know that the replacement for this should be list( ~MY_FUN ), but this doesnt seem to be working for my code.

Here are my data frames:

fake_data <- data.frame(var1 = rep("TEMP", times = 5),
                        var2 = rep("TEMP", times = 5),
                        var3 = c(1:5),
                        stringsAsFactors = FALSE)

lookup_sub <- data.frame(var_names = c("var1", "var2", "var3"),
                         example_value = c("a", "b", "c"),
                         stringsAsFactors = FALSE)

The following line of code works and does exactly what I want it to:

library(tidyverse)
library(rlang)
fake_data %>%
        mutate_if(.predicate = rlang::as_function(function(x){identical("TEMP", unique(x))}),
                  .funs = funs(as.character((lookup_sub %>% filter(var_names == quo_name(quo(.))) %>% pull(example_value))[1])))

Resulting in

  var1 var2 var3
1    a    b    1
2    a    b    2
3    a    b    3
4    a    b    4
5    a    b    5

BUT using the not-depreciated argument gives all NA values to the rows that evaluate to TRUE in the predicate

fake_data %>%
        mutate_if(.predicate = rlang::as_function(function(x){identical("TEMP", unique(x))}),
                  .funs = list(~as.character((lookup_sub %>% filter(var_names == quo_name(quo(.))) %>% pull(example_value))[1])))

which results in

  var1 var2 var3
1 <NA> <NA>    1
2 <NA> <NA>    2
3 <NA> <NA>    3
4 <NA> <NA>    4
5 <NA> <NA>    5

Can anybody explain this to me? I know that the problem occurs because of quo_name(quo(.)) but I dont know how to fix it. Thank you!

1 Answers

I think the previous comments are right, this definitely seems to be a problem with how the mutate_if function is built. I have another example of very interesting problem that - I believe - is impossible to answer within the current mutate_if framework.

Consider this fake data

smooth_data <- data.frame(`s(gam_variable_1_output)` = c(1.004, 1.345, 1.460, 1.540),
                          `s(gam_variable_2_output)` = c(1, 1, 1, 1),
                          grouping_variable_1 = c(1,2,3,4),
                          grouping_variable_2 = c("a", "b", "a", "b"),
                          grouping_variable_3 = c(500, 500, 500, 500),
                          stringsAsFactors = FALSE) 

I want to apply a function to all of the smooth variables (variables that begin with "s(") only if the levels of that smooth variable arent 1 unique value.

This should be accomplished through the following

smooth_data %>%
mutate_if(.predicate = funs(length(unique(.)) != 1 & grepl("s\\(", quo_name(quo(.)))),
          ~2) # we'll use this for example

Such that this should result in the following (I removed the rownames / numbers from R output)

s(gam_variable_1_output)
                       2
                       2
                       2
                       2
s(gam_variable_2_output)
                       1
                       1
                       1
                       1
grouping_variable_1       grouping_variable_2       grouping_variable_3
                  1                       "a"                       500
                  2                       "b"                       500
                  3                       "a"                       500
                  4                       "b"                       500

But the function doesnt work. If we want to split the predicate up, then we see something interesting.

#### FIRST PART OF CONDITIONAL: only look at variables that arent a single value
## THIS WORKS
smooth_data %>% mutate_if(funs(length(unique(.)) != 1), ~2)
## ^ THAT IS THE SAME AS THIS; THIS WORKS
smooth_data %>% mutate_if(~length(unique(.)) != 1, ~2)
## THIS DOES NOT WORK
smooth_data %>% mutate_if(length(unique(.)) != 1, ~2)


#### SECOND PART OF CONDITIONAL: only look at variables that have name including `s(`
## THIS WORKS
smooth_data %>% mutate_if(grepl("s\\(", names(.)), ~2)

## THIS THROWS ERROR (BECAUSE OF `names(.)`)
smooth_data %>% mutate_if(~grepl("s\\(", names(.)), ~2)

## THIS DOES NOT WORK
smooth_data %>% mutate_if(~grepl("s\\(", quo_name(quo(.))), ~2)

## WHICH IS THE SAME AS THIS
smooth_data %>% mutate_if(funs(grepl("s\\(", quo_name(quo(.)))), ~2)

## AND THIS THROWS ERROR (BECAUSE IT NEEDS ~)
smooth_data %>% mutate_if(grepl("s\\(", quo_name(quo(.))), ~2)

This is interesting because it suggests to me that my main line of code cant run since the first part of the conditional needs to be in the funs() setting, while the second part cannot be in the funs() setting. This isnt an answer to the question but I think it clearly shows the problem with mutate_if

Related