r语言 - dplyr filter_中的非标准评估 (NSE) 和从 MySQL 中提取数据



我想从带有动态过滤器的sql服务器中提取一些数据。我以以下方式使用伟大的R包dplyr:

#Create the filter
filter_criteria = ~ column1 %in% some_vector
#Connect to the database
connection <- src_mysql(dbname <- "mydbname", 
             user <- "myusername", 
             password <- "mypwd", 
             host <- "myhost") 
#Get data
data <- connection %>%
 tbl("mytable") %>% #Specify which table
 filter_(.dots = filter_criteria) %>% #non standard evaluation filter
 collect() #Pull data

这段代码运行良好,但现在我想以某种方式在表的所有列上循环它,因此我想将过滤器写为:

#Dynamic filter
i <- 2 #With a loop on this i for instance
which_column <- paste0("column",i)
filter_criteria <- ~ which_column %in% some_vector

然后使用更新后的筛选器重新应用第一个代码。

不幸的是,这种方法没有给出预期的结果。事实上,它没有给出任何错误,甚至没有将任何结果拉入R。特别是,我稍微研究了一下这两段代码生成的SQL查询,发现有一个重要的区别。

当第一个正在工作时,代码生成以下形式的查询:

SELECT ... FROM ... WHERE 
`column1` IN ....

(`在列名中签名),第二个生成以下形式的查询:

SELECT ... FROM ... WHERE 
'column1' IN ....

('在列名中签名)

有人对如何制定过滤条件以使其发挥作用有什么建议吗?

它实际上与SQL无关。R中的这个例子也不起作用:

df <- data.frame(
     v1 = sample(5, 10, replace = TRUE),
     v2 = sample(5,10, replace = TRUE)
)
df %>% filter_(~ "v1" == 1)

它不起作用,因为您需要向filter_传递表达式~ v1 == 1,而不是表达式~ "v1" == 1

要解决此问题,只需使用引用运算符quo和取消引用运算符!!

library(dplyr)
which_column = quot(v1)
df %>% filter(!!which_column == 1)

另一种解决方案,dplyr版本为0.5.0(可能更早实现),可以传递一个组合字符串作为.dots参数,我发现它比lazyval::interp解决方案更可读

df <- data.frame(
     v1 = sample(5, 10, replace = TRUE),
     v2 = sample(5,10, replace = TRUE)
)
which_col <- "v1"
which_val <- 1
df %>% filter_(.dots= paste0(which_col, "== ", which_val))
  v1 v2
1  1  1
2  1  2
3  1  4

更新dplyr 0.6及更高版本:

packageVersion("dplyr")
# [1] ‘0.5.0.9004’
df %>% filter(UQ(rlang::sym(which_col))==which_val)
#OR
df %>% filter((!!rlang::sym(which_col))==which_val)

(类似于@Matthew对dplyr 0.6的响应,但我假设which_col是一个字符串变量。)

第二次更新:Edwin Thoen为整洁的评估创建了一个不错的备忘单:https://edwinth.github.io/blog/dplyr-recipes/

这里有一个稍微不那么详细的解决方案,它使用提取函数'['的典型行为来按字符值选择列,而不是将其转换为语言元素:

df %>% filter(., '['(., which_column)==1 )
set.seed(123)
df <- data.frame(
      v1 = sample(5, 10, replace = TRUE),
      v2 = sample(5,10, replace = TRUE)
 )
which_column <- "v1"
df %>% filter(., '['(., which_column)==1)
#  v1 v2
#1  1  5

最新更新