首页 > 解决方案 > 新的 dplyr 版本 1.0.6(或者可能更早)似乎滥用了 plyr 。dplyr中的点突变

问题描述

在我的 R 从 4.0 版更新到 4.1 版之前,我的以下代码运行良好,这是我为我正在开发的软件编写的辅助函数的一部分,我已经相应地替换了一些东西。没有上下文,这个功能似乎毫无意义,请不要专注于代码似乎试图在这里完成的事情。

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() ]]),
                                                                  "(.)")))
                               ))

我收到错误Error: Problem with `mutate()` input `..1`. i `..1 = dplyr::across(...)`. x 'list' object cannot be coerced to type 'double'

有人可以帮忙吗,在此先感谢!

标签: rdynamicdplyracross

解决方案


当谈到~forpurrr时,您可以看到它们是如何轻松工作的

library(rlang)

fun <- as_function(~.x + 1)

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

在这里我们看到它将创建一个函数,并将.x'default 映射到第一个参数,.y'default 映射到第二个参数,以及.'default 到第一个参数。

您遇到的问题可能是由于dplyr的评估掩码和对语言的计算。

您正在表达您的功能如下

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

并期望使用purrr样式函数,但是实际发生的情况是,每次对列的调用都在评估之前将其表达式替换为列名。由于"(.)"是一个字符,因此不会发生替换,而是尝试使用.前缀 of magrittr

如果您执行以下操作,您可以了解正在发生的事情

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

如果没有出现浏览器控件,n请在控制台中键入以查看下一步,它应该显示为as.character(a),而不是as.character(.)

(注:输入Q退出调试浏览器)

以下内容也应该产生您想要的结果:

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(.))))
  ))

至于为什么在不同的版本中会出现这种情况,我不知道。我电脑上的 3.6.3 版产生的结果与 4.1 版相似。


推荐阅读