我正在寻找有关我是否有效使用data.table
的建议。
我有一个描述事件的数据集,每个事件占一行。在每一行我都有事件发生的日期。现在我只想计算每年有多少事件。我使用下面的代码完成了这项工作,但感觉效率低下。我很感激有关如何改进这一点的任何建议。 (数据集远远大于下图所示,我还必须做其他类似但更复杂的计数)
创建从2000年到2012年底的日期列表:
dates <- seq(as.Date("1/1/2000", format="%d/%m/%Y"),
as.Date("31/12/2012", format="%d/%m/%Y"),
"day")
# Choose one million occurrences on various dates:
sampleDate <- sample(dates, 1000000, replace=TRUE)
# Create `data.table`, one line per incident:
library(data.table)
DT.dt <- data.table(Date=sampleDate, incident=1)
# Time how long it takes to count the number of indidents in each year:
system.time(result <- DT.dt[,count(format(Date,"%Y"))])
user system elapsed
11.83 0.10 11.95
result[1:3,]
x freq
2000 76930
2001 77101
2002 76666
所以它有效(我认为),但我怀疑有更有效的解决方案......
答案 0 :(得分:3)
当您使用data.tables
,特别是对大型数据集进行聚合操作(分组)时,您应该将要分组的字段设置为key
(使用setkeyv(DT, "your_key_field")
等等......)此外,我无法就该主题发表明确的演讲,但一般来说,我认为使用data.table::
对象中的本地data.table
函数/操作比使用其他软件包时更好。& 39;函数,例如plyr::count
。下面,我制作了一些data.table
个对象 - 第一个与你的例子相同;第二个添加列Year
(而不是在函数执行时计算format(Date,"%Y")
),但将Date
设置为key
;第三个与第二个相同,只是它使用Year
作为key
。我还提出了一些以不同方式进行分组的功能(用于基准测试)。
library(data.table)
library(plyr) # for 'count' function
library(microbenchmark)
##
dates <- seq.Date(
from=as.Date("2000-01-01"),
to=as.Date("2012-12-31"),
by="day")
##
set.seed(123)
sampleDate <- sample(
dates,
1e06,
replace=TRUE)
##
DT.dt <- data.table(
Date=sampleDate,
incident=1)
##
DT.dt2 <- copy(DT.dt)
DT.dt2[,Year:=format(Date,"%Y")]
setkeyv(DT.dt2,"Date")
##
DT.dt3 <- copy(DT.dt2)
setkeyv(DT.dt3,"Year")
##
> head(DT.dt,3)
Date incident
1: 2003-09-27 1
2: 2010-04-01 1
3: 2005-04-26 1
> head(DT.dt2,3)
Date incident Year
1: 2000-01-01 1 2000
2: 2000-01-01 1 2000
3: 2000-01-01 1 2000
> head(DT.dt3,3)
Date incident Year
1: 2000-01-01 1 2000
2: 2000-01-01 1 2000
3: 2000-01-01 1 2000
## your original method
f1 <- function(dt)
{
dt[,count(format(Date,"%Y"))]
}
## your method - using 'Year' column
f1.2 <- function(dt)
{
dt[,count(Year)]
}
## use 'Date' column; '.N' and
## 'by=' instead of 'count'
f2 <- function(dt)
{
dt[,.N,by=format(Date,"%Y")]
}
## use 'Year' and '.N','by='
f3 <- function(dt)
{
dt[,.N,by=Year]
}
##
Res <- microbenchmark(
f1(DT.dt),
f1.2(DT.dt2),
f1.2(DT.dt3),
f2(DT.dt2),
f3(DT.dt3))
##
> Res
Unit: milliseconds
expr min lq median uq max neval
f1(DT.dt) 478.941767 515.144253 557.428159 585.579862 706.8724 100
f1.2(DT.dt2) 98.722062 115.588034 126.332104 137.792116 223.4967 100
f1.2(DT.dt3) 97.475673 118.134788 125.836817 136.136156 238.2697 100
f2(DT.dt2) 352.767219 373.337958 387.759996 429.301164 542.1674 100
f3(DT.dt3) 7.912803 8.441159 8.736887 9.685267 76.9629 100
<强>观察:强>
按预先计算的字段Year
进行分组,而不是计算
执行时format(Date,"%Y")
是一个明显的改进 -
适用于count
和.N
方法。你可以看到这个
将f1()
和f2()
次与f1.2()
次进行比较。
count
方法似乎比.N
&amp; &#39;可以通过=&#39;方法(f1()
与f2()
进行比较。
Year
和原生的data.table
分组.N
&amp; by=
; f3()
比其他四个时间要快得多。 在SO上有一些非常体验的data.table
用户,当然比我自己更多,所以可能有更快的方法来做到这一点。除此之外,在key
上设置data.table
绝对是个好主意。并且看起来你预算计算像Year
这样的字段要好得多,而不是这样做#34;在飞行中&#34 ;;如果您不需要使用DT.dt[,Year:=NULL]
另外,您说您正在尝试计算每年incident
的数量 - 并且因为您的示例数据对所有行都有incident = 1
,所以计数与求和相同。但假设您的实际数据具有不同的incident
值,您可以这样:
> DT.dt3[,list(Incidents=sum(incident)),by=Year]
Year Incidents
1: 2000 77214
2: 2001 77385
3: 2002 77080
4: 2003 76609
5: 2004 77197
6: 2005 76994
7: 2006 76560
8: 2007 76904
9: 2008 76786
10: 2009 76765
11: 2010 76675
12: 2011 76868
13: 2012 76963
(我在上面调用了setkeyv(DT.dt3,cols="Year")
)。