R使用ddply或聚合

scr*_*Owl 11 aggregate r plyr

我有一个包含3列的数据框:custId,saleDate,DelivDateTime.

> head(events22)
     custId            saleDate      DelivDate
1 280356593 2012-11-14 14:04:59 11/14/12 17:29
2 280367076 2012-11-14 17:04:44 11/14/12 20:48
3 280380097 2012-11-14 17:38:34 11/14/12 20:45
4 280380095 2012-11-14 20:45:44 11/14/12 23:59
5 280380095 2012-11-14 20:31:39 11/14/12 23:49
6 280380095 2012-11-14 19:58:32 11/15/12 00:10
Run Code Online (Sandbox Code Playgroud)

这是输入:

> dput(events22)
structure(list(custId = c(280356593L, 280367076L, 280380097L, 
280380095L, 280380095L, 280380095L, 280364279L, 280364279L, 280398506L, 
280336395L, 280364376L, 280368458L, 280368458L, 280368456L, 280368456L, 
280364225L, 280391721L, 280353458L, 280387607L, 280387607L), 
    saleDate = structure(c(1352901899.215, 1352912684.484, 1352914714.971, 
    1352925944.429, 1352925099.247, 1352923112.636, 1352922476.55, 
    1352920666.968, 1352915226.534, 1352911135.077, 1352921349.592, 
    1352911494.975, 1352910529.86, 1352924755.295, 1352907511.476, 
    1352920108.577, 1352906160.883, 1352905925.134, 1352916810.309, 
    1352916025.673), class = c("POSIXct", "POSIXt"), tzone = "UTC"), 
    DelivDate = c("11/14/12 17:29", "11/14/12 20:48", "11/14/12 20:45", 
    "11/14/12 23:59", "11/14/12 23:49", "11/15/12 00:10", "11/14/12 23:35", 
    "11/14/12 22:59", "11/14/12 20:53", "11/14/12 19:52", "11/14/12 23:01", 
    "11/14/12 19:47", "11/14/12 19:42", "11/14/12 23:31", "11/14/12 23:33", 
    "11/14/12 22:45", "11/14/12 18:11", "11/14/12 18:12", "11/14/12 19:17", 
    "11/14/12 19:19")), .Names = c("custId", "saleDate", "DelivDate"
), row.names = c("1", "2", "3", "4", "5", "6", "7", "8", "9", 
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"
), class = "data.frame")
Run Code Online (Sandbox Code Playgroud)

我正试图为每个人找到DelivDate最新saleDatecustId.

我可以像这样使用plyr :: ddply来做到这一点:

dd1 <-ddply(events22, .(custId),.inform = T, function(x){
x[x$saleDate == max(x$saleDate),"DelivDate"]
})
Run Code Online (Sandbox Code Playgroud)

我的问题是,是否有更快的方法来执行此操作,因为ddply方法有点耗时(完整数据集约为400k行).我已经看过使用aggregate()但不知道如何获得除我正在排序的值之外的值.

有什么建议?

编辑:

以下是10k行@ 10次迭代的基准测试结果:

      test replications elapsed relative user.self
2   AGG2()           10    5.96    1.000      5.93
1   AGG1()           10   20.87    3.502     20.75
5 DATATABLE()        10   61.32        1     60.31
3  DDPLY()           10   80.04   13.430     79.63
4 DOCALL()           10   90.43   15.173     88.39
Run Code Online (Sandbox Code Playgroud)

编辑2:虽然最快,AGG2()没有给出正确的答案.

    > head(agg2)
     custId            saleDate      DelivDate
1 280336395 2012-11-14 16:38:55 11/14/12 19:52
2 280353458 2012-11-14 15:12:05 11/14/12 18:12
3 280356593 2012-11-14 14:04:59 11/14/12 17:29
4 280364225 2012-11-14 19:08:28 11/14/12 22:45
5 280364279 2012-11-14 19:47:56 11/14/12 23:35
6 280364376 2012-11-14 19:29:09 11/14/12 23:01
> agg2 <- AGG2()
> head(agg2)
     custId      DelivDate
1 280336395 11/14/12 17:29
2 280353458 11/14/12 17:29
3 280356593 11/14/12 17:29
4 280364225 11/14/12 17:29
5 280364279 11/14/12 17:29
6 280364376 11/14/12 17:29
> agg2 <- DDPLY()
> head(agg2)
     custId             V1
1 280336395 11/14/12 19:52
2 280353458 11/14/12 18:12
3 280356593 11/14/12 17:29
4 280364225 11/14/12 22:45
5 280364279 11/14/12 23:35
6 280364376 11/14/12 23:01
Run Code Online (Sandbox Code Playgroud)

A5C*_*2T1 10

我也会data.table在这里推荐,但是既然你要求一个aggregate解决方案,这里有一个结合aggregatemerge获得所有列的解决方案:

merge(events22, aggregate(saleDate ~ custId, events22, max))
Run Code Online (Sandbox Code Playgroud)

或者只是aggregate如果您只想要"custId"和"DelivDate"列:

aggregate(list(DelivDate = events22$saleDate), 
          list(custId = events22$custId),
          function(x) events22[["DelivDate"]][which.max(x)])
Run Code Online (Sandbox Code Playgroud)

最后,这是一个使用选项sqldf:

library(sqldf)
sqldf("select custId, DelivDate, max(saleDate) `saleDate` 
      from events22 group by custId")
Run Code Online (Sandbox Code Playgroud)

基准

我不是基准测试或data.table专家,但令我感到惊讶的data.table是,这里的速度并不快.我怀疑在较大的数据集上结果会有很大不同,比如你的400k行数据.无论如何,这里有一些基准测试代码模仿@ mnel的答案,所以你可以对你的实际数据集做一些测试,以备将来参考.

library(rbenchmark)
Run Code Online (Sandbox Code Playgroud)

首先,根据您想要的基准设置功能.

DDPLY <- function() { 
  x <- ddply(events22, .(custId), .inform = T, 
             function(x) {
               x[x$saleDate == max(x$saleDate),"DelivDate"]}) 
}
DATATABLE <- function() { x <- dt[, .SD[which.max(saleDate), ], by = custId] }
AGG1 <- function() { 
  x <- merge(events22, aggregate(saleDate ~ custId, events22, max)) }
AGG2 <- function() { 
  x <- aggregate(list(DelivDate = events22$saleDate), 
                 list(custId = events22$custId),
                 function(x) events22[["DelivDate"]][which.max(x)]) }
SQLDF <- function() { 
  x <- sqldf("select custId, DelivDate, max(saleDate) `saleDate` 
             from events22 group by custId") }
DOCALL <- function() {
  do.call(rbind, 
          lapply(split(events22, events22$custId), function(x){
            x[which.max(x$saleDate), ]
          })
  )
}
Run Code Online (Sandbox Code Playgroud)

第二,做基准测试.

benchmark(DDPLY(), DATATABLE(), AGG1(), AGG2(), SQLDF(), DOCALL(), 
          order = "elapsed")[1:5]
#          test replications elapsed relative user.self
# 4      AGG2()          100   0.285    1.000     0.284
# 3      AGG1()          100   0.891    3.126     0.896
# 6    DOCALL()          100   1.202    4.218     1.204
# 2 DATATABLE()          100   1.251    4.389     1.248
# 1     DDPLY()          100   1.254    4.400     1.252
# 5     SQLDF()          100   2.109    7.400     2.108
Run Code Online (Sandbox Code Playgroud)


Aru*_*run 7

我认为最快ddply和之间的距离最快,特别是在您拥有的大量数据上.但是,最快的将是.aggregateaggregatedata.table

require(data.table)
dt <- data.table(events22)
dt[, .SD[which.max(saleDate),], by=custId]
Run Code Online (Sandbox Code Playgroud)

From ?data.table:.SDdata.table包含每个组的x的数据子集,不包括组列.