Как использовать оператор%.% В R (EDIT: оператор устарел в 2014 году)

EDIT:%.% теперь устарел. Используйте% > % от magrittr.

ОРИГИНАЛЬНЫЙ ВОПРОС Что делает этот оператор %.%? Я видел, что он много использовал с пакетом dplyr, но не может найти никакой подтверждающей документации о том, что это такое и как он работает.

Кажется, объединяют команды вместе, но это, насколько я могу судить... Пока я нахожусь, может кто-нибудь объяснить, что делает гамбит тех специальных операторов, которые висят с помощью знака %, и когда технически правильное время, чтобы использовать их для лучшего кода?

Ответы

Ответ 1

Я думаю, что Хэдли будет лучшим человеком, который тебе объяснит, но я дам ему шанс.

%.% - это двоичный оператор, называемый цепным оператором. В R вы можете в значительной степени определить любой собственный двоичный оператор со специальным символом %. Из того, что мне кажется, мы в значительной степени используем его, чтобы упростить "цепочечные" синтаксисы (например, x+y, намного лучше, чем sum(x,y)). Вы можете сделать действительно классный материал с ними, см. Этот классный пример здесь.

Какова цель %.% в dplyr? Чтобы облегчить вам выражение себя, уменьшая разрыв между тем, что вы хотите сделать, и тем, как вы его выражаете.

Взяв пример из введение в dplyr, пусть предположим, что вы хотите группировать полеты по годам, месяцам и дням, выберите эти переменные плюс задержки в прибытии и отъезде, суммируйте их по среднему значению и затем фильтруйте только те задержки за 30. Если бы не было %.%, вам нужно было бы написать вот так:

filter(
  summarise(
    select(
      group_by(hflights, Year, Month, DayofMonth),
      Year:DayofMonth, ArrDelay, DepDelay
    ),
    arr = mean(ArrDelay, na.rm = TRUE),
    dep = mean(DepDelay, na.rm = TRUE)
  ),
  arr > 30 | dep > 30
)

Он выполняет эту работу. Но довольно сложно выразить себя и прочитать. Теперь вы можете написать то же самое с более дружественным синтаксисом, используя оператор цепочки %.%:

hflights %.%
  group_by(Year, Month, DayofMonth) %.%
  select(Year:DayofMonth, ArrDelay, DepDelay) %.%
  summarise(
    arr = mean(ArrDelay, na.rm = TRUE),
    dep = mean(DepDelay, na.rm = TRUE)
  ) %.%
  filter(arr > 30 | dep > 30)

Легче писать и читать!

И как это работает?

Посмотрим на определения. Сначала для %.%:

function (x, y) 
{
    chain_q(list(substitute(x), substitute(y)), env = parent.frame())
}

Он использует другую функцию, называемую chain_q. Поэтому давайте посмотрим на это:

function (calls, env = parent.frame()) 
{
    if (length(calls) == 0) 
        return()
    if (length(calls) == 1) 
        return(eval(calls[[1]], env))
    e <- new.env(parent = env)
    e$`__prev` <- eval(calls[[1]], env)
    for (call in calls[-1]) {
        new_call <- as.call(c(call[[1]], quote(`__prev`), as.list(call[-1])))
        e$`__prev` <- eval(new_call, e)
    }
    e$`__prev`
}

Что это делает?

Чтобы упростить ситуацию, позвольте предположить, что вы вызвали: group_by(hflights,Year, Month, DayofMonth) %.% select(Year:DayofMonth, ArrDelay, DepDelay).

Ваши вызовы x и y будут тогда group_by(hflights,Year, Month, DayofMonth) и select(Year:DayofMonth, ArrDelay, DepDelay). Таким образом, функция создает новую среду под названием e (e <- new.env(parent = env)) и сохраняет объект с именем __prev с оценкой первого вызова (e$'__prev' <- eval(calls[[1]], env)). Затем для каждого другого вызова он создает другой вызов, первым аргументом которого является предыдущий вызов - это __prev - в нашем случае это будет select('__prev', Year:DayofMonth, ArrDelay, DepDelay) - поэтому он "цепочки" вызовов внутри цикла.

Поскольку вы можете использовать бинарные операторы друг над другом, вы действительно можете использовать этот синтаксис для очень сложных операций с выражением.

Ответ 2

Быстрый поиск приземлился на меня здесь:

dplyr обеспечивает еще одно новшество над plyr: возможность объединить операции слева направо с помощью оператора %.%. Это заставляет dplyr вести себя как грамматика манипуляции данными.

Пример:

Batting %.%
  group_by(playerID) %.%
  summarise(total = sum(G)) %.%
  arrange(desc(total)) %.%
  head(5)`

Подробнее об этом см. в разделе справки, "%.%".