data.table vs dplyr: может ли что-то сделать хорошо, а другое плохо или плохо?

Обзор

Я относительно знаком с data.table, а не с dplyr. Я прочитал некоторые dplyr виньетки и примеры, появившиеся на SO, и до сих пор мои выводы заключаются в следующем:

  • data.table и dplyr сравнимы по скорости, за исключением случаев, когда имеется много групп (т.е. > 10-100K) и в некоторых других обстоятельствах (см. ниже контрольные показатели).
  • dplyr имеет более доступный синтаксис
  • dplyr тезисы (или будут) потенциальные взаимодействия с БД
  • Есть некоторые незначительные различия в функциональности (см. ниже "Примеры/использование" )

В моем сознании 2. не имеет большого веса, потому что я довольно хорошо знаком с ним data.table, хотя я понимаю, что для пользователей, новых для обоих, это будет большой фактор. Я хотел бы избежать аргумента, который более интуитивно понятен, поскольку это не имеет значения для моего конкретного вопроса, заданного с точки зрения кого-то, уже знакомого с data.table. Я также хотел бы избежать дискуссии о том, как "более интуитивно понятный" ведет к более быстрому анализу (конечно, правда, но опять же, не то, что меня больше всего интересует).

Вопрос

Что я хочу знать:

  • Существуют ли аналитические задачи, которые намного проще кодировать с одним или другим пакетом для людей, знакомых с пакетами (т.е. требуется некоторая комбинация нажатий клавиш по сравнению с требуемым уровнем эзотеризма, где меньше всего — хорошая вещь).
  • Существуют ли аналитические задачи, которые выполняются по существу (то есть более двух раз) более эффективно в одном пакете по сравнению с другим.

Один недавний вопрос SO заставлял меня думать об этом немного больше, потому что до этого момента я не думал, что dplyr будет предлагать намного больше, чем я могу уже в data.table. Вот решение dplyr (данные в конце Q):

dat %.%
  group_by(name, job) %.%
  filter(job != "Boss" | year == min(year)) %.%
  mutate(cumu_job2 = cumsum(job2))

Это было намного лучше, чем моя попытка взлома в решении data.table. Тем не менее, хорошие решения data.table также очень хороши (спасибо Жан-Роберту, Арун, и обратите внимание, что я предпочитал одно утверждение строго строго оптимального решения):

setDT(dat)[,
  .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], 
  by=list(id, job)
]

Синтаксис последнего может показаться очень эзотерическим, но на самом деле это довольно просто, если вы привыкли к data.table (т.е. не используете некоторые из более эзотерических трюков).

В идеале, что бы я хотел увидеть, это несколько хороших примеров: путь dplyr или data.table значительно более краток или значительно лучше.

Примеры

Применение

  • dplyr не позволяет сгруппированным операциям возвращать произвольное количество строк (из eddi question, обратите внимание: похоже, что это будет быть реализовано в dplyr 0.5, также, @beginneR показывает потенциальный обход, используя do в ответе на @eddi).
  • data.table поддерживает скользящие соединения (спасибо @dholstius), а также перекрытие соединений
  • data.table внутренне оптимизирует выражения формы DT[col == value] или DT[col %in% values] для скорости с помощью автоматической индексации, которая использует бинарный поиск при использовании того же базового синтаксиса R. Смотрите здесь для получения более подробной информации и крошечного теста.
  • dplyr предлагает стандартные оценочные версии функций (например, regroup, summarize_each_), которые могут упростить программное использование dplyr (обратите внимание, что программное использование data.table, безусловно, возможно, просто требует некоторой осторожности, замена/цитирование и т.д., по крайней мере, насколько мне известно).

Ориентиры

Данные

Это для первого примера, который я показал в разделе вопросов.

dat <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L), name = c("Jane", "Jane", "Jane", "Jane", 
"Jane", "Jane", "Jane", "Jane", "Bob", "Bob", "Bob", "Bob", "Bob", 
"Bob", "Bob", "Bob"), year = c(1980L, 1981L, 1982L, 1983L, 1984L, 
1985L, 1986L, 1987L, 1985L, 1986L, 1987L, 1988L, 1989L, 1990L, 
1991L, 1992L), job = c("Manager", "Manager", "Manager", "Manager", 
"Manager", "Manager", "Boss", "Boss", "Manager", "Manager", "Manager", 
"Boss", "Boss", "Boss", "Boss", "Boss"), job2 = c(1L, 1L, 1L, 
1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 0L)), .Names = c("id", 
"name", "year", "job", "job2"), class = "data.frame", row.names = c(NA, 
-16L))
dat <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L), name = c("Jane", "Jane", "Jane", "Jane", 
"Jane", "Jane", "Jane", "Jane", "Bob", "Bob", "Bob", "Bob", "Bob", 
"Bob", "Bob", "Bob"), year = c(1980L, 1981L, 1982L, 1983L, 1984L, 
1985L, 1986L, 1987L, 1985L, 1986L, 1987L, 1988L, 1989L, 1990L, 
1991L, 1992L), job = c("Manager", "Manager", "Manager", "Manager", 
"Manager", "Manager", "Boss", "Boss", "Manager", "Manager", "Manager", 
"Boss", "Boss", "Boss", "Boss", "Boss"), job2 = c(1L, 1L, 1L, 
1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 0L, 0L, 0L, 0L, 0L)), .Names = c("id", 
"name", "year", "job", "job2"), class = "data.frame", row.names = c(NA, 
-16L))

data.table vs dplyr: может ли что-то сделать хорошо, а другое плохо или плохо?: 2 комментария

  1. Здесь моя попытка всестороннего ответа с точки зрения dplyr,
    следуя широкому описанию ответа Аруна (но несколько перестроен
    основанные на разных приоритетах).

    Синтаксис

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

    Одна важная вещь, которую делает dplyr для вас, это то, что она
    сдерживает ваши варианты. Я утверждаю, что большинство проблем с одной таблицей могут
    решаться с помощью всего лишь пяти ключевых глаголов, фильтровать, выбирать, мутировать, упорядочивать и
    резюмируем, наряду с наречием "по группе". Это ограничение — большая помощь
    когда вы изучаете манипуляции с данными, поскольку это помогает упорядочить ваши
    думая о проблеме. В dplyr каждый из этих глаголов отображается на
    одиночная функция. Каждая функция выполняет одно задание и легко понятна
    в изоляции.

    Вы создаете сложность, прокладывая эти простые операции вместе с
    %>%. Вот пример из одного из сообщений Arun связанных
    к
    :

    diamonds %>%
      filter(cut != "Fair") %>%
      group_by(cut) %>%
      summarize(
        AvgPrice = mean(price),
        MedianPrice = as.numeric(median(price)),
        Count = n()
      ) %>%
      arrange(desc(Count))
    

    Даже если вы никогда не видели dplyr раньше (или даже R!), вы все равно можете получить
    суть происходящего, потому что функции — все английские
    глаголы. Недостатком английских глаголов является то, что они требуют большего набора текста, чем
    [, но я думаю, что это может быть в значительной степени смягчено за счет лучшей автозаполнения.

    Здесь эквивалентный код данных.

    diamondsDT <- data.table(diamonds)
    diamondsDT[
      cut != "Fair", 
      .(AvgPrice = mean(price),
        MedianPrice = as.numeric(median(price)),
        Count = .N
      ), 
      by = cut
    ][ 
      order(-Count) 
    ]
    

    Сложнее выполнить этот код, если вы уже не знакомы с
    Таблица данных. (Я также не мог понять, как сделать отступ повторным [
    таким образом, который хорошо выглядит на мой взгляд). Лично, когда я смотрю код I
    написал 6 месяцев назад, ему нравится смотреть на код, написанный незнакомцем,
    поэтому я пришел, чтобы предпочесть простой, если подробный код.

    Два других незначительных фактора, которые, по моему мнению, несколько уменьшают читаемость:

    • Поскольку почти каждая операция таблицы данных использует [, вам нужно дополнительное
      контекст, чтобы выяснить, что происходит. Например, x[y]
      объединение двух таблиц данных или извлечение столбцов из фрейма данных?
      Это всего лишь небольшая проблема, потому что в хорошо написанном коде
      имена переменных должны указывать на то, что происходит.

    • Мне нравится, что group_by() — отдельная операция в dplyr. Это
      коренным образом меняет вычисления, поэтому я считаю, что должно быть очевидно
      при уменьшении кода, и легче определить group_by(), чем
      аргумент by для [.data.table.

    Мне также нравится, что труба
    не ограничивается только одним пакетом. Вы можете начать с
    данные с
    tidyr и
    закончите с сюжетом в ggvis. И вы
    не ограничиваясь пакетами, которые я пишу — любой может написать функцию
    который образует бесшовную часть канала обработки данных. На самом деле, я
    скорее предпочитают предыдущий код данных. таблица, переписанная с помощью %>%:

    diamonds %>% 
      data.table() %>% 
      .[cut != "Fair", 
        .(AvgPrice = mean(price),
          MedianPrice = as.numeric(median(price)),
          Count = .N
        ), 
        by = cut
      ] %>% 
      .[order(-Count)]
    



    И идея трубопровода с %>% не ограничивается только кадрами данных и
    легко обобщается на другие контексты: интерактивная сеть
    графика
    , web
    соскоб
    ,
    gists, время выполнения
    контракты
    ,…)

    Память и производительность

    Я собрал их вместе, потому что для меня это не так важно.
    Большинство пользователей R работают с более чем 1 миллионом строк данных, а dplyr —
    достаточно быстро для такого размера данных, о которых вы не знаете
    время обработки. Мы оптимизируем dplyr для выразительности на средних данных;
    не стесняйтесь использовать data.table для сырой скорости при больших данных.

    Гибкость dplyr также означает, что вы можете легко настроить производительность
    используя тот же синтаксис. Если производительность dplyr с
    бэкэнда фрейма данных недостаточно для вас, вы можете использовать
    data.table(хотя и несколько ограниченный набор функций).
    Если данные, с которыми вы работаете, не помещаются в память, вы можете использовать
    бэкэнд базы данных.

    Все, что сказал, производительность dplyr улучшится в долгосрочной перспективе. Что ж
    определенно реализовать некоторые из великих идей data.table, таких как radix
    упорядочивание и использование одного и того же индекса для объединений и фильтров. Мы также
    работая над параллелизацией, чтобы мы могли использовать несколько ядер.

    Функции

    Несколько вещей, над которыми мы планируем работать в 2015 году:

    • пакет fastread, чтобы упростить загрузку файлов с диска и в
      к памяти, аналог к ​​ fread().

    • Более гибкие соединения, включая поддержку неравномерных объединений.

    • Более гибкая группировка, например, образцы бутстрапов, накопительные пакеты и многое другое

    Я также инвестирую время в улучшение R database
    разъемы
    , возможность разговаривать с
    web apis, и это облегчает
    scrape html pages.

  2. В прямой ответ на заголовок вопроса…

    dplyr определенно делает то, что data.table не может.

    Ваша точка № 3

    dplyr тезисы (или будут) потенциальные взаимодействия с БД

    является прямым ответом на ваш собственный вопрос, но не поднимается до достаточно высокого уровня. dplyr — действительно расширяемый интерфейс для нескольких механизмов хранения данных, где data.table является расширением до одного.

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

    Вы никогда не захотите (я надеюсь) увидеть день, когда data.table пытается перевести ваши запросы на создание операторов SQL, работающих с дисковыми или сетевыми хранилищами данных.

    dplyr может делать вещи data.table не будет или может не работать.

    Основываясь на дизайне рабочей памяти, data.table может иметь гораздо более трудное время, расширяя себя на параллельную обработку запросов, чем dplyr.


    В ответ на вопросы тела…

    Использование

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

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

    Производительность

    Существуют ли аналитические задачи, которые выполняются по существу (то есть более чем 2x) более эффективно в одном пакете по сравнению с другим.

    Опять же, нет. data.table превосходит эффективность во всем, что он делает, где dplyr получает бремя ограниченности в некотором отношении базовому хранилищу данных и зарегистрированным обработчикам.

    Это означает, что когда вы столкнулись с проблемой производительности с помощью data.table, вы можете быть уверены, что она находится в вашей функции запроса, и если это на самом деле узкое место с data.table, тогда вы выиграли от радости от подачи отчета, Это также верно, если dplyr использует data.table в качестве исходного кода; вы можете увидеть некоторые издержки из dplyr, но вероятность того, что это ваш запрос.

    Когда dplyr имеет проблемы с производительностью с обратными концами, вы можете обойти их, зарегистрировав функцию для гибридной оценки или (в случае баз данных), обрабатывая сгенерированный запрос до выполнения.

    Также см. принятый ответ когда он лучше, чем data.table?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *