我是 R 的高级初学者,负责大型保险索赔数据集。
渴望建议我刚刚将团队编写的特定脚本的速度提高了约500倍。但我很想听听你对使用data.table
包提高速度的想法。
背景我们在保险数据库中每次购买的药物都被编码为包含去除member_id
和所述处方的startdate
的行。我们需要记录每次购买给定药物之间的时间长度。这样inbetween
次只是处方的startdate
与会员购买的下一个处方之间的差异。
CURRENT APPROACH base
使用数据框友好Claims
表示法对我们的主[a : b]
数据框/数据表进行子集化,可以避免使用避免循环的函数。正是这种表示法,我在使用data.table
包时遇到了困难,因为它的速度,我真的很想使用它。这个脚本已经需要大约25秒才能处理一个较小的数据集,约有300万患者需要约300万张处方;我们更大的数据集可以大10倍。我担心非线性缩放。
基本上,通过从前一行中减去预先排序的数据帧中的一行,可以轻松计算大多数inbetween
值。这种方法需要纠正每个成员的最后处方,但是,取决于该处方是否也是该成员的第一个处方(即,该成员只有一个处方)。因此,我们首先定义Claims$firstofmember
和Claims$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
答案 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"]