R:通过查找与使用dataframe[A:b]表示法等效的data.table来避免循环
我是R的高级初学者,负责处理大型保险索赔数据集 所需建议我刚刚将团队编写的特定脚本的速度提高了约500倍。但是我很想听听您对使用R:通过查找与使用dataframe[A:b]表示法等效的data.table来避免循环,r,dataframe,data.table,R,Dataframe,Data.table,我是R的高级初学者,负责处理大型保险索赔数据集 所需建议我刚刚将团队编写的特定脚本的速度提高了约500倍。但是我很想听听您对使用data.table包提高速度的想法 背景我们的保险数据库中的每一次药物购买都被编码为一行,其中包含一个取消识别的成员id和所述处方的起始日期。我们需要记录每次购买特定药物之间的时间长度。这两次之间的只是处方的开始日期与会员购买的下一次处方的开始日期之间的差异 当前方法基本避免循环的功能通过使用数据帧友好的[a:b]符号对我们的主要声明数据帧/数据表进行子集化而得到青睐
data.table
包提高速度的想法
背景我们的保险数据库中的每一次药物购买都被编码为一行,其中包含一个取消识别的成员id
和所述处方的起始日期
。我们需要记录每次购买特定药物之间的时间长度。这两次之间的只是处方的开始日期与会员购买的下一次处方的开始日期之间的差异
当前方法基本
避免循环的功能通过使用数据帧友好的[a:b]
符号对我们的主要声明
数据帧/数据表进行子集化而得到青睐。我在使用data.table
包执行时遇到的困难就是这种表示法,因为它的速度很快,所以我非常想使用它。该脚本已经需要约25秒来处理一个较小的数据集,该数据集包含约600000名患者的约300万张处方;我们更大的数据集可以大10倍。我担心非线性缩放
基本上,通过从前一行减去预排序数据帧中的一行,可以很容易地计算大多数中间值。然而,这种方法要求对每个成员的最后一个处方进行更正,这取决于该处方是否也是该成员的第一个处方(即该成员只有一个处方)。因此,我们首先定义索赔$firstofmember
和索赔$lastofmember
,然后在两者之间继续使用正确的计算方法:
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"]
在data.table中使用diff()和by
,应该可以得到您想要的:
setkey(Claims, member_id, startdate)
Claims[,inbetween:=c(NA,diff(startdate)), by=member_id]
setkey()确保排序正确并提高分组速度
在diff结果之前插入NA
可处理1个处方案例,并确保处方之间的间隔为尾随间隔而不是前导间隔。感谢Stephen Zander的回复
我在原始问题中提到的数据集上测试了Stephen的建议,该数据集可在25秒内处理约600000名患者的300万行数据
虽然我发现斯蒂芬的建议神秘地使我的脚本速度比我以前的版本慢了约10倍,但我还是受到了他的启发,他提到了setkey()
。通过使用键帮助合并主声明
和辅助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
>system.time({
+LTVTable
> 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