После некоторого обсуждения около первого / последнего вхождения по строкам из ряда столбцов в data.table , в котором предполагалось, что плавление вначале будет быстрее, чем вычисление по ряду, я решил провести эталонный тест:
pmin
(ответ Мэтта Доула выше), ниже как tm1
apply
(ответ Андри выше), ниже как tm2
- сначала плавится, затем мин по группам, ниже как тм3
так:
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
# m n 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'))
![enter image description here](https://i.stack.imgur.com/ECrjw.png)
Хотя я знал, что apply
(tm2) будет плохо масштабироваться, я удивляюсь, что pmin
(tm1) так хорошо масштабируется, если R на самом деле не предназначен для операций со строками. Я не смог определить случай, когда pmin
не следует использовать по минутам расплавления по группам (tm3).