动态地为 mutate 中的函数提供参数
Dynamically provide argument to function inside mutate
首先 - 如果之前有人问过这个问题,我深表歉意,我已经看过但没能找到任何与我正在尝试做的相匹配的东西。
我正在尝试创建一个函数,根据数据框中用户生成的列对数据进行分类。为此,我使用了来自 dplyr 的 mutate() 函数和来自 base R 的 cut() 。但是,我无法弄清楚如何使用通过 cut() 函数内部的函数传递的列名(出现在 mutate 中)。
我花了几个小时浏览 this and this 但仍然没有弄明白。我的理解是 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))
正如@aosmith 在他们的评论中提到的,解决方案是使用 mutate_(bins = interp(~cut(x, bins, dig.lab = 5, include.lowest = TRUE), x = as.name(col)))
。使用 mutate_
而不是 mutate
允许我们使用标准评估。
如果我们在 mutate_
之外调用 interp
,最容易看出 interp
和 cut
发生了什么。 (无论哪种方式执行方式都相同。)假设 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)))
首先 - 如果之前有人问过这个问题,我深表歉意,我已经看过但没能找到任何与我正在尝试做的相匹配的东西。
我正在尝试创建一个函数,根据数据框中用户生成的列对数据进行分类。为此,我使用了来自 dplyr 的 mutate() 函数和来自 base R 的 cut() 。但是,我无法弄清楚如何使用通过 cut() 函数内部的函数传递的列名(出现在 mutate 中)。
我花了几个小时浏览 this and this 但仍然没有弄明白。我的理解是 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))
正如@aosmith 在他们的评论中提到的,解决方案是使用 mutate_(bins = interp(~cut(x, bins, dig.lab = 5, include.lowest = TRUE), x = as.name(col)))
。使用 mutate_
而不是 mutate
允许我们使用标准评估。
如果我们在 mutate_
之外调用 interp
,最容易看出 interp
和 cut
发生了什么。 (无论哪种方式执行方式都相同。)假设 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)))