3

I had this following code that worked just fine before my R updated from version 4.0 to 4.1 note, this is all part of a helper function I've written for a piece of software I'm developing, and I've replaced things accordingly. Without context, this functionality seems meaningless, please dont focus on what the code appears to be trying to accomplish here.

input1 <- data.frame(a = as.numeric(c(0, NA, 0, 0, NA)),
                       b = as.factor(c("f", "f", NA, "f", NA)),
                       stringsAsFactors = FALSE)
input2 <- data.frame(a = as.numeric(1),
                     b = factor(c("m")),
                     c = factor(c("married")),
                     d = factor(c("AZ")),
                     e = as.character(0),
                     f = as.integer(59),
                     g = as.Date("2021-02-14"),
                     stringsAsFactors = FALSE
                     )

input2 <- input2 %>% dplyr::select( tidyselect::all_of( colnames(fake_data_) ) )

# error occurs here, in the second mutate
input1 %>%
  dplyr::mutate(dplyr::across(.fns = as.character)) %>%
  dplyr::mutate(dplyr::across( .cols = tidyselect::everything(),
                               .fns  = ~eval(parse(text = paste0( "as.",
                                                                  class( input2[[ dplyr::cur_column() ]]),
                                                                  "(.)")))
                               ))

I get error Error: Problem with `mutate()` input `..1`. i `..1 = dplyr::across(...)`. x 'list' object cannot be coerced to type 'double'

could someone help with this please, thanks in advance!

Voy
  • 99
  • 4

2 Answers2

0

okay so i did some digging and found that this works

input1 %>%
  dplyr::mutate(dplyr::across(.fns = as.character)) %>%
  dplyr::mutate(dplyr::across( .cols = tidyselect::everything(),
                               .fns  = function(x) eval(parse(text = paste0( "as.",
                                                                  class( input2[[ dplyr::cur_column() ]]),
                                                                  "(x)")))
                               ))

now my question is, why doesnt the . and purrr style function ~ work? Even better, what does across try to do when it sees this ., how is this . used in general? I've always struggled with this and could never find documentation of it

Voy
  • 99
  • 4
0

When it comes to the ~ for purrr, you can see how they work easily

library(rlang)

fun <- as_function(~.x + 1)

fun
#> <lambda>
#> function (..., .x = ..1, .y = ..2, . = ..1) 
#> .x + 1
#> attr(,"class")
#> [1] "rlang_lambda_function" "function"

Here we see that it will make a function, and it maps .x's default to the first argument, .y's default to the second argument, and .'s default to the first argument as well.

The issue you are encountering is likely due to dplyr's evaluation mask and computing on the language.

You are expressing your function as follows

 fun2 <- as_function(~eval(parse(text = paste0( "as.", 
   class( input2[[ dplyr::cur_column() ]]),
   "(.)"))))

and expecting a purrr style function to be used, however what is actually happening is each call for the columns have their expressions substituted with the column names prior to the time of evaluation. Since "(.)" is a character, there is no replacement happening and instead tries to use the . prefix of magrittr.

You can can a sense of what is happening if you do the following

input1 %>%
  dplyr::mutate(dplyr::across(.fns = ~{browser(); as.character(.)}))

If the browser controls do no appear, type n in the console to see the next step, it should read as.character(a), instead of as.character(.).

(Note: type Q to quit the debug browser)

The following should produce the results you want as well:

input1 %>%
  dplyr::mutate(dplyr::across(.fns = as.character)) %>%
  dplyr::mutate(dplyr::across( .cols = tidyselect::everything(),
                               .fns  = ~ do.call(paste0("as.",class(input2[[cur_column()]])), list(.))))
  ))

As for why this is occurring in different versions, I do not know. Version 3.6.3 on my computer was producing results similar to 4.1.

Justin Landis
  • 1,981
  • 7
  • 9