Подтвердить что ты не робот

Фильтровать фрейм данных по имени столбца символа (в dplyr)

У меня есть фрейм данных и вы хотите отфильтровать его одним из двух способов: либо столбцом "this", либо столбцом "that". Я хотел бы иметь возможность ссылаться на имя столбца как на переменную. Как (в dplyr, если это имеет значение) ссылаюсь ли я на имя столбца переменной?

library(dplyr)
df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
df
#   this that
# 1    1    1
# 2    2    1
# 3    2    2
df %>% filter(this == 1)
#   this that
# 1    1    1

Но скажу, что я хочу использовать переменную column для хранения либо "this", либо "this", и фильтровать все, что имеет значение column. Оба as.symbol и get работают в других контекстах, но не в этом:

column <- "this"
df %>% filter(as.symbol(column) == 1)
# [1] this that
# <0 rows> (or 0-length row.names)
df %>% filter(get(column) == 1)
# Error in get("this") : object 'this' not found

Как преобразовать значение column в имя столбца?

4b9b3361

Ответ 1

Из текущего файла справки dplyr (выделено мной):

dplyr раньше предлагал двойные версии каждого глагола с суффиксом подчеркивания. Эти версии имели стандартную семантику оценки (SE): вместо того, чтобы принимать аргументы по коду, как глаголы NSE, они брали аргументы по значению. Их цель состояла в том, чтобы сделать возможным программирование с помощью dplyr. Однако dplyr теперь использует семантику аккуратной оценки. Глаголы NSE по-прежнему фиксируют свои аргументы, но теперь вы можете заключать в кавычки части этих аргументов. Это обеспечивает полную программируемость с глаголами NSE. Таким образом, подчеркнутые версии теперь излишни.

Поэтому нам нужно сделать две вещи, чтобы иметь возможность ссылаться на значение "this" переменной column внутри dplyr::filter():

  1. Нам нужно превратить переменную column, которая имеет тип символа в тип symbol.

    Используя базу R, этого можно достичь с помощью функции as.symbol() который является псевдонимом для as.name(). Первый является предпочтительным Разработчики Tidyverse потому что это

    следует более современной терминологии (R типов вместо S мод).

    В качестве альтернативы то же самое может быть достигнуто с помощью rlang::sym() из тидиверса.

  2. Нам нужно убрать символ кавычки из 1).

    Что именно означает "цитирование", можно узнать из виньетки Программирование с dplyr. Это достигается функцией UQ() или как синтаксической сахар от !!. В настоящее время бывают ситуации - как у тебя - где правильно только первое работает, потому что !! может столкнуться с одним !.

Применимо к вашему примеру:

library(dplyr)
df <- data.frame(this = c(1, 2, 2),
                 that = c(1, 1, 2))
column <- "this"

df %>% filter(UQ(as.symbol(column)) == 1)
#   this that
# 1    1    1

Но не:

df %>% filter(!!as.symbol(column) == 1)
# [1] this that
# <0 Zeilen> (oder row.names mit Länge 0)

Синтаксический сахар !! снова работает как положено, если вы добавите несколько дополнительных круглых скобок (спасибо Martijn vd Voort за предложение):

df %>% filter((!!as.symbol(column)) == 1)
#   this that
# 1    1    1

Или, если вы просто поменяете местами два операнда сравнения (спасибо carand за подсказку):

df %>% filter(1 == !!as.symbol(column))
#   this that
# 1    1    1

Ответ 2

Я бы избегал использования get() вместе. Похоже, в этой ситуации это было бы очень опасно, особенно если вы программируете. Вы можете использовать либо неоплаченный вызов, либо вставную строку символов, но вам нужно использовать filter_() вместо filter().

df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
column <- "this"

Вариант 1 - использование необоснованного вызова:

Вы можете жестко закодировать y как 1, но здесь я показываю его как y, чтобы проиллюстрировать, как легко изменить значения выражения.

expr <- lazyeval::interp(quote(x == y), x = as.name(column), y = 1)
## or 
## expr <- substitute(x == y, list(x = as.name(column), y = 1))
df %>% filter_(expr)
#   this that
# 1    1    1

Вариант 2 - используя paste() (и, очевидно, проще):

df %>% filter_(paste(column, "==", 1))
#   this that
# 1    1    1

Главное в этих двух вариантах состоит в том, что нам нужно использовать filter_() вместо filter(). Фактически, из того, что я прочитал, если вы программируете с помощью dplyr, вы всегда должны использовать функции *_().

Я использовал этот пост в качестве полезной ссылки: символьная строка как аргумент функции r, и я использую dplyr версию 0.3.0.2.

Ответ 3

Что касается решения Ричарда, просто хочу добавить, что если столбец является символом. Вы можете добавить shQuote для фильтрации по символьным значениям.

Например, вы можете использовать

df %>% filter_(paste(column, "==", shQuote("a")))

Если у вас есть несколько фильтров, вы можете указать collapse = "&" в paste.

df %>$ filter_(paste(c("column1","column2"), "==", shQuote(c("a","b")), collapse = "&"))

Ответ 4

Вот еще одно решение для последней версии dplyr:

df <- data.frame(this = c(1, 2, 2),
                 that = c(1, 1, 2))
column <- "this"

df %>% filter(.[[column]] == 1)

#  this that
#1    1    1

Ответ 5

Или с помощью filter_at

library(dplyr)
df %>% 
   filter_at(vars(column), any_vars(. == 1))

Ответ 6

Подобно Салиму Б, описанному выше, но с незначительными изменениями:

df %>% filter(1 == !!as.name(column))

то есть. просто измените условие, потому что !! в противном случае ведет себя как

!!(as.name(column)==1)