我正在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
实现这一点?
发布于 2012-07-01 03:32:12
我猜这需要按"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是完全正确的。除了j
中的transform
是data.table wiki第2点的主题:“为了速度,不要按组执行transform()
,之后执行cbind()
”。但是,在v1.8.1中,:=
现在是按组工作的,并且既简单又快速。请看我的答案来说明(但不需要投票)。
好吧,我投了赞成票。下面是在Mac上安装v1.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包安装程序将r-forge读取为存储库。)
发布于 2012-07-01 17:06:15
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- 2.15.0+安装任何二进制包时都需要R Forge):
install.packages("data.table", repos="http://R-Forge.R-project.org")
如果你不能升级到最新的R,也可以从源代码安装。data.table
本身只需要R 2.12.0+。
扩展到1 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
上面的示例只有两个组,非常小,不到40MB,Rprof
显示96%的时间花在lm
上。因此,在这些情况下,:=
by group实际上不是为了速度优势,而是为了方便;即,需要编写的代码更少,并且没有多余的列添加到输出中。随着大小的增长,避免了副本的出现,速度优势开始显现。特别是,随着组数量的增加,j
中的transform
会变得非常慢。
https://stackoverflow.com/questions/11279304
复制