【问题标题】:dplyr without hard-coding the variable namesdplyr 没有硬编码变量名
【发布时间】:2014-03-27 03:47:25
【问题描述】:

是否可以在不硬编码变量名的情况下使用 dplyr 的 mutate 函数?例如,以下代码有效,因为我硬编码了名称 Var1:

            > d=expand.grid(1:3, 20:22)
            > d
            Var1 Var2
            1    1   20
            2    2   20
            3    3   20
            4    1   21
            5    2   21
            6    3   21
            7    1   22
            8    2   22
            9    3   22
            > d=mutate(d, x=percent_rank(Var1))
            > d
            Var1 Var2     x
            1    1   20 0.000
            2    2   20 0.375
            3    3   20 0.750
            4    1   21 0.000
            5    2   21 0.375
            6    3   21 0.750
            7    1   22 0.000
            8    2   22 0.375
            9    3   22 0.750

但是,当我将变量的名称设为变量时,它不再起作用:

            > my.variable='Var1'
            > d=mutate(d, x=percent_rank(my.variable))
            > d
                Var1 Var2   x
            1    1   20 NaN
            2    2   20 NaN
            3    3   20 NaN
            4    1   21 NaN
            5    2   21 NaN
            6    3   21 NaN
            7    1   22 NaN
            8    2   22 NaN
            9    3   22 NaN

eval() 和 as.symbol() 函数似乎也没有帮助。

【问题讨论】:

  • 为什么要这样做?您将 x 设置为等于字符串的percent_rank。您还期望发生什么?

标签: r dplyr


【解决方案1】:

dplyr(等待新版本0.6.0)的开发版本中,引入了quosures和取消引用函数(!!UQ)来评估group_by/summarise/mutate中的引号,这变成了更容易

 my.variable <- quo(Var1)
 percent_rank <- function(x) rank(x)/max(rank(x))
 d %>% 
   mutate(x = percent_rank(!!my.variable))
#  Var1 Var2     x
#1    1   20 0.250
#2    2   20 0.625
#3    3   20 1.000
#4    1   21 0.250
#5    2   21 0.625
#6    3   21 1.000
#7    1   22 0.250
#8    2   22 0.625
#9    3   22 1.000

它还具有传递列名的其他功能

mynewvar <- 'x'
d %>% 
   mutate(!!mynewvar := percent_rank(!!my.variable))
#  Var1 Var2     x
#1    1   20 0.250
#2    2   20 0.625
#3    3   20 1.000
#4    1   21 0.250
#5    2   21 0.625
#6    3   21 1.000
#7    1   22 0.250
#8    2   22 0.625
#9    3   22 1.000

我们也可以创建一个函数并传递参数

f1 <- function(dat, myvar, colN){
  myvar <- enquo(myvar)
  colN <- quo_name(enquo(colN))
 
  dat %>%
      mutate(!!colN := percent_rank(!!myvar))
 }

f1(d, Var1, x)
#  Var1 Var2     x
#1    1   20 0.250
#2    2   20 0.625
#3    3   20 1.000
#4    1   21 0.250
#5    2   21 0.625
#6    3   21 1.000
#7    1   22 0.250
#8    2   22 0.625
#9    3   22 1.000

在上述函数中,enquobase R 中的substitute 具有相似的功能,即获取用户输入参数并将其转换为quosure。由于我们需要字符串中的列名,我们可以使用quo_name 来转换为字符串,并且mutate 调用中的评估是通过取消引用来完成的(!!UQ

数据

d <- expand.grid(1:3, 20:22)

【讨论】:

    【解决方案2】:

    您可以使用get 并精确定位对象“Var1”所在的环境。

    > my.variable = 'Var1'
    > mutate(d, x = percent_rank(get(my.variable, envir = as.environment(d))))
      Var1 Var2     x
    1    1   20 0.000
    2    2   20 0.375
    3    3   20 0.750
    4    1   21 0.000
    5    2   21 0.375
    6    3   21 0.750
    7    1   22 0.000
    8    2   22 0.375
    9    3   22 0.750
    

    我建议您在 Hadley Wickham 的“高级 R 编程”wiki 上阅读有关“非标准评估”的更多信息:http://adv-r.had.co.nz/Computing-on-the-language.html

    编辑

    这个答案最近被投票,所以我意识到我一年半前给出的解决方案并不是很好,我借此机会升级我的答案。

    从 dplyr 0.3 开始,您可以使用 dplyr 函数的标准评估版本,使用它们的“fun_”版本。

    如果你对变量进行一些计算,你还必须使用lazyeval包中的interp

    my.variable = "Var1"
    expr <- lazyeval::interp(~percent_rank(x), x = as.name(my.variable))
    mutate_(d, .dots = setNames(list(expr), "x"))
    Var1 Var2     x
    1    1   20 0.000
    2    2   20 0.375
    3    3   20 0.750
    4    1   21 0.000
    5    2   21 0.375
    6    3   21 0.750
    7    1   22 0.000
    8    2   22 0.375
    9    3   22 0.750
    

    【讨论】:

      【解决方案3】:

      伟大的 Hadley Wickham 本人(以他的名字为圣!)在 mutatr Google 网上论坛上推荐了 this

      d <- expand.grid(1:3, 20:22)
      my.variable <- 'Var1'
      percent_rank <- function(x) rank(x)/max(rank(x))
      call <- substitute(mutate(d, percent_rank(var)), 
                         list(var = as.name(my.variable)))
      eval(call)
      #   Var1 Var2 percent_rank(Var1)
      # 1    1   20              0.250
      # 2    2   20              0.625
      # 3    3   20              1.000
      # 4    1   21              0.250
      # 5    2   21              0.625
      # 6    3   21              1.000
      # 7    1   22              0.250
      # 8    2   22              0.625
      # 9    3   22              1.000
      

      【讨论】:

      • +1,虽然我更喜欢不包括eval 的解决方案。但是,嘿,如果它对哈德利来说足够好的话:)。
      • 为什么你不喜欢eval
      • 主要的一点是eval 通常是不需要的,并且存在不那么晦涩的解决方案。另请参阅stackoverflow.com/questions/13649979/…
      • 我同意 eval(parse(text="&lt;string&gt;")) 这样的事情,但我认为当您以编程方式构造 call 时,避免 eval 是可能的。
      • 只是使用eval构造调用有点hack,原生支持使用字符串会更好。
      猜你喜欢
      • 1970-01-01
      • 2019-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-17
      相关资源
      最近更新 更多