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

У меня есть фрейм данных, и я хочу отфильтровать его одним из двух способов: по столбцу "это" или по столбцу "это". Я хотел бы иметь возможность ссылаться на имя столбца как переменную. Как (в 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 держать или "это" или "это", и фильтровать на любое значение 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 в название столбца?

8 ответов

Решение

Я бы держался подальше от использования 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.

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

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

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

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

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

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

Но не:

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

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

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

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

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

Вот еще одно решение для последней версии 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

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

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

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

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

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

Последний способ сделать это - использовать my.data.frame %>% filter(.data[[myName]] == 1), где myName - это переменная среды, содержащая имя столбца.

Или используя filter_at

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

Как и Салим Б, описанный выше, но с небольшим изменением:

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

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

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

Вы можете использовать синтаксис cross(all_of()), он принимает строку в качестве аргумента

      column = "this"
df %>% filter(across(all_of(column)) == 1)
Другие вопросы по тегам