R use ddply or aggregate
我有一个包含3列的数据框:custId,saleDate,DelivDateTime。
1 2 3 4 5 6 7 8 | > 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 |
这是赔率:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | > 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") |
我正在尝试为每个
我可以这样使用plyr :: ddply做到这一点:
1 2 3 | dd1 <-ddply(events22, .(custId),.inform = T, function(x){ x[x$saleDate == max(x$saleDate),"DelivDate"] }) |
我的问题是,是否有更快的方法来完成此操作,因为ddply方法非常耗时(整个数据集约为40万行)。我已经看过使用
有什么建议吗?
编辑:
这是10k行@ 10次迭代的基准结果:
1 2 3 4 5 6 | 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 |
EDIT2:
虽然速度最快,但AGG2()无法给出正确的答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | > 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 |
我也将在这里推荐
1 | merge(events22, aggregate(saleDate ~ custId, events22, max)) |
或者仅
1 2 3 | aggregate(list(DelivDate = events22$saleDate), list(custId = events22$custId), function(x) events22[["DelivDate"]][which.max(x)]) |
最后,这是使用
1 2 3 | library(sqldf) sqldf("select custId, DelivDate, max(saleDate) `saleDate` from events22 group by custId") |
基准测试
我不是基准测试或
1 | library(rbenchmark) |
首先,为要进行基准测试的功能设置功能。?pb>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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), ] }) ) } |
第二,进行基准测试。
1 2 3 4 5 6 7 8 9 | 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 |
在
1 2 3 | require(data.table) dt <- data.table(events22) dt[, .SD[which.max(saleDate),], by=custId] |
来自
每个组的数据,不包括组列。
这应该很快,但是
1 2 3 4 5 | do.call(rbind, lapply(split(events22, events22$custId), function(x){ x[which.max(x$saleDate), ] }) ) |
这是一个更快的
1 2 3 4 5 | DATATABLE <- function() { dt <- data.table(events, key=c('custId', 'saleDate')) dt[, maxrow := 1:.N==.N, by = custId] return(dt[maxrow==TRUE, list(custId, DelivDate)]) } |
请注意,此函数将创建
我还修改了所有先前的函数以返回结果,以便于比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | DDPLY <- function() { return(ddply(events, .(custId), .inform = T, function(x) { x[x$saleDate == max(x$saleDate),"DelivDate"]})) } AGG1 <- function() { return(merge(events, aggregate(saleDate ~ custId, events, max)))} SQLDF <- function() { return(sqldf("select custId, DelivDate, max(saleDate) `saleDate` from events group by custId"))} DOCALL <- function() { return(do.call(rbind, lapply(split(events, events$custId), function(x){ x[which.max(x$saleDate), ] }) )) } |
这是1万行的结果,重复10次:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | library(rbenchmark) library(plyr) library(data.table) library(sqldf) events <- do.call(rbind, lapply(1:500, function(x) events22)) events$custId <- sample(1:nrow(events), nrow(events)) benchmark(a <- DDPLY(), b <- DATATABLE(), c <- AGG1(), d <- SQLDF(), e <- DOCALL(), order ="elapsed", replications=10)[1:5] test replications elapsed relative user.self 2 b <- DATATABLE() 10 0.13 1.000 0.13 4 d <- SQLDF() 10 0.42 3.231 0.41 3 c <- AGG1() 10 12.11 93.154 12.03 1 a <- DDPLY() 10 32.17 247.462 32.01 5 e <- DOCALL() 10 56.05 431.154 55.85 |
由于所有函数均返回其结果,因此我们可以验证它们是否均返回相同的答案:
1 2 3 4 5 6 | c <- c[order(c$custId),] dim(a); dim(b); dim(c); dim(d); dim(e) all(a$V1==b$DelivDate) all(a$V1==c$DelivDate) all(a$V1==d$DelivDate) all(a$V1==e$DelivDate) |
/ Edit:在较小的20行数据集上,
1 2 3 4 5 6 | test replications elapsed relative user.self 2 b <- DATATABLE() 100 0.22 1.000 0.22 3 c <- AGG1() 100 0.42 1.909 0.42 5 e <- DOCALL() 100 0.48 2.182 0.49 1 a <- DDPLY() 100 0.55 2.500 0.55 4 d <- SQLDF() 100 1.00 4.545 0.98 |
/ Edit2:如果从函数中删除
1 2 3 4 5 6 7 8 9 10 11 12 13 | dt <- data.table(events, key=c('custId', 'saleDate')) DATATABLE2 <- function() { dt[, maxrow := 1:.N==.N, by = custId] return(dt[maxrow==TRUE, list(custId, DelivDate)]) } benchmark(a <- DDPLY(), b <- DATATABLE2(), c <- AGG1(), d <- SQLDF(), e <- DOCALL(), order ="elapsed", replications=10)[1:5] test replications elapsed relative user.self 2 b <- DATATABLE() 10 0.09 1.000 0.08 4 d <- SQLDF() 10 0.41 4.556 0.39 3 c <- AGG1() 10 11.73 130.333 11.67 1 a <- DDPLY() 10 31.59 351.000 31.50 5 e <- DOCALL() 10 55.05 611.667 54.91 |