【问题标题】:Pass a function to be applied as function argument in dbplyr在 dbplyr 中传递要作为函数参数应用的函数
【发布时间】:2021-02-18 11:05:33
【问题描述】:

假设我想创建一个函数,该函数可以使用用户传递的任何函数来改变列。我需要知道如何在该函数到达 dbplyr 解析器之前 引用和取消引用它。我们来看一个例子,假设我有一个这样的函数:

testFun <- function(data, fun, colName, colOut = "myAwesomeColumn") {
  dplyr::mutate(.data = data, !!colOut := fun(.data[[colName]]))
}

sc <- sparklyr::spark_connection(master = "local")
mtcars_spark <- dplyr::copy_to(sc, mtcars, "mtcars")
testFun(mtcars_spark, mean, "mpg")

所以在上面的示例中,我想将mean() 函数应用于"mpg" 列并将其存储在一个名为"myAwesomeColumn" 的新列中。

在使用 Spark,特别是 sparklyr 时,dbplyr 会尝试将此代码转换为 SQL 并将其发送到 Spark。我的理解是 dbplyr 应用以下规则:

  1. 如果它可以找到 Spark SQL 等效项,它将使用它(例如 mean() -> AVG
  2. 否则它将按原样传递函数以查找 Scala 扩展或 UDF

第二个选项是这里发生的情况,因为它找不到函数fun,因此它返回一个 Spark 错误

Error: org.apache.spark.sql.AnalysisException: Undefined function: 'fun'.
This function is neither a registered temporary function nor a permanent function
registered in the database 'default'.; line 1 pos 85
...

所以我们需要另一种方法。问题是让 rlang 在 dbplyr 解释之前将fun 转换为mean。我知道如果我将函数名作为字符串传递并使用rlang::parse_expr(),我可以做到这一点,例如:

testFun <- function(data, fun, colName, colOut = "myAwesomeColumn") {
  dplyr::mutate(data, !!colOut := rlang::parse_expr(paste0(fun, "(", colName, ")"))
}
testFun(mtcars_spark, "mean", "mpg")
# # Source: spark<?> [?? x 12]
#      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb myAwesomeColumn
#    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>           <dbl>
#  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4            20.1
#  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4            20.1
# # ... with more rows

【问题讨论】:

  • 解决方案应该解决什么问题?您想避免使用rlang::parse_expr 解析字符串吗?输入应该是一个裸名(例如mean)而不是字符串(“mean”)吗?
  • 是的,就是这样。我想通过mean 而不是"mean"
  • 我没有 Spark 和 sparklyr,所以无法测试。我会尝试rlang::call2 并使用!! 取消引用,因为bang bang 运算符会更早地评估其对象。但我不知道这是否可行。 testFun &lt;- function(data, fun, colName, colOut = "myAwesomeColumn") { .fun &lt;- enquo(fun) dplyr::mutate(.data = data, !!colOut := eval(call2(!! .fun, .data[[colName]]))) }
  • 这几乎奏效了,我做了些微调整,现在它确实奏效了。我会发布我的答案,谢谢。
  • 很高兴看到我正在使用 {rlang} 的 call2fun 的双引号和取消引号在正确的树上吠叫。我有点惊讶我们不需要eval call2 的输出。仍然想知道这是在哪里发生的。我也很惊讶这适用于tbl_spark,但不适用于data.frame(这里出现错误)。

标签: r dplyr dbplyr


【解决方案1】:

为了让它工作,我们必须引用和取消引用 fun 参数。我们还构建了我们真正想要传递给我们对mutate() 的调用的表达式。解决方法见下文。

testFun <- function(data, fun, colAmount, colOut = "output") { 
  fun <- rlang::enquo(fun) 
  dplyr::mutate(.data = data, !!colOut := rlang::call2(.fn = !!fun, rlang::sym(colAmount))) 
} 
     
testFun(mtcars_spark, mean, "mpg")                                                                                                                                 
# # Source: spark<?> [?? x 12]
#      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb output
#    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>
#  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4   20.1
#  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4   20.1
# # ... with more rows

请注意,如果您使用的是data.frames 而不是tbl_sparks,这会简单得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 2020-03-22
    • 2012-01-17
    • 2019-11-21
    相关资源
    最近更新 更多