эффективные последовательные операции на data.table
Мне нужно найти поровну минимум (+60)
относительно большой data.frame
(~ 250 000 x 3) (или я могу эквивалентно работать на xts
).
set.seed(1000) my.df <- sample(1:5, 250000*3, replace=TRUE) dim(my.df) <- c(250000,3) my.df <- as.data.frame(my.df) names(my.df) <- c("A", "B", "C")
Кадр данных my.df
выглядит так:
> head(my.df) ABC 1 2 5 2 2 4 5 5 3 1 5 3 4 4 4 3 5 3 5 5 6 1 5 3
Я пытался
- Многострочные методы обхода комментариев?
- Объедините два кадра данных по строкам (rbind), когда они имеют разные наборы столбцов
- Нарисуйте круг с ggplot2
- Как выровнять несколько графиков ggplot2 и добавить тени поверх них
- dplyr - mutate: использовать имена динамических переменных
require(data.table) my.dt <- as.data.table(my.df) my.dt[, row.min:=0] # without this: "Attempt to add new column(s) and set subset of rows at the same time" system.time( for (i in 1:dim(my.dt)[1]) my.dt[i, row.min:= min(A, B, C)] )
В моей системе это занимает ~ 400 секунд. Он работает, но я не уверен, что это лучший способ использовать data.table
. Я правильно использую data.table
? Есть ли более эффективный способ выполнения простых действий по строке?
- readRDS (файл) в R
- Подсчитайте слова в R
- ggplot2 0.9.0 автоматически отбрасывает неиспользованные уровни факторов из легенды сюжета?
- Memory Allocation "Ошибка: не может выделить вектор размера 75.1 Mb"
- Отображение времени в командной строке R
- Как получить метки данных для гистограммы в ggplot2?
- Осевая ось X в R
- Rstudio rmarkdown: как портретный, так и альбомный макет в одном PDF-файле
Или просто pmin
.
my.dt <- as.data.table(my.df) system.time(my.dt[,row.min:=pmin(A,B,C)]) user system elapsed 0.02 0.00 0.01 head(my.dt) ABC row.min [1,] 2 5 2 2 [2,] 4 5 5 4 [3,] 1 5 3 1 [4,] 4 4 3 3 [5,] 3 5 5 3 [6,] 1 5 3 1
Классический способ выполнения строковых операций в R заключается в применении:
apply(my.df, 1, min) > head(my.df) ABC min 1 2 5 4 2 2 4 3 1 1 3 1 1 5 1 4 4 1 5 1 5 3 3 4 3 6 1 1 1 1
На моей машине эта операция занимает около 0,25 секунды.
После некоторого обсуждения рядовых первых / последних входов из столбцов в таблице data.table , в которой предположилось, что сначала плавление будет быстрее, чем расчет по ряду строк, я решил сравнить:
-
pmin
(ответ Мэтта Доулла выше), ниже как tm1 -
apply
(ответ Андри выше), ниже как tm2 - сначала плавление, затем по группам, ниже, как tm3
так:
library(microbenchmark); library(data.table) set.seed(1000) b <- data.table(m=integer(), n=integer(), tm1 = numeric(), tm2 = numeric(), tm3 = numeric()) for (m in c(2.5,100)*1e5){ for (n in c(3,50)){ my.df <- sample(1:5, m*n, replace=TRUE) dim(my.df) <- c(m,n) my.df <- as.data.frame(my.df) names(my.df) <- c(LETTERS,letters)[1:n] my.dt <- as.data.table(my.df) tm1 <- mean(microbenchmark(my.dt[, foo := do.call(pmin, .SD)], times=30L)$time)/1e6 my.dt <- as.data.table(my.df) tm2 <- mean(microbenchmark(apply(my.dt, 1, min), times=30L)$time)/1e6 my.dt <- as.data.table(my.df)sv tm3 <- mean(microbenchmark( melt(my.dt[, id:=1:nrow(my.dt)], id.vars='id')[, min(value), by=id], times=30L )$time)/1e6 b <- rbind(b, data.table(m, n, tm1, tm2, tm3) ) } }
(У меня не хватило времени, чтобы попробовать больше комбинаций) дает нам:
b # mn tm1 tm2 tm3 # 1: 2.5e+05 3 16.20598 1000.345 39.36171 # 2: 2.5e+05 50 166.60470 1452.239 588.49519 # 3: 1.0e+07 3 662.60692 31122.386 1668.83134 # 4: 1.0e+07 50 6594.63368 50915.079 17098.96169 c <- melt(b, id.vars=c('m','n')) library(ggplot2) ggplot(c, aes(x=m, linetype=as.factor(n), col=variable, y=value)) + geom_line() + ylab('Runtime (millisec)') + xlab('# of rows') + guides(linetype=guide_legend(title='Number of columns'))
Хотя я знал, что apply
(tm2) будет плохо масштабироваться, я удивляюсь, что pmin (tm1) настолько хорошо масштабируется, если R не предназначен для разумных операций. Я не мог идентифицировать случай, когда pmin
не следует использовать для расплава-по-группе (tm3).