6

我在 R 中做经典的拆分应用重组事情。随着时间的推移,我的数据集是一堆公司。我正在做的应用是对每个公司进行回归并返回残差,因此,我没有按公司汇总。 plyr很好,但是当公司数量很大时,它需要很长时间才能运行。有没有办法做到这一点data.table

样本数据:

dte, id, val1, val2
2001-10-02, 1, 10, 25
2001-10-03, 1, 11, 24
2001-10-04, 1, 12, 23
2001-10-02, 2, 13, 22
2001-10-03, 2, 14, 21

我需要按每个 id (即 1 和 2)拆分。运行回归,返回残差并将其作为列附加到我的数据中。有没有办法做到这一点data.table

4

2 回答 2

8

DWin 的答案对于 v1.8.0 是正确的(目前在 CRAN 上)。但在 v1.8.1(在 R-Forge 存储库上)中,:=现在按组工作。它也适用于非连续组,因此无需setkey先排队。

dtb <- as.data.table(dat)
dtb
           dte id val1 val2
1:  2001-10-02  1   10   25
2:  2001-10-03  1   11   24
3:  2001-10-04  1   12   23
4:  2001-10-02  2   13   22
5:  2001-10-03  2   14   21
dtb[, resid:=residuals(lm(val1 ~ val2)), by=id]
           dte id val1 val2         resid
1:  2001-10-02  1   10   25  1.631688e-15
2:  2001-10-03  1   11   24 -3.263376e-15
3:  2001-10-04  1   12   23  1.631688e-15
4:  2001-10-02  2   13   22  0.000000e+00
5:  2001-10-03  2   14   21  0.000000e+00

要升级到 v1.8.1,只需从 R-Forge 存储库安装。(从 R-Forge 安装任何二进制包时需要 R 2.15.0+):

install.packages("data.table", repos="http://R-Forge.R-project.org")

或者如果您无法升级到最新的 R,则从源代码安装。 data.table它本身只需要 R 2.12.0+。

扩展到 1MM 外壳:

DT = data.table(dte=Sys.Date()+1:1000000, 
                id=sample(1:2, 1000000, repl=TRUE),
                val1=runif(1000000),  val2=runif(1000000) )
setkey(DT, id)
system.time(ans1 <- cbind(DT, DT[, residuals(lm(val1 ~ val2)), by="id"]) )
   user  system elapsed 
 12.272   0.872  13.182 
ans1
                dte id      val1       val2 id           V1
      1: 2012-07-02  1 0.8369147 0.57553383  1  0.336647598
      2: 2012-07-05  1 0.0109102 0.02532214  1 -0.488633325
      3: 2012-07-06  1 0.4977762 0.16607786  1 -0.001952414
     ---                                                   
 999998: 4750-05-27  2 0.1296722 0.62645838  2 -0.370627034
 999999: 4750-05-28  2 0.2686352 0.04890710  2 -0.231952238
1000000: 4750-05-29  2 0.9981029 0.91626787  2  0.497948275

system.time(DT[, resid:=residuals(lm(val1 ~ val2)), by=id])
   user  system elapsed 
  7.436   0.648   8.107 
DT
                dte id      val1       val2        resid
      1: 2012-07-02  1 0.8369147 0.57553383  0.336647598
      2: 2012-07-05  1 0.0109102 0.02532214 -0.488633325
      3: 2012-07-06  1 0.4977762 0.16607786 -0.001952414
     ---                                                
 999998: 4750-05-27  2 0.1296722 0.62645838 -0.370627034
 999999: 4750-05-28  2 0.2686352 0.04890710 -0.231952238
1000000: 4750-05-29  2 0.9981029 0.91626787  0.497948275

上面的示例只有 2 个组,非常小,不到 40MB,Rprof显示 96% 的时间都花在lm. 所以在这些情况下:=,分组并不是为了速度优势,而是为了方便;即,需要编写的代码更少,并且没有多余的列添加到输出中。随着大小的增长,避免复制开始出现,速度优势开始显现。特别是,随着组数的增加,transforminj会非常慢。

于 2012-07-01T17:06:15.183 回答
6

我猜这需要按“id”排序才能正确排列。幸运的是,当您设置密钥时会自动发生这种情况:

dat <-read.table(text="dte, id, val1, val2
 2001-10-02, 1, 10, 25
 2001-10-03, 1, 11, 24
 2001-10-04, 1, 12, 23
 2001-10-02, 2, 13, 22
 2001-10-03, 2, 14, 21
 ", header=TRUE, sep=",")
 dtb <- data.table(dat)
 setkey(dtb, "id")
 dtb[, residuals(lm(val1 ~ val2)), by="id"]
#---------------
cbind(dtb, dtb[, residuals(lm(val1 ~ val2)), by="id"])
#---------------
            dte id val1 val2 id.1            V1
[1,] 2001-10-02  1   10   25    1  1.631688e-15
[2,] 2001-10-03  1   11   24    1 -3.263376e-15
[3,] 2001-10-04  1   12   23    1  1.631688e-15
[4,] 2001-10-02  2   13   22    2  0.000000e+00
[5,] 2001-10-03  2   14   21    2  0.000000e+00



> dat <- data.frame(dte=Sys.Date()+1:1000000, 
                    id=sample(1:2, 1000000, repl=TRUE),  
                    val1=runif(1000000),  val2=runif(1000000) )
> dtb <- data.table(dat)
> setkey(dtb, "id")
> system.time(  cbind(dtb, dtb[, residuals(lm(val1 ~ val2)), by="id"]) )
   user  system elapsed 
  1.696   0.798   2.466 
> system.time( dtb[,transform(.SD,r = residuals(lm(val1~val2))),by = "id"] )
   user  system elapsed 
  1.757   0.908   2.690 

来自 Matthew 的编辑:这对于 CRAN 上的 v1.8.0 都是正确的。在data.table wiki第 2 点的主题中transform添加了一点点:“为了速度,不要按组,然后”。但是,现在在 v1.8.1 中按组工作,既简单又快速。请参阅我的答案以获取说明(但无需投票)。jtransform()cbind():=

嗯,我投了赞成票。这是在 Mac 上安装 v 1.8.1 的控制台命令(如果您有适当的 XCode 工具可用,因为它只存在于源代码中):

install.packages("data.table", repos= "http://R-Forge.R-project.org", type="source", 
               lib="/Library/Frameworks/R.framework/Versions/2.14/Resources/lib")

(由于某种原因,我无法让 Mac GUI 包安装程序将 r-forge 读取为存储库。)

于 2012-07-01T03:32:11.873 回答