【问题标题】:Efficient way to group data.table results by year按年份对 data.table 结果进行分组的有效方法
【发布时间】:2014-09-19 14:40:00
【问题描述】:

我正在寻求有关我是否有效地使用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

所以它有效(我认为),但我怀疑有一个更有效的解决方案......

【问题讨论】:

  • 不确定函数count 来自哪里。 data.table 的方式是:DT.dt[, .N, by=format(Date, "%Y")]。而且我感觉这个 Q 可能是重复的。

标签: r data.table


【解决方案1】:

当您使用 data.tables 进行聚合操作(分组)时,尤其是对于大型数据集,您应该将分组依据的字段设置为 key(使用 setkeyv(DT, "your_key_field"), ETC...)。另外,我不能明确地谈论这个话题,但总的来说,我认为在 data.table 对象中使用本机 data.table:: 函数/操作会比使用其他包的函数(如 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

观察:

  1. 按预先计算的字段Year分组,而不是计算 format(Date,"%Y") 在执行时是一个明显的改进 - 对于count.N 方法。你可以看到这个 将f1()f2() 时间与f1.2() 时间进行比较。

  2. count 方法似乎比 .N 和 'by=' 方法慢一些(f1()f2() 相比。

  3. 迄今为止最好的方法是使用预先计算的字段Year和本机data.table分组.Nby=; f3() 比其他四个时间快得多。

在 SO 上有一些非常有经验的 data.table 用户,肯定比我自己更多,所以可能有更快的方法来做到这一点。不过,抛开其他一切不谈,在data.table 上设置key 绝对是个好主意;而且看起来你预先计算像Year这样的字段比“即时”这样做要好得多;如果您不需要它,您可以随时使用 DT.dt[,Year:=NULL] 删除它。

此外,您说您正在尝试计算每年 incidents 的数量 - 由于您的示例数据的所有行都有 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"))。

【讨论】:

  • 小记。聚合操作无需设置键。我们更喜欢 adhoc by。你的基准测试应该包括setkey的时间。
  • 我知道发帖规则说我不能说谢谢。但我没有声誉,所以我什至不能投票给你的答案。但这是惊人的。谢谢!
猜你喜欢
  • 1970-01-01
  • 2020-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多