Использование filter_ в dplyr, где оба поля и значения находятся в переменных

Я хочу отфильтровать dataframe, используя поле, которое определено в переменной, чтобы выбрать значение, которое также находится в переменной. Скажем, у меня

df <- data.frame(V=c(6, 1, 5, 3, 2), Unhappy=c("N", "Y", "Y", "Y", "N"))
fld <- "Unhappy"
sval <- "Y"

Значение, которое я хочу, будет df[df$Unhappy == "Y", ].

Я прочитал виньетку nse, чтобы попробовать использовать filter_, но не могу ее полностью понять. Я попробовал

df %>% filter_(.dots = ~ fld == sval)

который ничего не возвращал. Я получил то, что хотел с помощью

df %>% filter_(.dots = ~ Unhappy == sval)

но очевидно, что он побеждает цель иметь переменную для хранения имени поля. Любые подсказки, пожалуйста? В конце концов я хочу использовать это, где fld - вектор имен полей, а sval - вектор значений фильтра для каждого поля в fld.

Ответ 1

Вы можете попробовать с interp от lazyeval

 library(lazyeval)
 library(dplyr)
 df %>%
     filter_(interp(~v==sval, v=as.name(fld)))
 #   V Unhappy
 #1 1       Y
 #2 5       Y
 #3 3       Y

Для нескольких пар ключ/значение, я нашел, что это работает, но я думаю, что лучший способ должен быть там.

  df1 %>% 
    filter_(interp(~v==sval1[1] & y ==sval1[2], 
           .values=list(v=as.name(fld1[1]), y= as.name(fld1[2]))))
 #  V Unhappy Col2
 #1 1       Y    B
 #2 5       Y    B

В этих случаях я считаю, что параметр base R будет проще. Например, если мы пытаемся выполнить filter строки на основе "ключевых" переменных в "fld1" с соответствующими значениями в "sval1", один параметр использует Map. Мы подмножаем набор данных (df1[fld1]) и применяем FUN (==) к каждому столбцу df1[f1d1] с соответствующим значением в 'sval1' и используем & с Reduce, чтобы получить логический вектор, который может быть используется для filter строк 'df1'.

 df1[Reduce(`&`, Map(`==`, df1[fld1],sval1)),]
 #   V Unhappy Col2
 # 2 1       Y    B
  #3 5       Y    B

данные

df1 <- cbind(df, Col2= c("A", "B", "B", "C", "A"))
fld1 <- c(fld, 'Col2')
sval1 <- c(sval, 'B')    

Ответ 2

Теперь, с rlang 0.4.0, он представляет новый более интуитивный способ для этого типа случая использования:

packageVersion("rlang")
# [1] ‘0.4.0

df <- data.frame(V=c(6, 1, 5, 3, 2), Unhappy=c("N", "Y", "Y", "Y", "N"))
fld <- "Unhappy"
sval <- "Y"

df %>% filter(.data[[fld]]==sval)

#OR
filter_col_val <- function(df, fld, sval) {
  df %>% filter({{fld}}==sval)
}

filter_col_val(df, Unhappy, "Y")

Дополнительную информацию можно найти по адресу https://www.tidyverse.org/articles/2019/06/rlang-0-4-0/.

Предыдущий ответ

С dplyr 0.6.0 и выше, этот код работает:

packageVersion("dplyr")
# [1] ‘0.7.1

df <- data.frame(V=c(6, 1, 5, 3, 2), Unhappy=c("N", "Y", "Y", "Y", "N"))
fld <- "Unhappy"
sval <- "Y"

df %>% filter(UQ(rlang::sym(fld))==sval)

#OR
df %>% filter((!!rlang::sym(fld))==sval)

#OR
fld <- quo(Unhappy)
sval <- "Y"
df %>% filter(UQ(fld)==sval)

Подробнее о синтаксисе dplyr доступном по адресу http://dplyr.tidyverse.org/articles/programming.html, и об использовании rlang пакете rlang https://cran.r-project.org/web/packages/rlang/index. HTML.

Если вам сложно освоить нестандартную оценку в dplyr 0. 6+, у Алекса Хейса есть отличная статья по этой теме: https://www.alexpghayes.com/blog/gentle-tidy-eval-with- Примеры /

Оригинальный ответ

С версией dplyr 0.5.0 и выше, можно использовать более простой синтаксис и приблизиться к синтаксису, который изначально хотел @Ricky, который я также нахожу более читабельным, чем использование lazyeval::interp

df %>% filter_(.dots = paste0(fld, "=='", sval, "'"))

#  V Unhappy
#1 1       Y
#2 5       Y
#3 3       Y

#OR
df %>% filter_(.dots = glue::glue("{fld}=='{sval}'"))

Ответ 3

Здесь альтернатива с базой R, которая, возможно, не очень элегантная, но она может быть полезной для того, чтобы быть довольно понятной:

df[df[colnames(df)==fld]==sval,]
#  V Unhappy
#2 1       Y
#3 5       Y
#4 3       Y

Ответ 4

Далее из LmW; лично я предпочитаю использовать dplyr-конвейер, где точки указываются перед конвейером, так что его проще использовать программно, скажем, в петле фильтров.

dots <-  paste0(fld," == '",sval,"'")
df   %>% filter_(.dots = dots)

Пример LmW правильный, но значения жестко запрограммированы.