Использование 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 правильный, но значения жестко запрограммированы.