Оцените, какой размер data.table быстрее, чем data.frame

Может кто-нибудь, пожалуйста, помогите мне оценить, какой размер фрейма данных с использованием data.table быстрее для поиска? В моем случае использование кадров данных будет 24000 строк и 560 000 строк. Блоки из 40 рядов всегда выделяются для дальнейшего использования.

Пример: DF - это кадр данных с 120 строками, 7 столбцами (от x1 до x7); "строка" занимает первые 40 строк x1.

DF2 - 1000 раз DF = > 120 000 строк

Для размера DF data.table медленнее, для размера DF2 он быстрее.

Код:

> DT <- data.table(DF)
> setkey(DT, x1)
> 
> DT2 <- data.table(DF2)
> setkey(DT2, x1)
> 
> microbenchmark(DF[DF$x1=="string", ], unit="us")
Unit: microseconds
                    expr     min       lq   median       uq     max neval
 DF[DF$x1 == "string", ] 282.578 290.8895 297.0005 304.5785 2394.09   100
> microbenchmark(DT[.("string")], unit="us")
Unit: microseconds
            expr      min       lq  median      uq      max neval
 DT[.("string")] 1473.512 1500.889 1536.09 1709.89 6727.113   100
> 
> 
> microbenchmark(DF2[DF2$x1=="string", ], unit="us")
Unit: microseconds
                      expr     min       lq   median       uq      max neval
 DF2[DF2$x1 == "string", ] 31090.4 34694.74 35537.58 36567.18 61230.41   100
> microbenchmark(DT2[.("string")], unit="us")
Unit: microseconds
             expr      min       lq   median       uq      max neval
 DT2[.("string")] 1327.334 1350.801 1391.134 1457.378 8440.668   100

Ответ 1

library(microbenchmark)
library(data.table)
timings <- sapply(1:10, function(n) {
  DF <- data.frame(id=rep(as.character(seq_len(2^n)), each=40), val=rnorm(40*2^n), stringsAsFactors=FALSE)
  DT <- data.table(DF, key="id")     
  tofind <- unique(DF$id)[n-1]
  print(microbenchmark( DF[DF$id==tofind,],
                        DT[DT$id==tofind,],
                        DT[id==tofind],
                        `[.data.frame`(DT,DT$id==tofind,),
                        DT[tofind]), unit="ns")$median
})

matplot(1:10, log10(t(timings)), type="l", xlab="log2(n)", ylab="log10(median (ns))", lty=1)
legend("topleft", legend=c("DF[DF$id == tofind, ]",
                           "DT[DT$id == tofind, ]",
                           "DT[id == tofind]",
                           "`[.data.frame`(DT,DT$id==tofind,)",
                           "DT[tofind]"),
       col=1:5, lty=1)

enter image description here

Январь 2016: обновление до data.table_1.9.7

data.table сделал несколько обновлений с тех пор, как это было написано (добавлено немного дополнительных надбавок к [.data.table, поскольку были добавлены еще несколько аргументов/проверки надежности, а также введение автоматической индексации). Здесь обновленная версия от версии от 13 января 2016 года версии 1.9.7 от GitHub:

jan_2016

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

(примечания об обновленном сюжете: некоторые незначительные вещи (отмена регистрации оси Y, выражение в микросекундах, изменение меток оси x, добавление заголовка), но одна нетривиальная вещь: я обновил microbenchmark, чтобы добавить некоторую стабильность в оценках, а именно, я установил аргумент times в as.integer(1e5/2^n))