Фильтровать фрейм данных по имени столбца символа (в 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
в имя столбца?
Ответы
Ответ 1
Из текущего файла справки dplyr (выделено мной):
dplyr раньше предлагал двойные версии каждого глагола с суффиксом подчеркивания. Эти версии имели стандартную семантику оценки (SE): вместо того, чтобы принимать аргументы по коду, как глаголы NSE, они брали аргументы по значению. Их цель состояла в том, чтобы сделать возможным программирование с помощью dplyr. Однако dplyr теперь использует семантику аккуратной оценки. Глаголы NSE по-прежнему фиксируют свои аргументы, но теперь вы можете заключать в кавычки части этих аргументов. Это обеспечивает полную программируемость с глаголами NSE. Таким образом, подчеркнутые версии теперь излишни.
Поэтому нам нужно сделать две вещи, чтобы иметь возможность ссылаться на значение "this"
переменной column
внутри dplyr::filter()
:
Нам нужно превратить переменную column
, которая имеет тип символа в тип symbol
.
Используя базу R, этого можно достичь с помощью функции as.symbol()
который является псевдонимом для as.name()
. Первый является предпочтительным
Разработчики Tidyverse
потому что это
следует более современной терминологии (R типов вместо S мод).
В качестве альтернативы то же самое может быть достигнуто с помощью rlang::sym()
из тидиверса.
Нам нужно убрать символ кавычки из 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)