Нестандартная оценка (NSE) в dplyr filter_ и извлечение данных из MySQL
Я хотел бы извлечь некоторые данные с сервера sql с помощью динамического фильтра. Я использую большой пакет R dplr следующим образом:
#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 ....
('знак в имени столбца)
Есть ли у кого-нибудь предложения о том, как сформулировать условие фильтрации, чтобы заставить его работать?
Ответы
Ответ 1
Это не связано с 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
.
версия dplyr >= 0,6
Чтобы решить проблему, просто используйте оператор цитирования quo
и оператор декатирования !!
library(dplyr)
which_column = quot(v1)
df %>% filter(!!which_column == 1)
версия dplyr < 0.6
Чтобы решить проблему, используйте функцию interp
из пакета lazyeval.
library(lazyeval)
filter_criteria <- interp(~ which_column == 1, which_column = as.name("v1"))
df %>% filter_(filter_criteria)
Ответ 2
Альтернативное решение с dplyr версией 0.5.0 (возможно, реализовано ранее), можно передать сгенерированную строку как аргумент .dots, который я считаю более читаемым, чем lazyeval:: interp solution:
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
UPDATE для 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 - строковая переменная.)
Ответ 3
Здесь несколько менее подробное решение и одно, которое использует типичное поведение функции extract, '['
при выборе столбца по значению символа, а не преобразовании его в элемент языка:
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