在 dplyr mutate_at 调用中使用多列的函数
Posted
技术标签:
【中文标题】在 dplyr mutate_at 调用中使用多列的函数【英文标题】:Using functions of multiple columns in a dplyr mutate_at call 【发布时间】:2017-01-05 16:55:42 【问题描述】:我想使用 dplyr 的 mutate_at
函数将函数应用于数据框中的多个列,其中函数输入直接应用它的列以及数据框中的另一列。
作为一个具体的例子,我希望改变以下数据帧
# Example input dataframe
df <- data.frame(
x = c(TRUE, TRUE, FALSE),
y = c("Hello", "Hola", "Ciao"),
z = c("World", "ao", "HaOlam")
)
使用与此类似的mutate_at
调用
df %>%
mutate_at(.vars = vars(y, z),
.funs = ifelse(x, ., NA))
返回一个看起来像这样的数据框
# Desired output dataframe
df2 <- data.frame(x = c(TRUE, TRUE, FALSE),
y_1 = c("Hello", "Hola", NA),
z_1 = c("World", "ao", NA))
所需的mutate_at
调用类似于以下对mutate
的调用:
df %>%
mutate(y_1 = ifelse(x, y, NA),
z_1 = ifelse(x, z, NA))
我知道这可以通过多种方式在基础 R 中完成,但我特别想使用 dplyr 的 mutate_at
函数来实现这一目标,以提高可读性、与数据库的接口等。
以下是在 *** 上提出的一些类似问题,没有解决我在这里提出的问题:
adding multiple columns in a dplyr mutate call
dplyr::mutate to add multiple values
Use of column inside sum() function using dplyr's mutate() function
【问题讨论】:
df %>% mutate_at(vars(y, z), funs(ifelse(x, ., NA)))
@eipi10 啊,好的。因此,如果我实际上将ifelse(x, ., NA)
包装在对funs()
的调用中,那么上面的代码就会起作用。谢谢!我已经检查了您的解决方案,并且效果很好。您的解决方案正是我想要的!
【参考方案1】:
@eipi10 在@eipi10 对这个问题的评论中回答了这个问题,但我写在这里是为了后代。
这里的解决方案是使用:
df %>%
mutate_at(.vars = vars(y, z),
.funs = list(~ ifelse(x, ., NA)))
您还可以将新的across()
函数与mutate()
一起使用,如下所示:
df %>%
mutate(across(c(y, z), ~ ifelse(x, ., NA)))
此处使用公式运算符(如~ ifelse(...)
)表明ifelse(x, ., NA)
是在对mutate_at()
的调用中定义的匿名函数。
这类似于在调用 mutate_at()
之外定义函数,如下所示:
temp_fn <- function(input) ifelse(test = df[["x"]],
yes = input,
no = NA)
df %>%
mutate_at(.vars = vars(y, z),
.funs = temp_fn)
关于 dplyr 中语法更改的注意事项: 在 dplyr 版本 0.8.0 之前,您只需编写 .funs = funs(ifelse(x, . , NA))
,但 funs()
函数已被弃用,并将很快从 dplyr 中删除。
【讨论】:
“这里使用funs()表示ifelse(x, ., NA)是匿名函数”----funs()
与传统匿名函数function(x)
有何不同?
在我的经验中最值得注意的是它需要更少的输入并且具有类似的可读性。但是,它还允许您提供匿名函数列表(例如 funs(avg = mean(.), total = sum(., na.rm = TRUE))
。参见 rdocumentation.org/packages/dplyr/versions/0.7.6/topics/funs。
函数定义在mutate
之外的例子只有在df
在函数定义和使用之间没有改变的情况下才有效。这似乎是一个冒险的策略。例如,如果有人先对数据进行分组怎么办?
同意,我不建议这样做。该示例仅用于帮助解释实际解决方案的工作原理。【参考方案2】:
作为对之前回复的补充,如果您希望 mutate_at()
添加新变量(而不是替换),名称如原始问题中的 z_1
和 y_1
,您只需:
across()
:添加 .names=".col_1"
,或者使用 list('1'=~ifelse(x, ., NA)
(反引号!)
dplyr [0.8, 1[:使用list('1'=~ifelse(x, ., NA)
dplyr :使用funs('1'=ifelse(x, ., NA)
library(tidyverse)
df <- data.frame(
x = c(TRUE, TRUE, FALSE),
y = c("Hello", "Hola", "Ciao"),
z = c("World", "ao", "HaOlam")
)
## Version >=1
df %>%
mutate(across(c(y, z),
list(~ifelse(x, ., NA)),
.names=".col_1"))
#> x y z y_1 z_1
#> 1 TRUE Hello World Hello World
#> 2 TRUE Hola ao Hola ao
#> 3 FALSE Ciao HaOlam <NA> <NA>
## 0.8 - <1
df %>%
mutate_at(.vars = vars(y, z),
.funs = list(`1`=~ifelse(x, ., NA)))
#> x y z y_1 z_1
#> 1 TRUE Hello World Hello World
#> 2 TRUE Hola ao Hola ao
#> 3 FALSE Ciao HaOlam <NA> <NA>
## Before 0.8
df %>%
mutate_at(.vars = vars(y, z),
.funs = funs(`1`=ifelse(x, ., NA)))
#> Warning: `funs()` is deprecated as of dplyr 0.8.0.
#> Please use a list of either functions or lambdas:
#>
#> # Simple named list:
#> list(mean = mean, median = median)
#>
#> # Auto named with `tibble::lst()`:
#> tibble::lst(mean, median)
#>
#> # Using lambdas
#> list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_warnings()` to see where this warning was generated.
#> x y z y_1 z_1
#> 1 TRUE Hello World Hello World
#> 2 TRUE Hola ao Hola ao
#> 3 FALSE Ciao HaOlam <NA> <NA>
由reprex package (v0.3.0) 于 2020 年 10 月 3 日创建
更多细节和技巧请见:Create new variables with mutate_at while keeping the original ones
【讨论】:
当函数定义在对mutate_at
的调用之外时,它是如何工作的?
不确定我得到你的问题@randy,如果主函数是在我们的外部定义的没有区别(注意ifelse
本身是在调用之外定义的)?以上是关于在 dplyr mutate_at 调用中使用多列的函数的主要内容,如果未能解决你的问题,请参考以下文章
R语言dplyr包arrage函数排序dataframe实战:单列排序多列排序自定义排序
R语言dplyr包使用case_when函数和mutate函数生成新的数据列实战:基于单列生成新的数据列基于多列生成新的数据列