Фильтровать фрейм данных по имени символьного столбца (в 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)