【问题标题】:Using dplyr quosure custom function with mutate_at将 dplyr quosure 自定义函数与 mutate_at 一起使用
【发布时间】:2020-01-27 23:01:16
【问题描述】:

我正在尝试构建一个辅助函数来提取参数中给出的列中的数字。我可以在mutate 中使用我的函数(并对所有感兴趣的列重复它),但它似乎在mutate_at 中不起作用。

这是我的数据的示例:

> set.seed(20190928)
> evalYr <- 2018
> n <- 5
> (df <- data.frame(
+     AY = sample(2016:2019, n, replace = T),
+     Pay00 = rgamma(n, 2, 1/1000),
+     Pay01 = rgamma(n, 2, 1/1000),
+     Pay02 = rgamma(n, 2, 1/1000),
+     Pay03 = rgamma(n, 2, 1/1000)
+ ))
    AY     Pay00     Pay01     Pay02     Pay03
1 2018 2520.3772 2338.9490  919.8245  629.1657
2 2016  259.7804 1543.4450  661.6488 2382.7916
3 2018 2446.3075  312.5143 2297.9717  942.5627
4 2017 1386.6288 4179.0352 2370.2669 1846.5838
5 2018  541.8261 2104.4589 2622.1758 2606.0694

所以我已经构建(使用dplyr 语法)这个助手来改变我拥有的每个PayXX 列:

# Helper function to get the number inside column `PayXX` name
f1 <- function(pmt) enquo(pmt) %>% quo_name() %>% str_extract('(\\d)+') %>% as.numeric()

这个函数在dplyr::mutate 上运行良好:

> df %>% mutate(Pay00_numcol = f1(Pay00),
+               Pay01_numcol = f1(Pay01),
+               Pay02_numcol = f1(Pay02),
+               Pay03_numcol = f1(Pay03))
    AY     Pay00     Pay01     Pay02     Pay03 Pay00_numcol Pay01_numcol Pay02_numcol Pay03_numcol
1 2018 2520.3772 2338.9490  919.8245  629.1657            0            1            2            3
2 2016  259.7804 1543.4450  661.6488 2382.7916            0            1            2            3
3 2018 2446.3075  312.5143 2297.9717  942.5627            0            1            2            3
4 2017 1386.6288 4179.0352 2370.2669 1846.5838            0            1            2            3
5 2018  541.8261 2104.4589 2622.1758 2606.0694            0            1            2            3

但是当我尝试在mutate_at 中使用相同的函数时,它会返回 NA:

> df %>% mutate_at(vars(starts_with('Pay')), list(numcol = ~f1(.)))
    AY     Pay00     Pay01     Pay02     Pay03 Pay00_numcol Pay01_numcol Pay02_numcol Pay03_numcol
1 2018 2520.3772 2338.9490  919.8245  629.1657           NA           NA           NA           NA
2 2016  259.7804 1543.4450  661.6488 2382.7916           NA           NA           NA           NA
3 2018 2446.3075  312.5143 2297.9717  942.5627           NA           NA           NA           NA
4 2017 1386.6288 4179.0352 2370.2669 1846.5838           NA           NA           NA           NA
5 2018  541.8261 2104.4589 2622.1758 2606.0694           NA           NA           NA           NA

有人遇到过类似的问题吗?这种情况下mutate_at函数该如何处理?

谢谢,

可重现的示例

library(tidyverse)
library(stringr)
set.seed(20190928)
evalYr <- 2018
n <- 5
(df <- data.frame(
    AY = sample(2016:2019, n, replace = T),
    Pay00 = rgamma(n, 2, 1/1000),
    Pay01 = rgamma(n, 2, 1/1000),
    Pay02 = rgamma(n, 2, 1/1000),
    Pay03 = rgamma(n, 2, 1/1000)
))

# Helper function to get the number inside column `PayXX` name
f1 <- function(pmt) enquo(pmt) %>% quo_name() %>% str_extract('(\\d)+') %>% as.numeric()

# Working
df %>% mutate(Pay00_numcol = f1(Pay00),
              Pay01_numcol = f1(Pay01),
              Pay02_numcol = f1(Pay02),
              Pay03_numcol = f1(Pay03))

# Not working
df %>% mutate_at(vars(starts_with('Pay')), list(numcol = ~f1(.)))

【问题讨论】:

  • 我不知道,但以下代码有效:df %&gt;% mutate_at(vars(starts_with('Pay')), list(numcol = f1))。不知何故,这种语法 ~f1(.) 不起作用。
  • 仅供参考 stringrtidyverse 加载的包之一,所以你不需要单独加载它

标签: r dplyr quosure


【解决方案1】:

我想到的第一个方法是重塑数据可能会更容易。但是,仍然需要纠结tidyr 函数才能获得 1)“Pay00”、“Pay01”等列; 2)提取数字; 3) 操作,以便您可以使用tidyr::spread 回到宽形;和 4) 传播并删除我添加的“_value”位。

我相信使用最新版本的tidyr 可以更好地执行此操作,因为新的pivot_wider 函数应该能够将多个列作为value。我完全没有弄乱这个,但也许其他人可以把它写出来。

library(tidyverse)

df %>%
  rowid_to_column() %>%
  gather(key, value, -AY, -rowid) %>%
  mutate(numcol = as.numeric(str_extract(key, "\\d+$"))) %>%
  gather(key = coltype, value, value, numcol) %>%
  unite(key, key, coltype) %>%
  spread(key, value) %>%
  select(AY, ends_with("value"), ends_with("numcol")) %>%
  rename_all(str_remove, "_value")
#>     AY     Pay00     Pay01     Pay02     Pay03 Pay00_numcol Pay01_numcol
#> 1 2018 2520.3772 2338.9490  919.8245  629.1657            0            1
#> 2 2016  259.7804 1543.4450  661.6488 2382.7916            0            1
#> 3 2018 2446.3075  312.5143 2297.9717  942.5627            0            1
#> 4 2017 1386.6288 4179.0352 2370.2669 1846.5838            0            1
#> 5 2018  541.8261 2104.4589 2622.1758 2606.0694            0            1
#>   Pay02_numcol Pay03_numcol
#> 1            2            3
#> 2            2            3
#> 3            2            3
#> 4            2            3
#> 5            2            3

或者,如果您想坚持使用 tidyeval 方法:获取要调用函数的列的名称。请注意,如果您使用 list(numcol = ~f1(.)) 表示法,所有这些 quosures 都会显示为 .

f1 <- function(pmt) {
  str_extract(rlang::as_name(enquo(pmt)), "\\d+$") %>%
    as.numeric()
}

df %>%
  mutate_at(vars(starts_with("Pay")), list(numcol = f1))
# same output as prev

【讨论】:

  • 非常感谢您的帮助!我的问题有点复杂(我需要根据列的名称进行特定的计算,即 Pay01、Pay02 等。所以通过收集/传播的争论绝对是我想要的。但是,如果我想要坚持我原来的方法,我想将多个参数传递给 .funs 内部的函数(在我的真实情况下,f1 有 4 个参数),如果不使用 ~ 运算符,我将如何处理这个问题?谢谢你的帮助,一次再次。
  • 变得过于复杂可能超出了我的 tidyeval 技能。但是您可以在 mutate_at 调用内部但在 list 调用外部传递其他参数
猜你喜欢
  • 2018-09-20
  • 1970-01-01
  • 2020-04-11
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 2021-07-20
  • 2020-07-08
  • 1970-01-01
相关资源
最近更新 更多