从函数内将列名传递给 dplyr 过滤器函数

pass column name to dplyr filter function from within a function

我正在编写一个函数来向我正在构建的数据框添加一些额外的行。

我已经阅读了以前 OP 中的许多问题和答案。但是我在那里找到的所有答案、提示和技巧都不适合我。

对于这个问题,我有以下测试数据框:

tst <- data.frame("col 1" = c("a","a", "c"), "keyword test" = c("What", "Why", "how"), check.names = F)
> tst
  col 1 keyword test
1     a         What
2     a          Why
3     c          how

如您所见,我无法删除数据框中的空格,因为下一个工具需要列名中有空格(不要问我为什么!)。

例如,现在我想过滤所有以 "how" 开头的行,并将 "how" 替换为 "no_idea"。这发生在临时 DF 内部。以便稍后我可以将行 "c" "no_idea" 添加到原始数据框。

我为此编写的函数如下所示:

add_keyword <- function(df, filterColumn, filterValue,replacement){
  library(plyr)
  library(dplyr)
  temp_df <- dplyr::filter_(df, filterColumn == filterValue)
  temp_df$`Target keyword` <- gsub(as.character(filterValue), as.character(replacement), temp_df$`Target keyword`)
  df_out <- rbind(df, temp_df)
  return(df_out)
}

tst2 <- add_keyword(tst, "keyword test", "how", "no_idea")

当然,如果我 运行 函数内的行在函数外,它就完美无缺。

我希望在里面得到的结果 tst2

> tst2
  col 1 keyword test
1     a         What
2     a         Why
3     c         how
4     c         no_idea 

我们可以用 lazyeval 中的 interp 来做到这一点:

library(dplyr)
library(lazyeval)

add_keyword <- function(df, filterColumn, filterValue,replacement){
    temp_df <- df %>%
        filter_(interp(~ var == fval, var = as.name(filterColumn), fval = filterValue))

    temp_df[[filterColumn]] <- gsub(filterValue, replacement = replacement, temp_df[[filterColumn]])
    rbind(df, temp_df)
}
add_keyword(tst, "keyword test", "how", "no_idea")
#   col 1 keyword test
# 1     a         What
# 2     a          Why
# 3     c          how
# 4     c      no_idea

如果我们不想创建额外的行,我们也可以尝试:

add_keyword <- function(df, filterColumn, filterValue, replacement){
    df <- df %>%
        mutate_(
            .dots = setNames(
                list(
                    interp(~ ifelse(startsWith(as.character(var), fval), rval, as.character(var)),
                           var = as.name(filterColumn), fval = filterValue, rval = replacement)),
                filterColumn
            )
        )
    df
}
add_keyword(tst, "keyword test", "how", "no_idea")


#   col 1 keyword test
# 1     a         What
# 2     a          Why
# 3     c      no_idea

我们可以使用dplyr的devel版本(即将发布0.6.0)来利用quosures

add_keyword <- function(df, filterColumn, filterValue, replacement){
   filterColumn <- enquo(filterColumn)

   filtColumn <- quo_name(filterColumn)
   filterValue <- quo_name(enquo(filterValue))
   replacement <- quo_name(enquo(replacement))
   
   df %>%
       filter(UQ(filterColumn) ==  filterValue) %>%
       mutate(!!filtColumn  := gsub(filterValue, replacement, !!filterColumn)) %>%
       bind_rows(df, .)
       

 }

除了标准名称,不自然的列名可以用反引号传递。

add_keyword(tst, `keyword test`, how, no_idea)
#   col 1 keyword test
#1     a         What
#2     a          Why
#3     c          how
#4     c      no_idea

在这里,enquo 的行为类似于 base R 中的 substitute,通过获取输入参数并将其转换为 quosure。我们使用 quo_name 将 quosure 转换为字符串,并通过取消引号(UQ!!)对值进行评估。

检查其他值

add_keyword(tst, `keyword test`, how, new_idea)
#  col 1 keyword test
#1     a         What
#2     a          Why
#3     c          how
#4     c     new_idea

add_keyword(tst, `col 1`, a, z)
#  col 1 keyword test
#1     a         What
#2     a          Why
#3     c          how
#4     z         What
#5     z          Why

数据

tst <- data.frame("col 1" = c("a","a", "c"), "keyword test" = c("What", "Why", "how"),
             check.names = FALSE, stringsAsFactors=FALSE)

一个简单的技巧,无法解释但写下下面的代码片段:

filter(df[, paste(filterColumn)] == filterValue)

这对我有用,希望对正在寻找解决方案的您有用:)