данные.таблица vs dplyr: может ли один сделать что-то хорошо, а другой не может или делает плохо?
обзор
я относительно знаком с data.table
, не так много с dplyr
. Я прочитал некоторые dplyr
виньеток и примеры, которые выскочили на SO, и до сих пор мои выводы таковы:
-
data.table
иdplyr
сопоставимы по скорости, за исключением тех случаев, когда есть много (т. е. >10-100K) групп, а также в некоторых других обстоятельствах (см. тесты ниже) -
dplyr
имеет более доступными синтаксис -
dplyr
абстракты (или воля) потенциальные взаимодействия с БД - есть некоторые незначительные функциональные различия (см. раздел "примеры использования" ниже)
в моем сознании 2. не несет большого веса, потому что я довольно хорошо знаком с ним data.table
, хотя я понимаю, что для новичков в обоих это будет большой фактор. Я хотел бы избежать аргумента о том, что является более интуитивным, поскольку это не имеет отношения к моему конкретному вопросу, заданному из перспектива кого-то уже знакомого с data.table
. Я также хотел бы избежать обсуждения того, как" более интуитивный " приводит к более быстрому анализу (конечно, верно, но опять же, не то, что меня больше всего интересует здесь).
вопрос
то, что я хочу знать:
- существуют ли аналитические задачи, которые намного проще кодировать с одним или другим пакетом для людей, знакомых с пакетами (т. е. требуется некоторая комбинация нажатий клавиш против необходимый уровень эзотеризма, где меньше каждого-это хорошо).
- существуют ли аналитические задачи, которые выполняются существенно (т. е. более чем в 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
путь существенно более сжат или выполняет существенно более лучше.
примеры
Использование-
dplyr
не позволяет группировать операции, возвращающие произвольное количество строк (от Эдди вопрос, Примечание: похоже, что это будет реализовано в dplyr 0.5, кроме того, @beginneR показывает потенциальную работу с использованиемdo
в ответ на @реитсталл вопрос.) -
data.table
поддерживает подвижного соединения (спасибо @dholstius), а также перекрытие присоединяется -
data.table
внутренне оптимизирует выражения видаDT[col == value]
илиDT[col %in% values]
на скорость через автоматическое индексирование использует бинарный поиск в то время как, используя тот же базовый синтаксис Р. посмотреть здесь для получения более подробной информации и крошечного эталона. -
dplyr
предлагает стандартные оценочные версии функций (напримерregroup
,summarize_each_
), что может упростить программное использованиеdplyr
(обратите внимание на программное использованиеdata.table
определенно возможно, просто требует некоторых тщательных размышлений, замены / цитирования и т. д., По крайней мере, насколько мне известно)
- я побежал мои собственные контрольные показатели и обнаружил, что оба пакета сопоставимы в анализе стиля "split apply combine", за исключением случаев, когда они очень большие количество групп (>100K) в этот момент
data.table
становится существенно быстрее. - @Арун побежал какой-то тесты на присоединениях, показав, что
data.table
весы лучше, чемdplyr
по мере увеличения числа групп (обновлено с последними улучшениями как в пакетах, так и в последней версии R). Кроме того, эталон при попытке получить уникальные значения иdata.table
~6х быстрее. - (непроверенных) и
data.table
75% быстрее на больших версии группы / применить / сортировать в то время какdplyr
был на 40% быстрее на меньших (еще один так вопрос из комментариев, спасибо Данас). - Мэтт, главный автор
data.table
, и бенчмаркинг группировка операций поdata.table
,dplyr
и pythonpandas
на до 2 миллиардов строк (~100 ГБ в оперативной памяти). - An более старый бенчмарк на 80k групп и
data.table
~8x быстрее
сведения
это первый пример, который я показал в разделе Вопрос.
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))
3 ответа:
мы должны охватить по крайней мере эти аспекты, чтобы дать исчерпывающий ответ/сравнение (без особого порядка важности):
Speed
,Memory usage
,Syntax
иFeatures
.мое намерение состоит в том, чтобы охватить каждый из них как можно более четко из данных.таблица перспектива.
Примечание: если явно не указано иное, ссылаясь на dplyr, мы ссылаемся на данные dplyr.интерфейс фрейма, внутренние элементы которого находятся в C++ с использованием Rcpp.
данные.синтаксис таблицы непротиворечив в своей форме -
DT[i, j, by]
. Чтобы сохранитьi
,j
иby
вместе с дизайном. Сохраняя связанные операции вместе, это позволяет оптимизировать операции скорость и что еще более важно использование памяти, а также мощные функции, все при сохранении согласованности в синтаксисе.1. Скорость
довольно много тестов (хотя в основном по операциям группировки) были добавлены к вопросу, уже показывающему данные.таблица получает быстрее чем dplyr как количество групп и/или строк в группе, в том числе бенчмарки от Matt по группировке из от 10 миллионов до 2 миллиардов строк (100 ГБ в оперативной памяти) на 100 - 10 миллионов участников!--123--> и различные столбцы группировки, которые также сравниваются
pandas
.на бенчмарках было бы здорово охватить и эти оставшиеся аспекты:
группировка операций с участием подмножество строк - то есть,
DT[x > val, sum(y), by = z]
операции типа.тест другие операции, такие как обновление и соединения.
также Тест объем памяти для каждой операции в дополнение к времени выполнения.
2. Использование памяти
операции
filter()
илиslice()
в dplyr может быть неэффективна память (на обоих данных.кадры и данные.таблицы.) этот пост.обратите внимание, что Хэдли переговоры о скорость (что dplyr в изобилии быстро для него), в то время как основная проблема здесь .
данные.интерфейс таблицы на данный момент позволяет изменять/обновлять столбцы по ссылке (обратите внимание, что нам не нужно повторно присваивать результат переменной).
# sub-assign by reference, updates 'y' in-place DT[x >= 1L, y := NA]
но dplyr никогда обновление ПО ССЫЛКЕ. Эквивалент dplyr будет (обратите внимание, что результат должен быть повторно назначен):
# copies the entire 'y' column ans <- DF %>% mutate(y = replace(y, which(x >= 1L), NA))
беспокойство по этому поводу референциальной прозрачности. Обновление данных.объект таблицы по ссылке, особенно в пределах функции, может быть не всегда желательным. Но это невероятно полезная функция: см.этой и этой сообщения для интересных случаев. И мы хотим его сохранить.
поэтому мы работаем в направлении экспорта в данных.таблица, которая предоставит пользователю обе возможности. Для например, если желательно не изменять входные данные.таблица внутри функции, затем можно сделать:
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. }
не используя
shallow()
, старая функциональность сохраняется: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. }
создать мелкая копия используя
shallow()
, мы понимаем, что вы не хотите изменять исходный объект. Мы заботимся обо всем внутренне, чтобы гарантировать, что в то же время обеспечивая копирование столбцов, которые вы изменяете только когда это абсолютно необходимо. При реализации, это должно урегулировать референциальной прозрачности проблема в целом, предоставляя пользователю обе возможности.кроме того, один раз
shallow()
экспортируются данные dplyr.интерфейс таблицы должен избегать почти всех копий. Поэтому те, кто предпочитает синтаксис dplyr, могут использовать его с данными.таблицы.но ему все равно будет не хватать многих функций этих данных.таблица предусматривает, в том числе (sub)-присвоение по ссылка.
агрегат при присоединении:
Предположим, у вас есть два данных.таблицы следующим образом:
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 way DT1[, .(z = sum(z)), keyby = .(x,y)][DT2][, z := z*mul][] # dplyr equivalent DF1 %>% group_by(x, y) %>% summarise(z = sum(z)) %>% right_join(DF2) %>% mutate(z = z * mul)
2) сделать все это за один раз (используя
by = .EACHI
характеристика):DT1[DT2, list(z=sum(z) * mul), by = .EACHI]
в чем преимущество?
нам не нужно выделять память для промежуточных результатов.
нам не нужно группировать/хэшировать дважды (один для агрегации, а другой для присоединения).
и что еще более важно, операция, которую мы хотели выполнить, понятна, глядя на
j
in (2).Регистрация этот пост подробное описание
by = .EACHI
. Промежуточные результаты не материализуются, и агрегат join+выполняется за один раз.посмотреть этой,этой и этой сообщения для реальных сценариев использования.
In
dplyr
вы должны сначала присоединиться и агрегировать или агрегировать, а затем присоединиться, ни из которых столь же эффективны, с точки зрения памяти (что в свою очередь переводится на скорость).обновление и присоединяется:
считать данные.код таблицы, показанный ниже:
DT1[DT2, col := i.mul]
добавляет/обновления С
col
Сmul
СDT2
на тех строках, гдеDT2
ключевой столбец соответствуетDT1
. Я не думаю, что есть точный эквивалент этой операции вdplyr
, т. е., не избежать*_join
операции, которые придется скопировать весьDT1
просто добавить новый столбец к нему, что не нужно.Регистрация этот пост для реального сценария использования.
подводя итог, важно понимать, что каждый кусочек имеет значение оптимизации. Как Грейс Хоппер хочу сказать, следите за своими наносекундами!
3. Синтаксис
Давайте теперь посмотрим на синтаксис. Хэдли прокомментировал здесь:
таблицы данных очень быстро, но я думаю, что их краткость делает его труднее учиться и код, который использует его труднее читать после написания ...
я нахожу это замечание бессмысленным, потому что оно очень субъективно. То, что мы, возможно, можем попытаться противопоставить последовательность в синтаксисе. Мы сравним данные.таблица и синтаксис dplyr бок о бок.
мы будем работать с фиктивными данными показано ниже:
DT = data.table(x=1:10, y=11:20, z=rep(1:2, each=5)) DF = as.data.frame(DT)
основные операции агрегации / обновления.
# 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])
данные.синтаксис таблицы компактен и dplyr довольно многословен. Вещи более или менее эквивалентны в случае (а).
в случае (b), мы должны были использовать
filter()
в dplyr пока подводя итоги. Но пока обновление, мы должны были переместить логику внутриmutate()
. В данных.таблица однако, мы выражаем обе операции с той же логикой-работать на строках, гдеx > 2
, но в первом случае, получитьsum(y)
, тогда как во втором случае обновить эти строкиy
с накопленной суммой.вот что мы имеем в виду, когда говорим
DT[i, j, by]
форма последовательно.аналогично в случае (c), когда у нас есть
if-else
условие, мы можем выразить логику "в" в обоих данных.стол и dplyr. Однако, если мы хотели бы вернуть только те строки, гдеif
условию удовлетворяет и пропустить в противном случае, мы не можем использоватьsummarise()
непосредственно (AFAICT). Мы должныfilter()
сначала подведем итоги, потому чтоsummarise()
всегда ожидает одно значение.в то время как он возвращает тот же самый результат, используя
filter()
здесь делает фактическая работа менее очевидна.это вполне может быть возможным, чтобы использовать
filter()
в первом случае также (не кажется очевидным для меня), но я хочу сказать, что мы не должны этого делать.агрегация / обновление по нескольким столбцам
# 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))
в случае (a) коды более или менее эквивалентны. данные.таблица использует знакомую базовую функцию
lapply()
, аdplyr
вводит*_each()
вместе с кучей функцийfuns()
.данные.стол-это
:=
требует, чтобы имена столбцов были предоставлены, в то время как dplyr генерирует его автоматически.в случае (b) синтаксис dplyr относительно прост. Улучшение агрегации / обновления по нескольким функциям находится на данных.список таблиц.
в случае (c) хотя, dplyr вернется
n()
столько раз, сколько столбцов, а не только один раз. В данных.таблица, все, что нам нужно сделать, это вернуть список вj
. Каждый элемент списка станет столбцом в результате. Таким образом, мы можем использовать, еще раз, знакомую базовую функциюc()
для объединения.N
доlist
который возвращает alist
.Примечание: еще раз, в данные.стол, все, что нам нужно сделать, это вернуть список в
j
. Каждый элемент списка станет столбцом в результате. Вы можете использоватьc()
,as.list()
,lapply()
,list()
etc... базовые функции для достижения этой цели, без необходимости изучать какие-либо новые функции.вам нужно будет узнать только специальные переменные -
.N
и.SD
по крайней мере. Эквивалентом в dplyr являютсяn()
и.
соединения
dplyr предоставляет отдельные функции для каждого типа соединения, где как данные.таблица позволяет соединять с использованием того же синтаксиса
DT[i, j, by]
(и с причина.) Он также предоставляет эквивалент в данных.таблица, которая позволяет очень быстро переупорядочить данные.таблицы по ссылке.dplyr обеспечивает интерфейс к базам данных используя тот же синтаксис, что данные.таблицы нет на данный момент.
data.table
обеспечивает более быстрые эквиваленты set operations от v1.9. 7+ (написано Яном Горецким) -fsetdiff
,fintersect
,funion
иfsetequal
дополнительные
вот моя попытка дать исчерпывающий ответ с точки зрения dplyr, следуя широкому контуру ответа Аруна (но несколько перестроенного на основе различных приоритетов).
синтаксис
есть некоторая субъективность в синтаксисе, но я стою на своем утверждении, что сжатие данных.таблица делает его труднее учиться и труднее читать. Отчасти это связано с тем, что dplyr решает гораздо более простую проблему!
одна очень важная вещь, которую делает dplyr для вас это так и есть ограничения ваши варианты. Я утверждаю, что большинство проблем с одной таблицей могут решается всего с помощью пяти ключевых глаголов фильтр, выберите, мутировать, упорядочить и резюмируйте вместе с наречием" по группе". Это ограничение является большой помощью когда вы изучаете манипуляции с данными, потому что это помогает упорядочить ваши думая о проблеме. В dplyr каждый из этих глаголов сопоставляется с a одна функция. Каждая функция выполняет одну работу, и ее легко понять в изоляции.
вы создайте сложность путем пронзать эти простые деятельности вместе с
%>%
. Вот пример из одного из постов Аруна связаны к: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()
чем элемент до[.data.table
.мне тоже нравится, что трубы не ограничивается только одним пакетом. Вы можете начать с уборки данные с tidyr, и закончите с сюжетом в ggvis. И ты не ограничиваясь пакетами, которые я пишу - любой может написать функцию это образует бесшовную часть данных труба манипуляции. На самом деле, я скорее отдайте предпочтение предыдущим данным.код таблицы переписан с помощью
%>%
:diamonds %>% data.table() %>% .[cut != "Fair", .(AvgPrice = mean(price), MedianPrice = as.numeric(median(price)), Count = .N ), by = cut ] %>% .[order(-Count)]
и идея трубопровода с
%>%
не ограничивается только фреймами данных и легко обобщается на другие контексты:интерактивные веб графика, web выскабливание, логи,выполнить времени контракты, ...)память и производительность
я объединил вместе, потому что для меня, они не так важны. Большинство пользователей R работают с более чем 1 миллионом строк данных, а dplyr-это достаточно быстро для такого размера данных, о котором вы не знаете время обработки. Мы оптимизируем dplyr для выразительности на средних данных; не стесняйтесь использовать данные.таблица для необработанной скорости на больших данных.
гибкость dplyr также означает, что вы можете легко настроить производительность характеристики, использующие тот же синтаксис. Если производительность dplyr с бэкэнд фрейма данных не очень хорош достаточно для вас, вы можете использовать данные.бэкэнд таблицы (хотя и с несколько ограниченным набором функций). Если данные, с которыми вы работаете, не помещаются в память, вы можете использовать бэкэнд базы данных.
все, что сказал, производительность dplyr станет лучше в долгосрочной перспективе. Мы определенно реализовать некоторые из великих идей данных.таблица как представление упорядочение и использование одного и того же индекса для соединений и фильтров. Мы тоже работа над распараллеливанием, чтобы мы могли воспользоваться несколькими сердце.
особенности
несколько вещей, над которыми мы планируем работать в 2015 году:
the
readr
пакета, чтобы сделать его легко получить файлы с диска и в на память, аналогичноfread()
.более гибкие соединения, включая поддержку неэкви-соединений.
более гибкая группировка, такая как образцы bootstrap, rollups и многое другое
я тоже вкладывают время на улучшение R
в прямом ответе на Заголовок Вопрос...
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 лучше, чем данные.стол?