data.table上有效的逐行操作

Ryo*_*ogi 29 r 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")
Run Code Online (Sandbox Code Playgroud)

数据框my.df看起来像这样

> head(my.df)

  A B C
1 2 5 2
2 4 5 5
3 1 5 3
4 4 4 3
5 3 5 5
6 1 5 3
Run Code Online (Sandbox Code Playgroud)

我试过了

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)]
)
Run Code Online (Sandbox Code Playgroud)

在我的系统上,这需要约400秒.它有效,但我不相信它是最好的使用方式data.table.我使用data.table得当吗?有没有更有效的方法来进行简单的行式操作?

Mat*_*wle 44

或者,只是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)
#      A B C 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
Run Code Online (Sandbox Code Playgroud)

  • @gkaupas那会是一个很好的问题.野性未经测试的刺:`DT [,do.call(pmin,.SD),. SDcols = x]`或者也许是`do.call(pmin,lapply(x,get))`.使用`.SDcols`的第一种方式应该更有效.您不必分组使用`.SD`.如果需要,也可以添加`by`子句,而不更改`j`. (7认同)
  • 很抱歉,如果这是劫持,但如果我动态创建列列表,例如任何POSIXct类型的列,或其名称与表达式匹配,给我一个变量,如:x = c("A","B ","C")...我如何在pmin表达式中使用"x"?它使用x的文字值而不是将它们限定为列中的值. (3认同)
  • 谢谢马修.第一个工作,我需要添加na.rm = TRUE,这需要一些试验和错误,但我最终得到了'DT < - DT [,NEWCOLNAME:= do.call(pmin,c( .SD,na.rm = TRUE)),. SDcols = x]`.我想知道`.SDcols`在哪里被记录并在[新闻](https://r-forge.r-project.org/scm/viewvc.php/pkg/NEWS?view=markup&root=datatable)中找到了发布1.6.3,但没有任何help()主题. (3认同)

And*_*rie 21

在R中执行行式操作的经典方法是使用apply:

apply(my.df, 1, min)
> head(my.df)
  A B C 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
Run Code Online (Sandbox Code Playgroud)

在我的机器上,此操作大约需要0.25秒.

  • 提起下:"首先尝试简单的东西". (6认同)
  • 这将 my.df 隐式转换为矩阵......通常不是一个好主意。 (3认同)

C8H*_*4O2 15

对data.table中的列系列的行第一次/最后一次出现进行一些讨论之后,这表明首先熔化比行计算更快,我决定进行基准测试:

  • pmin(Matt Dowle上面的答案),低于tm1
  • apply(Andrie的答案在上面),下面是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) ) 
  }
}
Run Code Online (Sandbox Code Playgroud)

(我没时间尝试更多组合)给了我们:

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'))
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

虽然我知道apply(tm2)会缩放得很差,但我很惊讶pmin(如果R不是真正设计用于行式操作的话,(tm1)可以很好地扩展.我不能确定一个pmin不应该用于熔化最小组(tm3)的情况.