繁体   English   中英

使用dplyr进行逐行操作

[英]rowwise operation with dplyr

我正在研究2,3万条记录的R中的大型数据框,其中包含具有开始和停止时间的位置的用户交易。 我的目标是创建一个新的数据框,其中包含每个用户/每个位置连接的时间量。 我们称这是每小时连接一次。

交易可以从8分钟到48小时不等,因此目标数据框将是大约1亿条记录,并且每个月都会增长。

下面的代码显示了最终数据框的开发方式,尽管总代码更复杂。 在英特尔(R)Xeon(R)CPU E5-2630 v3 @ 2.40GHz,16核128GB RAM上运行总代码大约需要9个小时。

library(dplyr)

numsessions<-1000000
startdate <-as.POSIXlt(runif(numsessions,1,365*60*60)*24,origin="2015-1-1")

df.Sessions<-data.frame(userID = round(runif(numsessions,1,500)),
           postalcode = round(runif(numsessions,1,100)),
           daynr = format(startdate,"%w"),
              start =startdate ,
              end=   startdate + runif(1,1,60*60*10)
           )


dfhourly.connected <-df.Sessions %>% rowwise %>% do(data.frame(userID=.$userID,
                                          hourlydate=as.Date(seq(.$start,.$end,by=60*60)),
                                          hournr=format(seq(.$start,.$end,by=60*60),"%H")
                                          )
                               )

我们希望在16个核心(部分)上并行化此过程以加速该过程。 第一次尝试是使用multidplyr包。 分区基于daynr

df.hourlyconnected<-df.Sessions %>% 
                      partition(daynr,cluster=init_cluster(6)) %>%
                      rowwise %>% do(data.frame(userID=.$userID,
                            hourlydate=as.Date(seq(.$start,.$end,by=60*60)),
                            hournr=format(seq(.$start,.$end,by=60*60),"%H")
                              )
                            ) %>% collect()

现在, rowwise函数似乎需要数据帧作为输入而不是分区。

我的问题是

  • 是否有解决方法来对每个核心的分区执行逐行计算?

  • 有没有人有建议用不同的R包和方法执行这个计算?

(我认为将此作为答案发布可能会使对未来对有效编码感兴趣的读者受益。)


R是矢量化语言,因此按行操作是最昂贵的操作之一; 特别是如果您正在评估许多函数,调度方法,转换类和创建新数据集。

因此,第一步是减少“ 通过 ”操作。 通过查看您的代码,您似乎正在根据userIDstartend来扩大数据集的大小 - 所有其余操作都可以在后面进行(因此可以进行矢量化)。 另外,两次按行运行seq (它本身不是一个非常有效的函数)不会增加任何内容。 最后,在POSIXt类上显式调用seq.POSIXt将为您节省方法调度的开销。

我不确定如何使用dplyr有效地执行此dplyr ,因为mutate无法处理它,并且do函数(IIRC)总是证明它自身是非常低效的。 因此,让我们尝试一下可以轻松处理此任务的data.table

library(data.table) 
res <- setDT(df.Sessions)[, seq.POSIXt(start, end, by = 3600), by = .(userID, start, end)] 

再次请注意,我将“ 按行 ”操作最小化为单个函数调用,同时避免了方法调度


现在我们已准备好数据集,我们不再需要任何行操作,从现在开始,所有内容都可以进行矢量化。

虽然,矢量化并不是故事的结局。 我们还需要考虑类转换,方法调度等。例如,我们可以使用不同的Date类函数或使用format或甚至substr来创建hourlydatehournr 需要考虑的权衡是,例如, substr将是最快的,但结果将是一个character向量而不是Date - 由您来决定您是否更喜欢速度或质量最终产品。 有时你可以赢得两者,但首先你应该检查你的选择。 让我们基于3种不同的矢量化方法来计算hournr变量

library(microbenchmark)
set.seed(123)
N <- 1e5
test <- as.POSIXlt(runif(N, 1, 1e5), origin = "1900-01-01")

microbenchmark("format" = format(test, "%H"),
               "substr" = substr(test, 12L, 13L),
               "data.table::hour" = hour(test))

# Unit: microseconds
#             expr        min         lq        mean    median        uq       max neval cld
#           format 273874.784 274587.880 282486.6262 275301.78 286573.71 384505.88   100  b 
#           substr 486545.261 503713.314 529191.1582 514249.91 528172.32 667254.27   100   c
# data.table::hour      5.121      7.681     23.9746     27.84     33.44     55.36   100 a  

data.table::hour是速度和质量的明显赢家(结果是一个整数向量而不是一个字符),同时将你之前的解决方案的速度提高了~x12,000倍 (我甚至没有测试它对你的行实现)。

现在让我们尝试3种不同的data.table::hour

microbenchmark("as.Date" = as.Date(test), 
               "substr" = substr(test, 1L, 10L),
               "data.table::as.IDate" = as.IDate(test))

# Unit: milliseconds
#                 expr       min        lq      mean    median        uq       max neval cld
#              as.Date  19.56285  20.09563  23.77035  20.63049  21.16888  50.04565   100  a 
#               substr 492.61257 508.98049 525.09147 515.58955 525.20586 663.96895   100   b
# data.table::as.IDate  19.91964  20.44250  27.50989  21.34551  31.79939 145.65133   100  a 

似乎第一个和第三个选项在速度方面几乎相同,而我更喜欢as.IDate因为integer存储模式。


既然我们知道效率和质量在哪里,我们就可以通过运行来完成任务

res[, `:=`(hourlydate = as.IDate(V1), hournr = hour(V1))]

(然后你可以使用res[, yourcolname := NULL]的类似语法轻松删除不必要的列,我将留给你)


可能有更有效的方法来解决这个问题,但这证明了如何提高代码效率的可行方法。

作为旁注,如果您想进一步调查data.table语法/功能,这里是一个很好的阅读

https://github.com/Rdatatable/data.table/wiki/Getting-started

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM