Почему rbindlist "лучше", чем rbind?


Я просматриваю документацию data.table а также заметил из некоторых разговоров здесь на так что rbindlist должен быть лучше, чем rbind.

Я хотел бы знать, почему rbindlist лучше, чем rbind и в каких сценариях rbindlist действительно превосходит rbind?

есть ли какие-либо преимущества с точки зрения использования памяти?

2 111

2 ответа:

rbindlist - это оптимизированная версия do.call(rbind, list(...)), который известен как медленный при использовании rbind.data.frame


где он действительно преуспевает

некоторые вопросы, которые показывают, где rbindlist блеском

быстрое векторизованное слияние списка данных.кадры по строке

проблема преобразования длинного списка данных.кадры (~1 миллион) для отдельных данных.используя рамки делать.звоните и ldply

эти тесты, которые показывают, как быстро это может быть.


rbind.данные.кадр медленный, по какой-то причине

rbind.data.frame делает много проверок, и будет соответствовать по имени. (т. е. rbind.данные.фрейм будет учитывать тот факт, что столбцы могут быть в разных порядках и совпадать по имени),rbindlist не делает такого рода проверки, и присоединится по позиции

например

do.call(rbind, list(data.frame(a = 1:2, b = 2:3), data.frame(b = 1:2, a = 2:3)))
##    a b
## 1  1 2
## 2  2 3
## 3  2 1
## 4  3 2

rbindlist(list(data.frame(a = 1:5, b = 2:6), data.frame(b = 1:5, a = 2:6)))
##     a b
##  1: 1 2
##  2: 2 3
##  3: 1 2
##  4: 2 3

некоторые другие ограничения rbindlist

это используется к борьбу с factors, из-за ошибки, которая с тех пор была исправлена:

rbindlist два данных.таблицы, где один имеет фактор, а другой имеет тип символа для столбца (ошибка #2650)

он имеет проблемы с дублированием имен столбцов

посмотреть предупреждение: в rbindlist (allargs): NAs введено принуждение: возможная ошибка в данных.стол? (ошибка #2384)


rbind.данные.фреймовые имена строк могут расстраивать

rbindlist справлюсь с listsdata.frames и data.tables, и возвращает данные.таблица без имен строк

вы можете попасть в путаницу имен строк с помощью do.call(rbind, list(...)) смотрите

как избежать переименования строк при использовании rbind внутри do.позвонить?


эффективность памяти

С точки зрения реализуется в C, так что память эффективна, он использует setattr для установки атрибутов по ссылке

rbind.data.frame реализуется в R, он делает много назначения, и использует attr<-class<- и rownames<- все (внутренне) создавать копии созданных данных.рамка.

By v1.9.2,rbindlist эволюционировали совсем немного, реализуя многие функции, в том числе:

  • выбор самого высокого SEXPTYPE столбцов при привязке-реализовано в v1.9.2 закрытие FR #2456 и ошибка #4981.
  • обращение factor столбцы правильно-сначала реализовано в v1.8.10 закрытие ошибка #2650 и распространяется на привязку приказал факторы, тщательно в v1.9.2 также, закрытие FR #4856 и ошибка #5019.

кроме того, в v1.9.2,rbind.data.table получила fill аргумент, позволяющий связывать путем заполнения недостающих столбцов, реализован в R.

сейчас v1.9.3, есть еще больше улучшений на существующие особенности:

  • rbindlist получает аргумент use.names, который по умолчанию составляет FALSE для обратной совместимости.
  • rbindlist также получает аргумент fill, который по умолчанию тоже FALSE для обратной совместимости.
  • все эти функции реализованы на C и написаны тщательно, чтобы не идти на компромисс в скорости при добавлении функциональных возможностей.
  • С rbindlist теперь можно сопоставить по именам и заполнить недостающие столбцы,rbind.data.table просто называет rbindlist сейчас. Разница только в том, что use.names=TRUE по умолчанию rbind.data.table, для обратной совместимости.

rbind.data.frame замедляется совсем немного в основном из-за копий (что также указывает @mnel), которых можно было бы избежать (Перейдя на C). Я думаю, что это не единственная причина. Реализация для проверки / сопоставления имен столбцов в rbind.data.frame может также замедлиться, когда есть много столбцов на данные.фрейма и таких данных много.фреймы для привязки (как показано в тесте ниже).

, что rbindlist отсутствие (ed) определенных функций (например, проверка фактора уровни или соответствующие имена) несет очень маленький (или нет) вес к нему быстрее, чем rbind.data.frame. Это потому, что они были тщательно реализованы в C, оптимизированы для скорости и памяти.

вот тест, который подчеркивает эффективную привязку при сопоставлении по именам столбцов, а также с помощью rbindlist ' s use.names функцию v1.9.3. Набор данных состоит из 10000 данных.кадров, каждый размером 10*500.

NB: этот тест был обновлен, чтобы включить сравнение к dplyr ' s bind_rows

library(data.table) # 1.11.5, 2018-06-02 00:09:06 UTC
library(dplyr) # 0.7.5.9000, 2018-06-12 01:41:40 UTC
set.seed(1L)
names = paste0("V", 1:500)
cols = 500L
foo <- function() {
    data = as.data.frame(setDT(lapply(1:cols, function(x) sample(10))))
    setnames(data, sample(names))
}
n = 10e3L
ll = vector("list", n)
for (i in 1:n) {
    .Call("Csetlistelt", ll, i, foo())
}

system.time(ans1 <- rbindlist(ll))
#  user  system elapsed 
# 1.226   0.070   1.296 

system.time(ans2 <- rbindlist(ll, use.names=TRUE))
#  user  system elapsed 
# 2.635   0.129   2.772 

system.time(ans3 <- do.call("rbind", ll))
#   user  system elapsed 
# 36.932   1.628  38.594 

system.time(ans4 <- bind_rows(ll))
#   user  system elapsed 
# 48.754   0.384  49.224 

identical(ans2, setDT(ans3)) 
# [1] TRUE
identical(ans2, setDT(ans4))
# [1] TRUE

привязка столбцов как таковых без проверки имен заняла всего 1,3, где проверка имен столбцов и привязка соответственно заняла всего 1,5 секунды больше. По сравнению с базовым решением, это в 14 раз быстрее, и в 18 раз быстрее, чем dplyr'S версии.