使用dplyr中的mutate_at与自定义函数

11

我想从一个表中取出两个变量,将它们除以第三个变量,并将这些计算作为两个新列添加。 mutate_at 已经让我接近目标,但在下面的自定义函数 f 中,我想要访问数据集中的另一列。 有任何建议或其他整洁的工具方法吗?

library(dplyr)
# this works fine but is NOT what I want
f <- function(fld){
  fld/5
}

# This IS what I want where wt is a field in the data
f <- function(fld){
  fld/wt
}

mutate_at(mtcars, .vars = vars(mpg, cyl), .funs = funs(xyz = f))

# This works but is pretty clumsy
f <- function(fld, dat) fld/dat$wt
mutate_at(mtcars, .vars = vars(mpg, cyl), .funs = funs(xyz = f(., mtcars)))

# This is closer but still it would be better if the function allowed the dataset to be submitted to the function without restating the name of the dataset

f <- function(fld, second){
  fld/second
}

mutate_at(mtcars, .vars = vars(mpg, cyl), .funs = funs(xyz = f(., wt)))
4个回答

14

dplyr 1.0.6的更新版本:

mtcars %>% 
  mutate(across(c(mpg, cyl), ~ . / wt))

或者选择这个,可能会更慢

mtcars %>% 
  mutate(across(c(mpg, cyl), `/`, wt))

之前的回答:

library(tidyverse)
f <- function(num, denom) num/denom

mtcars %>% 
  mutate_at(vars(mpg, cyl), f, denom = quote(wt))

虽然在这个特定的例子中,不需要使用自定义函数。

mtcars %>% 
  mutate_at(vars(mpg, cyl), `/`, quote(wt))

我认为这个答案和问题中的示例一样笨拙。当然,您已经剥离了所有的.funs=funs...混乱,但那不是问题的重点。这只是另外两种方法,完全可以做到问题解决方案所做的事情:调用函数并在mutate()内传递第二个参数。问题的关键是找到一种避免必须传递第二个参数的方法。 - randy
抱歉没有表达清楚(并且使用了对抗性的语言)。分母必须提供,但是可以在不将其作为函数参数的情况下完成。我认为这是问题的意图,因为有“这就是我想要的”这个陈述,后面跟着一个只有一个参数的函数,并且因为问题已经包含了两个可行的解决方案,它们做的事情与这个答案相同,但语法略有不同。我认为通过展示选项f <- function(num) num/cur_data()$denom,可以改进这个答案。如果您不介意,我会添加它。 - randy

10
也许是这样的吗?
f <- function(fld,var){
    fld/var
}

mtcars %>%
    mutate_at(vars(mpg,cyl), .funs = funs(xyz = f(.,wt)))

编辑(2020-08-24):

自 2020 年下半年第二学期起,随着 dplyr 1.0.0 的发布,mutate_at 已被 mutateacross 函数的组合所取代:

mtcars %>%
    mutate(across(c(mpg, cyl), ~ f(.,wt), .names = "{col}_xyz"))

1

有一个cur_data()函数可以帮助使mutate_at()调用更加紧凑,因为您不必指定应用于每列的函数的第二个参数

f <- function(fld){
  fld / cur_data()$wt
}
mutate_at(mtcars, .vars=vars(mpg, cyl), .funs=funs(xyz = f))

附加说明:

  1. 如果您需要函数引用分组变量,请使用cur_data_all()
  2. mutate_at现在已被mutate(.data, across())取代,因此最好这样做
mtcars %>% mutate(across(.cols=c(mpg, cyl), .fns=f, .names='{.col}_xyz'))

1
为什么不简单地这样做?
mutate(mtcars, mpg2 = mpg / wt, cyl2 = cyl / wt)

这对于一些字段是有效的,但不够灵活,在处理10个字段时会很麻烦。如果您改变了后缀(“2”),则必须在多个地方进行更改等。 - ZRoss
同意,我想我错过了你问题的关键。 - whalea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接