данные.таблица против dplyr: может ли один делать что-то хорошо, а другой не может или делает плохо?

обзор

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

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

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

вопрос

что я хочу знать, это:

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

один последние, так вопрос заставило меня задуматься об этом немного больше, потому что до этого момента я не думал, что 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 в ответе на вопрос @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))

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] операции типа.

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

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

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

  1. операции filter() или slice() в dplyr может быть неэффективной памятью (на обоих данных.кадры и данные.таблицы.) этот пост.

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

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

    # 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)-назначение по ссылка.

  3. агрегат при присоединении:

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

    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+выполняется за один раз.

    посмотреть этой, этой и этой сообщения для реальных сценариев использования.

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

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

    считать данные.код таблицы, показанный ниже:

    DT1[DT2, col := i.mul]
    

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

    Регистрация этот пост для реального сценария использования.

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

3. Синтаксис

Давайте теперь посмотрим на синтаксис. - Прокомментировал Хэдли.--306-->здесь:

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

последовательность в синтаксисе. Мы сравним данные.таблица и синтаксис 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])
    
    • данные.синтаксис таблицы компактен и dplyr довольно многословен. Вещи более или менее эквивалентны в случае (а).

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

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

    • аналогично в случае (c), когда мы имеем if-else условие, мы можем выразить логику "в" в обоих данных.стол и dplyr. Однако, если мы хотим вернуть только те строки, где if условие удовлетворяет и пропускает в противном случае, мы не можем использовать summarise() напрямую (AFAICT). Мы должны!--23--> сначала, а затем суммировать, потому что 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))
    
    • в случае (a) коды более или менее эквивалентны. данные.таблица использует знакомую базовую функцию lapply(), а dplyr вводит *_each() вместе с кучей функций funs().

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

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

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

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

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

  3. соединения

    dplyr предоставляет отдельные функции для каждого типа соединения, где как данные.таблица позволяет соединениям использовать тот же синтаксис 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() который похож на базу R.

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

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

    • данные.таблицы могут агрегат при присоединении (3), а также обновление при вступлении (4), используя by = .EACHI характеристика. Почему materialse весь результат соединения, чтобы добавить/обновить только несколько колонны?

    • данные.таблица способна подвижного соединения (5) - roll вперед, LOCF, откат назад, NOCB, ближайший.

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

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

    • data.table обеспечивает более быстрые эквиваленты набор операций из v1.9.7+ (автор Ян Горецкий) -fsetdiff, fintersect, funion и fsetequal дополнительные


вот моя попытка всеобъемлющего ответа с точки зрения 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() чем the до [.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 для выразительности на средних данных; не стесняйтесь использовать данные.таблица для скорости raw на больших данных.

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

все, что сказал, производительность dplyr станет лучше в долгосрочной перспективе. Мы определенно реализовать некоторые из великих идей данных.таблица как представление упорядочение и использование одного и того же индекса для joins & filters. Мы также работа на parallelisation поэтому мы можем воспользоваться несколькими сердце.

особенности

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

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

  • более гибкие соединения, включая поддержку для non-equi-соединений.

  • более гибкая группировка, как bootstrap образцы, rollups и многое другое

я также инвестирую время на улучшение R


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

dplyr наверняка делает вещи, которые data.table не может.

Ваш пункт #3

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

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

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

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

dplyr можем сделать вещи!--1--> не будет или не может сделать так же.

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


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

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

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

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

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

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

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

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