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

Обзор

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

  1. data.table и dplyr сопоставимы по скорости, за исключением случаев, когда групп много (т. Е.> 10–100 КБ), и в некоторых других обстоятельствах (см. Тесты ниже)
  2. dplyr имеет более доступный синтаксис
  3. dplyr абстрагирует (или будет) потенциальные взаимодействия с БД
  4. Есть некоторые незначительные различия в функциональности (см. «Примеры / Использование» ниже).

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

Вопрос

Я хочу знать следующее:

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

Один недавний вопрос 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 был значительно более кратким или работал значительно лучше.

Примеры

Usage
  • dplyr не разрешает сгруппированные операции, возвращающие произвольное количество строк (из вопрос eddi, примечание: похоже, это будет реализовано в 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 определенно возможно, просто требует некоторого тщательного обдумывания, замены / цитирования и т. Д., По крайней мере, насколько мне известно)
Benchmarks

Данные

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

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))

person BrodieG    schedule 29.01.2014    source источник
comment
Решение, которое по чтению похоже на решение dplyr: as.data.table(dat)[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], by = list(name, job)]   -  person eddi    schedule 29.01.2014
comment
Для № 1 команды dplyr и data.table работают над тестами, поэтому в какой-то момент ответ будет. # 2 (синтаксис) imO строго неверно, но это явно выходит на территорию мнений, поэтому я тоже голосую за закрытие.   -  person eddi    schedule 29.01.2014
comment
@eddi, но есть ли проблемы, которые можно четко выразить в одном, но нет в другом? Или, по-вашему, это сугубо стиль?   -  person BrodieG    schedule 29.01.2014
comment
ну, снова imO, набор проблем, которые более четко выражены в (d)plyr, имеет меру 0   -  person eddi    schedule 29.01.2014
comment
@eddi, это тоже мое общее мнение, хотя я недостаточно хорошо знаю dplyr, чтобы исключить его, и мне было любопытно, может ли кто-нибудь придумать хороший контрпример. Кроме того, приятное упрощение формулировки data.table.   -  person BrodieG    schedule 29.01.2014
comment
Это очень похоже на stackoverflow.com/q/16153947/892313, который спрашивал о plyr и data.table и также был закрыт. Мой ответ здесь объясняет, почему это больше.   -  person Brian Diggs    schedule 29.01.2014
comment
@eddi ИМХО набор проблем, более четко выраженный в data.table, имеет меру 0, но это не противоречит вашему мнению;)   -  person hadley    schedule 29.01.2014
comment
@hadley :) это непротиворечиво только в том случае, если вы думаете, что ни один из них не покрывает набор проблем с ненулевой мерой; на более серьезном замечании, я думаю, что виньетка, которая покрывает 99% вопросов группы / суммирования / и т.д., которые появляются на SO (которые все являются очень близкими реинкарнациями примерно 5-10 различных проблем), со всеми различными реализациями и их тестами давно пора   -  person eddi    schedule 29.01.2014
comment
@eddi также согласуется, если вы считаете, что data.table и dplyr одинаково выразительны для каждой проблемы. Я не думаю, что тесты так интересны, но с моей точки зрения cran.r-project.org/web/packages/dplyr/vignettes/ охватывает наиболее интересные (для меня) типы проблем.   -  person hadley    schedule 30.01.2014
comment
@hadley да, это, вероятно, покрывает большинство проблем. Два быстрых комментария - нет data.table присоединений в конце? И (вероятно, повторяя очевидное, изложенное в другом месте) как насчет более интересных размеров данных (где я бы определил интересное как что-то, что занимает больше минуты в базе - что обычно является основной причиной, по которой люди начинают изучать тесты пакетов)?   -  person eddi    schedule 30.01.2014
comment
@BrodieG - единственное, что меня действительно беспокоит в отношении синтаксиса как dplyr, так и plyr, и, по сути, является основной причиной того, что мне не нравится их синтаксис, заключается в том, что мне нужно выучить слишком много (читайте более 1) дополнительных функций (с именами что все еще не имеют для меня смысла), помните, что они делают, какие аргументы приводят и т. д. Это всегда сильно отталкивало меня от философии plyr.   -  person eddi    schedule 30.01.2014
comment
@eddi Я просто устал писать преобразования data.table. Я с радостью приму пул-реквест, в который добавлены эквиваленты data.table. Я придерживался небольших размеров данных, поскольку большинство алгоритмов - O (n), но я был бы рад увидеть примеры счетчиков.   -  person hadley    schedule 30.01.2014
comment
@eddi [насмешливо] Единственное, что меня действительно беспокоит в синтаксисе data.table, - это то, что я должен узнать, как взаимодействуют слишком многие аргументы функций и что означают загадочные сокращения (например, .SD). [серьезно] Я думаю, что это законные различия в дизайне, которые понравятся разным людям   -  person hadley    schedule 30.01.2014
comment
@hadley re .SD et al - это справедливо - .SD мне потребовалось немного времени, чтобы понять, но к тому времени, когда я добрался до этого, я уже был в состоянии сделать много, в то время как (d) plyr представляет вам большой барьер прямо перед собой.   -  person eddi    schedule 30.01.2014
comment
Кажется, это пример, когда data.table (в настоящее время) предоставляет более простое решение: stackoverflow.com/questions/21295936/   -  person Vincent    schedule 31.01.2014
comment
Здесь [stackoverflow.com/questions/21477525/ - это один из примеров сравнения dplyr и data.table.   -  person danas.zuokas    schedule 31.01.2014
comment
Здесь есть аспект сравнения, который я только что сделал, чтобы дать дополнительный взгляд на недавние твиты о соединениях на dplyr /Таблица данных. HTH.   -  person Arun    schedule 07.05.2014
comment
@ Арун, интересно. Я записал это в своем посте.   -  person BrodieG    schedule 07.05.2014
comment
Думал, что этот вопрос был закрыт как не разрешенный на S.O.? Если его собираются поддерживать здесь, то как насчет того, чтобы сделать его вики-страницей сообщества - я полностью за это. В настоящее время в сравнении чего-то не хватает; например заказал присоединяется. Можно ли добавлять ответы?   -  person Matt Dowle    schedule 24.10.2014
comment
@MattDowle, он был закрыт, но кто-то его снова открыл. Предположительно ответы разрешены? Конечно, можно превратить в вики сообщества (не знаю, как).   -  person BrodieG    schedule 24.10.2014
comment
Роллинг присоединяется. data.table делает их. dplyr нет (пока).   -  person dholstius    schedule 05.11.2014
comment
@dholstius, теперь отмечен, спасибо, что указали на это, я об этом не подумал. Это также привело меня к открытию реализации перекрывающихся соединений.   -  person BrodieG    schedule 05.11.2014
comment
Мой совет для тех, кто начинает с одного из них вроде меня: я бы порекомендовал выучить data.table, он намного менее подробен, чем стандартный фрейм данных, и большинство операций выполняется быстрее. Это заставляет вас думать векторным мышлением (что дает более высокую производительность). Я считаю это хорошей заменой фрейма данных. Синтаксис сначала не был интуитивно понятным, но как только вы начнете его использовать, его легко запомнить. Я вижу dplyr как набор функций, но data.table как новый объект класса с гораздо более высокой производительностью и кратким синтаксисом, чем традиционный фрейм данных.   -  person David Leal    schedule 15.03.2017
comment
@eddi Почему вы пишете все это выражение на стороне j, а не на стороне i?   -  person skan    schedule 16.07.2017
comment
@skan Я больше не помню контекста этого, по-видимому, для достижения подобного прочтения.   -  person eddi    schedule 17.07.2017


Ответы (4)


Нам необходимо охватить по крайней мере эти аспекты, чтобы дать исчерпывающий ответ / сравнение (без определенного порядка важности): Speed, Memory usage, Syntax и Features.

Я намерен максимально четко осветить каждый из них с точки зрения таблицы данных.

Примечание: если явно не указано иное, ссылаясь на dplyr, мы ссылаемся на интерфейс dplyr data.frame, внутреннее устройство которого находится на C ++ с использованием Rcpp.


Синтаксис data.table согласован по форме - DT[i, j, by]. Сохранять вместе i, j и by - это задумано. Сохраняя связанные операции вместе, он позволяет легко оптимизировать операции для скорости и, что более важно, использования памяти, а также предоставляет некоторые мощные функции при сохранении единообразия синтаксиса.

1. Скорость

К вопросу, уже показывающему, что data.table становится быстрее, чем dplyr, было добавлено довольно много тестов (хотя в основном по операциям группировки) по количеству групп и / или строк для группировки по увеличению, включая тесты Мэтта по группировке от 10 миллионов до 2 миллиардов строк (100 ГБ ОЗУ) для 100–10 миллионов групп и различных столбцов группировки, что также позволяет сравнить pandas. См. Также обновленные тесты производительности, которые также включают Spark и pydatatable.

Что касается тестов, было бы неплохо охватить и эти оставшиеся аспекты:

  • Операции группирования, включающие подмножество строк, т. Е. Операции типа DT[x > val, sum(y), by = z].

  • Сравните другие операции, такие как обновление и присоединение.

  • Также проверьте объем памяти для каждой операции в дополнение к времени выполнения.

2. Использование памяти

  1. # P10 #
    # P11 #
  2. # P12 #
     # sub-assign by reference, updates 'y' in-place
     DT[x >= 1L, y := NA]
    
    # P13 #
     # copies the entire 'y' column
     ans <- DF %>% mutate(y = replace(y, which(x >= 1L), NA))
    
    # P14 # P15 #
     foo <- function(DT) {
         DT = shallow(DT)          ## shallow copy DT
         DT[, newcol := 1L]        ## does not affect the original DT 
         DT[x > 2L, newcol := 2L]  ## no need to copy (internally), as this column exists only in shallow copied DT
         DT[x > 2L, x := 3L]       ## have to copy (like base R / dplyr does always); otherwise original DT will 
                                   ## also get modified.
     }
    
    # P16 #
     bar <- function(DT) {
         DT[, newcol := 1L]        ## old behaviour, original DT gets updated by reference
         DT[x > 2L, x := 3L]       ## old behaviour, update column x in original DT.
     }
    
    # P17 #
    # P18 #
    # P19 #
  3. Агрегировать при присоединении:

    Предположим, у вас есть две таблицы data.tables, как показано ниже:

     DT1 = data.table(x=c(1,1,1,1,2,2,2,2), y=c("a", "a", "b", "b"), z=1:8, key=c("x", "y"))
     #    x y z
     # 1: 1 a 1
     # 2: 1 a 2
     # 3: 1 b 3
     # 4: 1 b 4
     # 5: 2 a 5
     # 6: 2 a 6
     # 7: 2 b 7
     # 8: 2 b 8
     DT2 = data.table(x=1:2, y=c("a", "b"), mul=4:3, key=c("x", "y"))
     #    x y mul
     # 1: 1 a   4
     # 2: 2 b   3
    

    И вы хотите получить sum(z) * mul для каждой строки в DT2 при объединении по столбцам x,y. Мы можем либо:

      1. агрегировать DT1, чтобы получить sum(z), 2) выполнить соединение и 3) умножить (или)

        data.table способ

        DT1 [,. (Z = sum (z)), keyby =. (X, y)] [DT2] [, z: = z * mul] []

        эквивалент dplyr

        DF1% ›% group_by (x, y)%›% summarize (z = sum (z))% ›% right_join (DF2)%›% mutate (z = z * mul)

      1. сделайте все за один раз (используя функцию by = .EACHI):

        DT1 [DT2, list (z = sum (z) * mul), by = .EACHI]

    В чем преимущество?

    • Нам не нужно выделять память для промежуточного результата.

    • Нам не нужно дважды группировать / хешировать (один для агрегации, а другой для присоединения).

    • И что еще более важно, операция, которую мы хотели выполнить, становится понятной, если посмотреть на j в (2).

    Подробное описание by = .EACHI см. В этом сообщении. Никаких промежуточных результатов не материализуется, и объединение + агрегирование выполняется за один раз.

    Взгляните на this, this и this публикуют сообщения для реальных сценариев использования.

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

  4. Обновление и присоединение:

    Рассмотрим код data.table, показанный ниже:

     DT1[DT2, col := i.mul]
    

    добавляет / обновляет столбец DT1 col на mul из DT2 в тех строках, где ключевой столбец DT2 совпадает с DT1. Я не думаю, что есть точный эквивалент этой операции в dplyr, т.е. без исключения *_join операции, которая должна была бы скопировать весь DT1 только для того, чтобы добавить к нему новый столбец, что не нужно.

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

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

3. Синтаксис

Теперь посмотрим на синтаксис. Хэдли прокомментировал здесь:

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

Я считаю это замечание бессмысленным, потому что оно очень субъективно. Возможно, мы можем попробовать противопоставить согласованность синтаксиса. Мы сравним синтаксис data.table и dplyr бок о бок.

Мы будем работать с фиктивными данными, показанными ниже:

DT = data.table(x=1:10, y=11:20, z=rep(1:2, each=5))
DF = as.data.frame(DT)
  1. Основные операции агрегирования / обновления.

     # case (a)
     DT[, sum(y), by = z]                       ## data.table syntax
     DF %>% group_by(z) %>% summarise(sum(y)) ## dplyr syntax
     DT[, y := cumsum(y), by = z]
     ans <- DF %>% group_by(z) %>% mutate(y = cumsum(y))
    
     # case (b)
     DT[x > 2, sum(y), by = z]
     DF %>% filter(x>2) %>% group_by(z) %>% summarise(sum(y))
     DT[x > 2, y := cumsum(y), by = z]
     ans <- DF %>% group_by(z) %>% mutate(y = replace(y, which(x > 2), cumsum(y)))
    
     # case (c)
     DT[, if(any(x > 5L)) y[1L]-y[2L] else y[2L], by = z]
     DF %>% group_by(z) %>% summarise(if (any(x > 5L)) y[1L] - y[2L] else y[2L])
     DT[, if(any(x > 5L)) y[1L] - y[2L], by = z]
     DF %>% group_by(z) %>% filter(any(x > 5L)) %>% summarise(y[1L] - y[2L])
    
    • Синтаксис data.table компактен и довольно многословен в dplyr. В случае (а) все более или менее эквивалентно.

    • В случае (b) нам пришлось использовать filter() в dplyr при подведении итогов. Но при обновлении нам пришлось переместить логику внутрь mutate(). Однако в data.table мы выражаем обе операции с одинаковой логикой - работаем со строками, где x > 2, но в первом случае получаем sum(y), тогда как во втором случае обновляем эти строки для y с его совокупной суммой.

      Это то, что мы имеем в виду, когда говорим, что форма DT[i, j, by] согласована.

    • Точно так же в случае (c), когда у нас есть if-else условие, мы можем выразить логику как есть как в data.table, так и в dplyr. Однако, если мы хотим вернуть только те строки, которым удовлетворяет условие if, и пропустить в противном случае, мы не можем использовать summarise() напрямую (AFAICT). Мы должны сначала filter(), а затем подвести итоги, потому что summarise() всегда ожидает единственного значения.

      Хотя он возвращает тот же результат, использование здесь filter() делает фактическую операцию менее очевидной.

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

  2. Агрегация / обновление по нескольким столбцам

     # case (a)
     DT[, lapply(.SD, sum), by = z]                     ## data.table syntax
     DF %>% group_by(z) %>% summarise_each(funs(sum)) ## dplyr syntax
     DT[, (cols) := lapply(.SD, sum), by = z]
     ans <- DF %>% group_by(z) %>% mutate_each(funs(sum))
    
     # case (b)
     DT[, c(lapply(.SD, sum), lapply(.SD, mean)), by = z]
     DF %>% group_by(z) %>% summarise_each(funs(sum, mean))
    
     # case (c)
     DT[, c(.N, lapply(.SD, sum)), by = z]     
     DF %>% group_by(z) %>% summarise_each(funs(n(), mean))
    
    • В случае (а) коды более или менее эквивалентны. data.table использует знакомую базовую функцию lapply(), тогда как dplyr вводит *_each() вместе с набором функций для funs().

    • := для data.table требует, чтобы имена столбцов были указаны, тогда как dplyr генерирует их автоматически.

    • В случае (b) синтаксис dplyr относительно прост. Улучшение агрегирования / обновления для нескольких функций находится в списке data.table.

    • Однако в случае (c) dplyr вернет n() столько столбцов, а не только один раз. В data.table все, что нам нужно сделать, это вернуть список в j. Каждый элемент списка станет столбцом в результате. Итак, мы можем снова использовать знакомую базовую функцию c() для объединения .N в list, который возвращает list.

    Примечание. Еще раз, в data.table все, что нам нужно сделать, это вернуть список в j. В результате каждый элемент списка станет столбцом. Для этого вы можете использовать базовые функции c(), as.list(), lapply(), list() и т. Д., Без необходимости изучать какие-либо новые функции.

    Вам нужно будет изучить только специальные переменные - по крайней мере, .N и .SD. Эквивалентными в dplyr являются n() и .

  3. Присоединяется

    dplyr предоставляет отдельные функции для каждого типа соединения, в то время как data.table позволяет объединять с использованием того же синтаксиса DT[i, j, by] (и с обоснованием). В качестве альтернативы он также предоставляет эквивалентную функцию merge.data.table().

     setkey(DT1, x, y)
    
     # 1. normal join
     DT1[DT2]            ## data.table syntax
     left_join(DT2, DT1) ## dplyr syntax
    
     # 2. select columns while join    
     DT1[DT2, .(z, i.mul)]
     left_join(select(DT2, x, y, mul), select(DT1, x, y, z))
    
     # 3. aggregate while join
     DT1[DT2, .(sum(z) * i.mul), by = .EACHI]
     DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% 
         inner_join(DF2) %>% mutate(z = z*mul) %>% select(-mul)
    
     # 4. update while join
     DT1[DT2, z := cumsum(z) * i.mul, by = .EACHI]
     ??
    
     # 5. rolling join
     DT1[DT2, roll = -Inf]
     ??
    
     # 6. other arguments to control output
     DT1[DT2, mult = "first"]
     ??
    
  • Некоторые могут найти более удобную отдельную функцию для каждого соединения (левое, правое, внутреннее, анти, полу и т. Д.), В то время как другим может понравиться DT[i, j, by] или merge() data.table, который похож на базовый R.

  • Однако присоединения dplyr именно так и делают. Больше ничего. Не меньше.

  • data.tables может выбирать столбцы при объединении (2), и в dplyr вам нужно сначала select() на обоих data.frames, прежде чем присоединяться, как показано выше. В противном случае вы материализуете соединение с ненужными столбцами только для того, чтобы удалить их позже, а это неэффективно.

  • data.tables могут объединяться при объединении с помощью функции by = .EACHI (3), а также обновляться при объединении (4). Зачем материализовать весь результат соединения, чтобы добавить / обновить всего несколько столбцов?

  • data.table поддерживает скользящие соединения (5) - прокрутите вперед, LOCF, откат назад, NOCB, ближайший .

  • data.table также имеет аргумент mult =, который выбирает первое, последнее или все совпадения (6).

  • data.table имеет аргумент allow.cartesian = TRUE для защиты от случайных недопустимых объединений.

И снова синтаксис соответствует DT[i, j, by] с дополнительными аргументами, позволяющими дополнительно контролировать вывод.

  1. do()...

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

     DT[, list(x[1], y[1]), by = z]                 ## data.table syntax
     DF %>% group_by(z) %>% summarise(x[1], y[1]) ## dplyr syntax
     DT[, list(x[1:2], y[1]), by = z]
     DF %>% group_by(z) %>% do(data.frame(.$x[1:2], .$y[1]))
    
     DT[, quantile(x, 0.25), by = z]
     DF %>% group_by(z) %>% summarise(quantile(x, 0.25))
     DT[, quantile(x, c(0.25, 0.75)), by = z]
     DF %>% group_by(z) %>% do(data.frame(quantile(.$x, c(0.25, 0.75))))
    
     DT[, as.list(summary(x)), by = z]
     DF %>% group_by(z) %>% do(data.frame(as.list(summary(.$x))))
    
  • .SD эквивалент .

  • В data.table вы можете бросить почти все что угодно в j - единственное, что нужно помнить, - это возвращать список, чтобы каждый элемент списка преобразовывался в столбец.

  • В dplyr этого сделать нельзя. Придется прибегать к do() в зависимости от того, насколько вы уверены в том, всегда ли ваша функция будет возвращать одно значение. И это довольно медленно.

И снова синтаксис data.table соответствует DT[i, j, by]. Мы можем просто добавлять выражения в j, не беспокоясь об этих вещах.

Взгляните на этот вопрос SO и этот. Интересно, можно ли было бы выразить ответ настолько просто, используя синтаксис dplyr ...

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

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

Но следует также учитывать количество функций, которых не хватает dplyr по сравнению с data.table.

4. Особенности

Я указал на большинство функций здесь, а также в этом сообщении. Кроме того:

  • fread - быстрая программа для чтения файлов доступна уже давно.

  • fwrite - теперь доступна распараллеленная быстрая запись файлов. См. этот пост для подробного объяснения реализации. и # 1664 для отслеживания дальнейших разработок.

  • Автоматическое индексирование - еще одна удобная функция для внутренней оптимизации базового синтаксиса R.

  • Специальная группировка: dplyr автоматически сортирует результаты, группируя переменные в течение summarise(), что может быть не всегда желательно.

  • Упомянутые выше многочисленные преимущества объединений data.table (для скорости / эффективности использования памяти и синтаксиса).

  • Неэквивалентные соединения: разрешает соединения с использованием других операторов <=, <, >, >= наряду со всеми другими преимуществами соединений data.table.

  • Объединение перекрывающихся диапазонов было недавно реализовано в data.table. Ознакомьтесь с этим сообщением для обзора тестов.

  • setorder() функция в data.table, которая позволяет очень быстро переупорядочивать data.tables по ссылке.

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

  • data.table обеспечивает более быстрые эквиваленты операций с множеством (написано Яном Горецки) - fsetdiff, fintersect, funion и fsetequal с дополнительным аргументом all (как в SQL).

  • data.table загружается чисто, без предупреждений о маскировании и имеет механизм, описанный здесь для [.data.frame совместимости при передаче в любой пакет R. dplyr изменяет базовые функции filter, lag и [, что может вызвать проблемы; например здесь и здесь.


Наконец-то:

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

  • О параллелизме - Все сложно, пока кто-то не пойдет вперед и не сделает. Конечно, это потребует усилий (потокобезопасность).

    • Progress is being made currently (in v1.9.7 devel) towards parallelising known time consuming parts for incremental performance gains using OpenMP.
person Community    schedule 31.12.2014
comment
... Итак, то, что вы делаете (несколько неявно), - это подмножество строк, и это то, для чего создан filter в dplyr. - person talat; 31.12.2014
comment
@docendodiscimus, извини, но каков идиоматический способ сделать DT[, if(any(x>1)) y[1], by=z] еще раз, пожалуйста? Теперь я смущен. Следует или не следует использовать здесь фильтр? - person Arun; 31.12.2014
comment
summarise() еще не имеет столбцов списка, но mutate() есть, и та же проблема с . существует (да, это не идиоматично, но ошибка есть ошибка). - person Arun; 31.12.2014
comment
Вам следует использовать фильтр, поскольку вы разбиваете строки на подмножества. (В вашем примере вы не удаляете ни одну из групп z, поскольку обе они имеют any(x > 1) == TRUE. Так что, скорее всего, вы захотите сделать: DF %>% group_by(z) %>% filter(any(x > 1)) %>% summarise(y = y[1]). - person talat; 31.12.2014
comment
Комментарии не подлежат расширенному обсуждению; этот разговор был перешел в чат. - person Taryn; 31.12.2014
comment
@bluefeet: Я не думаю, что вы оказали остальным из нас какую-либо большую услугу, переместив обсуждение в чат. У меня сложилось впечатление, что Арун был одним из разработчиков, и это могло привести к полезным выводам. - person IRTFM; 06.01.2015
comment
@BondedDust Проблема заключалась в том, что к этому сообщению было добавлено более 30 комментариев, что делало невероятно трудным найти что-либо, что могло бы быть в них полезным. Все комментарии были перемещены в чат, после чего их можно было включить в ответ по мере необходимости. В комментариях не должно быть расширенных обсуждений, для этого и нужен чат. - person Taryn; 06.01.2015
comment
Когда я пошел в чат по вашей ссылке, оказалось, что весь материал, следующий за комментарием, начинающимся «Вы должны использовать фильтр»…, пропал. Мне что-то не хватает в механизме SO-чата? - person IRTFM; 06.01.2015
comment
@BondedDust Никаких дополнительных комментариев здесь или в чате после комментария You should filter ... не было. Похоже, никто не продолжил обсуждение ни здесь, ни в чате. - person Taryn; 06.01.2015
comment
Я думаю, что везде, где вы используете присвоение по ссылке (:=), эквивалент dplyr также должен использовать <-, как в DF <- DF %>% mutate..., а не просто DF %>% mutate... - person David Arenburg; 07.01.2015
comment
По поводу синтаксиса. Я считаю, что dplyr может быть проще для пользователей, которые использовали plyr синтаксис, но data.table может быть проще для пользователей, которые использовали синтаксис языков, таких как SQL, и реляционную алгебру, стоящую за ним, которая связана с преобразованием табличных данных. @Arun, вы должны заметить, что операторы набора очень легко выполнить, обернув функцию data.table, и, конечно же, значительно ускоряет выполнение. - person jangorecki; 14.01.2015
comment
добавьте dplyr внутри функции и сразу увидите экспоненциально сложный код. вы также должны добавить их в свое сравнение - person MySchizoBuddy; 10.06.2016
comment
@MySchizoBuddy, может быть, суть, чтобы показать, о чем вы конкретно говорите? Это версия SE, которую вы приписываете? - person Arun; 15.06.2016
comment
@Arun да, когда dplyr используется внутри функции, вы должны использовать версии SE вместе с lazyeval :: interp () Есть ли у data.table аналогичные проблемы при использовании внутри функции - person MySchizoBuddy; 16.06.2016
comment
@MySchizoBuddy, хорошее замечание. .SD в data.table вместе с .SDcols намного проще, чем interp() AFAIU, по крайней мере, для большинства сценариев. Этого вместе с использованием get() и mget() (в редких случаях) достаточно в ~ 99% всех сценариев. [data.table должна реализовать еще одну вещь в i-аргументе - лучшие правила определения объема, но это крайне редкий сценарий.] Я подумаю, как отредактировать это в сообщении, если мне удастся понять interp(). Спасибо. - person Arun; 16.06.2016
comment
@Arun Мне бы очень хотелось, чтобы data.table поддерживал бэкенды базы данных! Представьте себе синтаксис data.table на бэкэнде Redshift. Возможно, с некоторым уровнем локального кэширования, чтобы избежать дорогостоящих повторных запросов, когда это возможно. Вот это да. - person andrew; 08.08.2018
comment
Я так много раз читал этот пост, и он очень помог мне в понимании data.table и возможности лучше его использовать. В большинстве случаев я предпочитаю data.table dplyr, pandas или PL / pgSQL. Однако я не мог перестать думать, как это выразить. Синтаксис непростой, ясный или подробный. Фактически, даже после того, как я много использовал data.table, мне все еще трудно понять свой собственный код, который я написал буквально неделю назад. Это жизненный пример языка только для записи. en.wikipedia.org/wiki/Write-only_language Итак, будем надеяться, что однажды мы сможем использовать dplyr для data.table. - person Ufos; 18.12.2018
comment
В c) и b) из 1. Основные операции агрегирования / обновления. Я не получаю эквивалентных результатов от dplyr и dt. - person its.me.adam; 05.03.2020
comment
На самом деле большая часть кода dplyr больше не применяется (из-за обновлений?) ... В этом ответе можно было бы обновить, поскольку это такой отличный ресурс. - person its.me.adam; 05.03.2020
comment
@ its.me.adam проблема в том, что этот код менялся несколько раз с течением времени, поэтому его обновление на самом деле бессмысленно, потому что через некоторое время вам нужно будет обновить его снова. Искать записи вручную (для текущего API) не так сложно. - person jangorecki; 20.10.2020
comment
@Ufos теперь вы можете использовать синтаксис dplyr, который переведен в data.table (это может быть не самый быстрый код, но это начало); cran.r-project.org/web/packages/dtplyr/index. html - person llrs; 10.06.2021

Вот моя попытка дать исчерпывающий ответ с точки зрения dplyr, следуя общей схеме ответа Аруна (но несколько перестроенной на основе разных приоритетов).

Синтаксис

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

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

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

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

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

Вот эквивалентный код data.table:

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

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

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

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

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

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

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

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

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

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

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

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

Функции

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

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

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

  • Более гибкая группировка, например образцы начальной загрузки, накопительные пакеты и т. Д.

Я также трачу время на улучшение коннекторов баз данных в R, возможности общения с API-интерфейсы и упрощение очистить HTML-страницы.

person hadley    schedule 08.01.2015
comment
Кстати, я согласен со многими из ваших аргументов (хотя сам предпочитаю синтаксис data.table), но вы можете легко использовать %>% для выполнения data.table операций, если вам не нравится стиль [. %>% не относится к dplyr, а исходит из отдельного пакета (соавтором которого вы тоже являетесь), поэтому я не уверен, что понимаю, что вы пытаетесь сказать в большей части своего синтаксиса < / b> абзац. - person David Arenburg; 08.01.2015
comment
@DavidArenburg хороший замечание. Я переписал синтаксис, чтобы, надеюсь, прояснить мои основные моменты и подчеркнуть, что вы можете использовать %>% с data.table - person hadley; 08.01.2015
comment
Спасибо Хэдли, это полезная перспектива. Повторный отступ я обычно делаю DT[\n\texpression\n][\texpression\n] (gist), который действительно работает хорошо хорошо. Я сохраняю ответ Аруна в качестве ответа, поскольку он более прямо отвечает на мои конкретные вопросы, которые не столько касаются доступности синтаксиса, но я думаю, что это хороший ответ для людей, пытающихся получить общее представление о различиях / сходствах между dplyr и data.table. - person BrodieG; 08.01.2015
comment
Незначительный момент: чтобы отличить объединения от подмножества столбцов data.frame, вы можете использовать X[J(Y)] или X[.(Y)]. - person Arun; 08.01.2015
comment
@BrodieG, это намного лучше, но мне все равно это не нравится. - person hadley; 08.01.2015
comment
Справедливо. Хотя я счастливый data.table пользователь (и иногда dplyr пользователь), время от времени я чувствую, что data.table пытается сделать слишком много внутри [. - person BrodieG; 08.01.2015
comment
@BrodieG помещает все, что необходимо для алгебры отношений в оператор [, формирует запросы data.table, которые могут быть прямым аналогом (и глубоким расширением) для запросов SQL < / b>. На данный момент я не вижу лишних [ аргументов (кроме drop), и да, их уже много :) - person jangorecki; 08.01.2015
comment
@JanGorecki, на самом деле у меня нет проблем с количеством аргументов data.table (я плохо сформулировал это), но, например, иногда мне кажется, что i не следует использовать как для объединений, так и для индексации, хотя компактный синтаксис хорош ( в отличие от merge), поэтому у меня смешанные чувства по этому поводу. - person BrodieG; 08.01.2015
comment
Зачем работать с fastread, когда уже есть fread()? Разве время не было бы лучше потрачено на улучшение fread () или работу над другими (недоработанными) вещами? - person EDi; 28.01.2015
comment
@EDi, потому что у fastread и fread разные цели. fastread также предоставляет API C ++, поэтому вы можете использовать тот же базовый код для чтения в новых форматах файлов. - person hadley; 28.01.2015
comment
API data.table основан на массовом злоупотреблении нотацией []. Это его величайшая сила и самая большая слабость. - person Paul; 28.01.2016
comment
@DavidArenburg, используя оператор конвейера, как в приведенном выше примере, вы заметили, что есть 4 точки (.) Две из них предназначены для самого объекта данных, а две другие нет. Это меня просто сбивает с толку. - person MySchizoBuddy; 20.06.2016
comment
Спасибо за разрешение использовать data.table на больших данных! Одно это стоило ответа. - person piccolbo; 01.05.2018
comment
Я предпочитаю data.table фрагмент, который вы показываете, dplyr: и я использовал data.table только один раз для небольшой задачи 18 месяцев назад. Достаточно читабельно и лаконично. Мне очень жаль, что в python-land была разумная параллель. Пакет datatable в python медленно улучшается, но не начинает трогать r data.table - person WestCoastProjects; 30.03.2020

В ответ на заголовок вопроса ...

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

Ваша точка зрения # 3

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

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

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

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

dplyr может делать то, что data.table не будет или не может делать тоже.

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


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

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

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

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

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

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

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

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

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

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

person Thell    schedule 16.11.2014
comment
Не удается dplyr обернуть таблицу данных с помощью tbl_dt? Почему бы просто не получить лучшее из обоих миров? - person aaa90210; 09.12.2014
comment
@ aaa90210 см. это сообщение - person BrodieG; 17.12.2014
comment
Вы забываете упомянуть обратный оператор data.table определенно делает то, что dplyr не может, что также верно. - person jangorecki; 05.01.2015
comment
@JanGorecki Это потому, что я не знаю возможности, которая есть у data.table, но dplyr не может быть ни напрямую, ни через обработчики. Существуют особенности (обсуждаемые с точки зрения скорости, памяти и синтаксиса), которые обсуждались как различия в ответ на качественную (плохо) часть вопроса, но я не помню, чтобы видел возможности, которые нельзя / нельзя обобщить и абстрагировать на уровне. - person Thell; 05.01.2015
comment
Ответ Аруна хорошо это объясняет. Наиболее важными (с точки зрения производительности) будут fread, обновление по ссылке, скользящие соединения, перекрывающиеся соединения. Я считаю, что нет ни одного пакета (не только dplyr), который мог бы конкурировать с этими функциями. Хорошим примером может служить последний слайд из этой презентации. - person jangorecki; 05.01.2015
comment
В целом, data.table - вот почему я до сих пор использую R. В противном случае я бы использовал pandas. Это даже лучше / быстрее панд. - person marbel; 02.12.2016
comment
Мне нравится data.table из-за его простоты и сходства со структурой синтаксиса SQL. Моя работа включает в себя ежедневный очень интенсивный специальный анализ данных и графики для статистического моделирования, и мне действительно нужен достаточно простой инструмент, чтобы делать сложные вещи. Теперь я могу сократить свой набор инструментов только до data.table для данных и решетки для графика в моей повседневной работе. Приведите пример, я могу даже выполнять такие операции: $ DT [group == 1, y_hat: = pred (fit1, data = .SD),] $, что действительно удобно, и я считаю это большим преимуществом SQL в классическая среда R. - person xappppp; 31.12.2016

Читая ответы Хэдли и Аруна, создается впечатление, что тем, кто предпочитает синтаксис dplyr, в некоторых случаях придется переключиться на data.table или пойти на компромисс в течение длительного времени.

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

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

new_table <- mtcars2 %>% 
  lazy_dt() %>%
  filter(wt < 5) %>% 
  mutate(l100k = 235.21 / mpg) %>% # liters / 100 km
  group_by(cyl) %>% 
  summarise(l100k = mean(l100k))

  new_table

#> Source: local data table [?? x 2]
#> Call:   `_DT1`[wt < 5][, `:=`(l100k = 235.21/mpg)][, .(l100k = mean(l100k)), 
#>     keyby = .(cyl)]
#> 
#>     cyl l100k
#>   <dbl> <dbl>
#> 1     4  9.05
#> 2     6 12.0 
#> 3     8 14.9 
#> 
#> # Use as.data.table()/as.data.frame()/as_tibble() to access results

Объект new_table не оценивается до его вызова _12 _ / _ 13 _ / _ 14_, после чего выполняется соответствующая операция data.table.

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

person Iyar Lin    schedule 14.06.2020
comment
у вас, вероятно, не будет много функций, для которых в dplyr нет API, таких как подчиненное назначение по ссылке, скользящие соединения, перекрывающиеся соединения, неэквивалентные соединения, обновление при объединении и, возможно, многие другие. - person jangorecki; 24.06.2020
comment
Я должен признать, что ни одна из этих функций не вызывает особого беспокойства. Не могли бы вы привести конкретные примеры в data.table? - person Iyar Lin; 25.06.2020
comment
?data.table примеры, все, что я упомянул, за исключением перекрытия присоединяется. здесь - person jangorecki; 26.06.2020
comment
Обновление при соединении, прокатке, соединение внахлест можно легко построить с несколькими частями трубы. - person Arthur Yip; 16.10.2020
comment
См. Fuzzyjoin для неэквивалентных соединений (кажется, имеет даже больше функций и возможностей, чем неэквивалентные соединения data.table). - person Arthur Yip; 16.10.2020