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


person William Denton    schedule 29.11.2014    source источник
comment
почему ты хочешь сделать это? Пытаетесь параметризовать выбор переменной?   -  person smci    schedule 30.11.2014
comment
У меня есть набор данных с двумя типами вещей и разными столбцами для каждого. Я хочу подходить к этому так, потому что я использую Shiny для создания интерактивной визуализации, и я хочу, чтобы люди выбирали, какой тип объекта смотреть, но затем использовать тот же код для извлечения данных, передавая имя столбца на основе на их выбор.   -  person William Denton    schedule 30.11.2014
comment
Почти опубликовал свой вопрос и нашел это :)   -  person biocyberman    schedule 17.02.2016


Ответы (7)


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

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

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

  1. # P4 # # P5 #
    # P6 #
    # P7 #
  2. Нам нужно убрать кавычки с символа 1).

    Что именно означает удаление кавычек, можно узнать из виньетки Программирование с dplyr. Это достигается с помощью синтаксического сахара !!.

    (В более ранних версиях dplyr (или базового rlang соответственно) были ситуации (включая вашу), когда !! сталкивался с одним !, но это больше не проблема , поскольку !! получил правильный приоритет оператора.)

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

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

df %>% filter(!!as.symbol(column) == 1)
#   this that
# 1    1    1
person Salim B    schedule 08.11.2017
comment
В последнем случае вы можете использовать дополнительные круглые скобки df %>% filter((!!as.name(column)) == 1) - person Martijn vd Voort; 04.01.2018
comment
Отлично, но «UQ ()» и «UQS ()» были мягко устаревшими в rlang 0.2.0, чтобы сделать синтаксис квазиквотирования более согласованным. - person ggll; 20.02.2020
comment
Спасибо @ggll, я обновил ответ, чтобы учесть недавние улучшения в dplyr / rlang. - person Salim B; 20.02.2020

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

person Rich Scriven    schedule 29.11.2014
comment
Вариант 1 - надежное решение, рекомендованное @hadley в нескольких сообщениях на SO и в виньетке (nse). Однако похоже, что это идет вразрез с элегантным стилем вызова функций Хэдли. Разве его нельзя упаковать и представить проще? - person biocyberman; 17.02.2016
comment
почему get () считается опасным? - person pluke; 18.05.2016
comment
Это хороший ответ для более старых версий dpylr, но функции подчеркивания устарели, поэтому ответ @Salim-B ниже лучше подходит для дальнейшего развития. - person Elin; 26.11.2017

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

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

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

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

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

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

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

person Phoenix Mu    schedule 21.09.2020

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

library(dplyr)
df %>% 
   filter_at(vars(column), any_vars(. == 1))
person akrun    schedule 25.08.2019

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

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

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

!!(as.name(column)==1)
person carand    schedule 15.01.2018