【问题标题】:Dynamically provide argument to function inside mutate在 mutate 中动态地为函数提供参数
【发布时间】:2016-06-27 21:00:25
【问题描述】:

首先 - 如果之前有人问过这个问题,我深表歉意,我已经看过并且无法找到与我正在尝试做的事情相匹配的任何内容。

我正在尝试创建一个函数,该函数根据数据框中的用户生成列对数据进行分箱。为此,我使用了 dplyr 中的 mutate() 函数和 base R 中的 cut() 函数。但是,我不知道如何使用通过 cut() 函数内部的函数传递的列名(其中出现在 mutate 中)。

我花了几个小时浏览thisthis,但仍然没有弄明白。我的理解是下面代码中的 foo()、bar() 和最后一行都应该产生相同的输出。但是,我得到了两个函数错误,一个没有包含在函数中并且只使用硬编码列名的错误可以正常工作。

这里发生了什么?为什么 foo() 产生的输出与 bar() 不同?以及如何正确使用lazyeval 来允许函数中的正确行为?

library(dplyr)
library(lazyeval)

foo <- function(data, col, bins){
    by = lazyeval::interp(quote(x), x = as.name(col))
    print(paste0("typeof(by): ", typeof(by)))
    print(paste0(" by: ", by))

    df <- data %>%
      dplyr::mutate(bins = cut(by,
        breaks = bins,
        dig.lab = 5,
        include.lowest = T))
    df
}

bar <- function(data, col, bins){
  df <- data %>%
    dplyr::mutate(bins = cut(lazyeval::interp(quote(x), x = as.name(col)),
      breaks = bins,
      dig.lab = 5,
      include.lowest = T))
  df
}

#produce sample data and bins list
df <- expand.grid(temp=0:8,precip=seq(0.7,1.3,by=0.1))
df$rel <- seq(40,100,length=63)
bins <- seq(40,100,by=10)

foo(df,"rel",bins) # produces "Error: 'rel' not found"
bar(df,"rel",bins) # produces "Error: 'x' must be numeric"

# but this works
dplyr::mutate(df, bins = cut(rel, breaks = bins, dig.lab = 5, include.lowest = T))

【问题讨论】:

  • 我可能会在函数中有类似mutate_(bins = interp(~cut(x, bins, dig.lab = 5, include.lower = TRUE), x = as.name(col))) 的东西。
  • @aosmith 想了很多,我很难理解它为什么起作用。看起来 interp() 是在 cut 之前执行的,但我之前的所有编码经验都告诉我,函数是从内到外执行的。
  • 要了解更多细节,阅读 Advanced R 的non-standard evaluation section 可能会有所帮助。您的第一个链接涵盖了一些相同的信息。我认为捕获表达式以稍后评估的部分可能是相关的。

标签: r dplyr lazyeval


【解决方案1】:

正如@aosmith 在他们的评论中提到的,解决方案是使用mutate_(bins = interp(~cut(x, bins, dig.lab = 5, include.lowest = TRUE), x = as.name(col)))。使用mutate_ 代替mutate 允许我们使用标准评估。

如果我们在mutate_ 之外调用interp,最容易看到interpcut 发生了什么。 (它的执行方式相同。)假设col == "rel"

call = interp(~cut(x, bins, dig.lab = 5, include.lowest = TRUE), x = as.name(col))) 

会给

~cut(rel, bins, dig.lab = 5, include.lowest = TRUE)

将这个表达式插入 mutate 可以让我们完全遵循here 提供的示例。

muatate_(bins = call)

给出正确的结果。

您还可以允许用户提供替换“bins”的列名:

dplyr::mutate_(.dots = setNames(call, c(binName)))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-22
    • 2018-05-15
    • 1970-01-01
    • 2018-11-25
    • 2012-02-03
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    相关资源
    最近更新 更多