【问题标题】:R dplyr operate on a column known only by its string nameR dplyr 对仅通过其字符串名称知道的列进行操作
【发布时间】:2018-02-28 19:27:15
【问题描述】:

我正在努力在 R 中使用 dplyr 进行编程,以对数据框的列进行操作,这些列只能通过字符串名称知道。我知道最近对dplyr 进行了更新以支持quosures 等,我在这里查看了我认为是新的“使用dplyr 编程”文章的相关组件:http://dplyr.tidyverse.org/articles/programming.html。但是,我仍然无法做我想做的事。

我的情况是我只通过字符串名称知道数据框的列名。因此,我不能在函数内调用 dplyr 时使用非标准评估,甚至不能在列名可能在运行之间更改的脚本中使用非标准评估,因为我无法对未引用的(即“裸”)进行硬编码列名一般。我想知道如何解决这个问题,我猜我用新的引用/取消引用语法忽略了一些东西。

例如,假设我的用户输入定义了数据分布的截止百分位数。用户可以使用他/她想要的任何百分位数运行代码,他/她选择的百分位数将改变输出。在分析中,中间数据框中的一列是用所用百分位数的名称创建的;因此,此列的名称会根据用户输入的截止百分位数而变化。

下面是一个最小的例子来说明。我想用截止百分位数的各种值调用该函数。我希望名为 MPGCutoffs 的数据框有一个根据所选截止分位数命名的列(这目前在下面的代码中有效),我想稍后对该列名进行操作。由于这个列名的通用性,我在写函数的时候只能根据输入pctCutoff知道,所以需要在只知道probColName定义的字符串的情况下对其进行操作,它遵循基于pctCutoff 值的预定义模式。

userInput_prob1 <- 0.95
userInput_prob2 <- 0.9

# Function to get cars that have the "best" MPG
# fuel economy, where "best" is defined by the
# percentile cutoff passed to the function.
getBestMPG <- function( pctCutoff ){

  # Define new column name to hold the MPG percentile cutoff.
  probColName <- paste0('P', pctCutoff*100)

  # Compute the MPG percentile cutoff by number of gears.
  MPGCutoffs <- mtcars %>%
    dplyr::group_by( gear ) %>%
    dplyr::summarize( !!probColName := quantile(mpg, pctCutoff) )

  # Filter mtcars with only MPG values above cutoffs.
  output <- mtcars %>%
    dplyr::left_join( MPGCutoffs, by='gear' ) %>%
    dplyr::filter( mpg > !!probColName ) #****This doesn't run; this is where I'm stuck

  # Return filtered data.
  return(output)
}

best_1 <- getBestMPG( userInput_prob1 )
best_2 <- getBestMPG( userInput_prob2 )

dplyr::filter() 语句是我无法正常运行的。我试过了:

dplyr::filter( mpg &gt; probColName ) - 没有错误,但没有返回行。

dplyr::filter( mpg &gt; !!probColName ) - 没有错误,但没有返回行。

我还看到了一些示例,其中我可以将 quo(P95) 之类的内容传递给函数,然后在对 dplyr::filter() 的调用中取消引用它;我已经让它工作了,但它并没有解决我的问题,因为它需要在函数之外对变量名进行硬编码。例如,如果我这样做并且用户传递的百分位数是 0.90,那么对 dplyr::filter() 的调用将失败,因为创建的列名为 P90 而不是 P95

任何帮助将不胜感激。我希望有一个简单的解决方案,我只是忽略了。

【问题讨论】:

    标签: r dynamic dplyr quoting rlang


    【解决方案1】:

    如果您在字符串(也称为字符向量)中有列名,并且您想将其与 tidyeval 一起使用,那么您可以使用 rlang::sym() 将其隐藏。换个方式

    dplyr::filter( mpg > !!rlang::sym(probColName) )
    

    它应该可以工作。这取自此 github 问题的建议:https://github.com/tidyverse/rlang/issues/116

    用起来还是不错的

    dplyr::summarize( !!probColName := quantile(mpg, pctCutoff) )
    

    因为在动态设置参数名称时,您只需要字符串而不是不带引号的符号。

    【讨论】:

    • 这正是我想要做的,谢谢@MrFlick。问题:当我评估str(rlang::sym(probColName)) 时,它返回symbol 95(或根据pctCutoff 的值的其他数字。!! 运算符在符号的情况下做了什么?它是否将其转换为未加引号的裸变量姓名?
    • 一个符号就像一个没有环境的quosure。这 !!基本上,符号和 quosures 都相同。符号是一个未加引号的裸变量名。这就是在 tidyeval 改变范式之前使用的。
    • 有趣的是,如果符号只是一个未加引号的裸变量名,那么符号上仍然需要取消引用运算符 !!,这正是 dplyr 动词使用 NSE 所期望的。无论如何,这解决了我一段时间以来遇到的问题,并且通过最新版本的dplyr 更新的quosure 处理,将使使用dplyr 动词的函数的泛型参数处理更加简化。感谢您的洞察力!
    • 它确实与您希望何时评估符号以及何时不评估它有关。这 !!让你有更多的控制权。
    • 如果你查看?sym帮助页面,你会发现它们处理编码的方式有所不同,但其他方面基本相同。
    【解决方案2】:

    这是 Hadley 在 MrFlick 的回答 (https://github.com/tidyverse/rlang/issues/116) 中提到的帖子中的评论的替代解决方案。使用 base R 中的 as.name() 代替 rlang::sym(),您仍然需要取消引用它。也就是说,以下也有效:

    dplyr::filter( mpg &gt; !!as.name(probColName) )

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-27
      • 1970-01-01
      • 2023-01-14
      • 2021-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多