R:通过查找A data.table等效使用dataframe [a:b]表示法

时间:2015-11-26 04:08:31

标签: r dataframe data.table

我是 R 的高级初学者,负责大型保险索赔数据集。

渴望建议我刚刚将团队编写的特定脚本的速度提高了约500倍。但我很想听听你对使用data.table包提高速度的想法。

背景我们在保险数据库中每次购买的药物都被编码为包含去除member_id和所述处方的startdate的行。我们需要记录每次购买给定药物之间的时间长度。这样inbetween次只是处方的startdate与会员购买的下一个处方之间的差异。

CURRENT APPROACH base使用数据框友好Claims表示法对我们的主[a : b]数据框/数据表进行子集化,可以避免使用避免循环的函数。正是这种表示法,我在使用data.table包时遇到了困难,因为它的速度,我真的很想使用它。这个脚本已经需要大约25秒才能处理一个较小的数据集,约有300万患者需要约300万张处方;我们更大的数据集可以大10倍。我担心非线性缩放。

基本上,通过从前一行中减去预先排序的数据帧中的一行,可以轻松计算大多数inbetween值。这种方法需要纠正每个成员的最后处方,但是,取决于该处方是否也是该成员的第一个处方(,该成员只有一个处方)。因此,我们首先定义Claims$firstofmemberClaims$lastofmember,然后继续计算in-between的正确方法:

  library(data.table)
  Claims[order(member_id, startdate)]
  Claims$inbetween[1 : (nrow(Claims) - 1)] <- Claims$startdate[2 : nrow(Claims)] 
                               - Claims$startdate[1 : (nrow(Claims) - 1)] 

  Claims$firstofmember <- "Y"
  Claims$firstofmember[2 : nrow(Claims)] <- 
       ifelse(Claims$member_id[2 : nrow(Claims)] != 
       Claims$member_id[1 : (nrow(Claims) - 1)], "Y", "N")
  Claims$lastofmember <- "Y"
  Claims$lastofmember[1 : (nrow(Claims) - 1)] <- 
       ifelse(Claims$member_id[2 : nrow(Claims)] != 
       Claims$member_id[1 : (nrow(Claims) - 1)], "Y", "N")

 Claims$inbetween <- 0
 Claims$inbetween[1 : (nrow(Claims) - 1)] <- Claims$startdate[2 : nrow(Claims)] - 
       Claims$startdate[1 : (nrow(Claims) - 1)]
 Claims$inbetween[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"]   
       <- round(pmax(Claims$ltvdate[Claims$firstofmember == "Y" &        
       Claims$lastofmember == "Y"], Claims$enddate[Claims$firstofmember == 
       "Y" & Claims$lastofmember == "Y"])) -  
       Claims$startdate[Claims$firstofmember == "Y" & Claims$lastofmember == 
       "Y"]
 Claims$inbetween[Claims$firstofmember == "N" & Claims$lastofmember == "Y"] 
       <- Claims$enddate[Claims$firstofmember == "N" & Claims$lastofmember 
       == "Y"] - Claims$startdate[Claims$firstofmember == "N" &  
       Claims$lastofmember == "Y"]

会话信息

 R version 3.2.2 (2015-08-14)
 Platform: x86_64-apple-darwin13.4.0 (64-bit)
 Running under: OS X 10.11 (El Capitan)

 locale:
 [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

 attached base packages:
  [1] grid      stats4    parallel  stats     graphics  grDevices utils               datasets  methods   base     

 other attached packages:
  [1] XVector_0.10.0       BiocInstaller_1.20.1 RColorBrewer_1.1-2   gplots_2.17.0        RPostgreSQL_0.4      DBI_0.3.1           
  [7] zoo_1.7-12           stringr_1.0.0        fields_8.3-5         maps_3.0.0-2         spam_1.3-0           shiny_0.12.2        
 [13] zipcode_1.0          visreg_2.2-0         plyr_1.8.3           IRanges_2.4.1        S4Vectors_0.8.2      BiocGenerics_0.16.1 
 [19] dplyr_0.4.3          data.table_1.9.6    

 loaded via a namespace (and not attached):
 [1] Rcpp_0.12.2        bitops_1.0-6       tools_3.2.2        zlibbioc_1.16.0    digest_0.6.8       jsonlite_0.9.17    lattice_0.20-33   
 [8] gtools_3.5.0       caTools_1.17.1     R6_2.1.1           gdata_2.17.0       magrittr_1.5       htmltools_0.2.6    assertthat_0.1    
 [15] mime_0.4           xtable_1.8-0       httpuv_1.3.3       KernSmooth_2.23-15 stringi_1.0-1      lazyeval_0.1.10    chron_2.3-47 

2 个答案:

答案 0 :(得分:0)

在data.table中使用diff()和by可以为您提供所需内容:

setkey(Claims, member_id, startdate)
Claims[,inbetween:=c(NA,diff(startdate)), by=member_id]

setkey()确保排序正常工作并提高分组速度。

在差异结果之前插入NA会处理1个处方案例,并确保您的尾随差距不是处方之间​​的主要差距。

答案 1 :(得分:0)

谢谢Stephen Zander的回复。

我测试了斯蒂芬关于我在原始问题中提到的数据集的建议,其约300万行的约300万行可在约25秒内处理。

虽然我发现斯蒂芬的建议使我的剧本相对于我以前的版本神秘地减速了约10倍,但我的灵感来自他对setkey()的提及。通过使用键来帮助合并我的主Claims和辅助LTVTable data.tables,相对于我以前的版本,我的脚本加倍了大约3倍。这是我的剧本:

  > system.time({
  +   LTVTable <- LTVTable[, .(member_id, mincorsdate, maxcoredate)]
  +   LTVTable <- LTVTable[order(member_id, mincorsdate, maxcoredate)]
  +   setkey(LTVTable, member_id)
  +   setkey(Claims, member_id)
  +   Claims <- Claims[LTVTable]
  +   Claims <- as.data.frame(Claims)
  +   Claims$mincorsdate <- as.Date(Claims$mincorsdate, origin = "1970-01-01")
  +   Claims$maxcoredate <- as.Date(Claims$maxcoredate, origin = "1970-01-01")
  +   Claims$ltvdate <- as.Date(Claims$mincorsdate + ltv, origin = "1970-01-01")
  +   
  +   Claims$firstofmember <- "Y"
  +   Claims$firstofmember[2 : nrow(Claims)] <- ifelse(Claims$member_id[2 : nrow(Claims)] != Claims$member_id[1 : (nrow(Claims) - 1)], "Y", "N")
  +   Claims$lastofmember <- "Y"
  +   Claims$lastofmember[1 : (nrow(Claims) - 1)] <- ifelse(Claims$member_id[2 : nrow(Claims)] != Claims$member_id[1 : (nrow(Claims) - 1)], "Y", "N")
  +     
  +   Claims$inbetween <- 0
  +   Claims$inbetween[1 : (nrow(Claims) - 1)] <- Claims$startdate[2 : nrow(Claims)] - Claims$startdate[1 : (nrow(Claims) - 1)]
  +   Claims$inbetween[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"] <- round(pmax(Claims$ltvdate[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"], Claims$enddate[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"])) - Claims$startdate[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"]
  +   Claims$inbetween[Claims$firstofmember == "N" & Claims$lastofmember == "Y"] <- Claims$enddate[Claims$firstofmember == "N" & Claims$lastofmember == "Y"] - Claims$startdate[Claims$firstofmember == "N" & Claims$lastofmember == "Y"]
  +   
  +   Claims$inbetween2 <- 0
  +   Claims$inbetween2[1 : (nrow(Claims) - 1)] <- Claims$startdate[2 :      nrow(Claims)] - Claims$startdate[1 : (nrow(Claims) - 1)]
  +   Claims$inbetween2[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"] <- Claims$enddate[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"] - Claims$startdate[Claims$firstofmember == "Y" & Claims$lastofmember == "Y"]
  +   Claims$inbetween2[Claims$firstofmember == "N" & Claims$lastofmember == "Y"] <- Claims$inbetween[Claims$firstofmember == "N" & Claims$lastofmember == "Y"]
  + })
  user  system elapsed 
  8.003   1.497   9.582

很想听到任何其他想法浮出水面。我自己正在研究以开头的代码行的data.table等价物  Claims$inbetweenN[Claims$firstofmember == "X" & Claims$lastofmember == "Y"]