【问题标题】:Data Table merge based on date ranges基于日期范围的数据表合并
【发布时间】:2014-02-28 21:29:18
【问题描述】:

我有两张桌子,policiesclaims

policies<-data.table(policyNumber=c(123,123,124,125), 
                EFDT=as.Date(c("2012-1-1","2013-1-1","2013-1-1","2013-2-1")), 
                EXDT=as.Date(c("2013-1-1","2014-1-1","2014-1-1","2014-2-1")))
> policies
   policyNumber       EFDT       EXDT
1:          123 2012-01-01 2013-01-01
2:          123 2013-01-01 2014-01-01
3:          124 2013-01-01 2014-01-01
4:          125 2013-02-01 2014-02-01


claims<-data.table(claimNumber=c(1,2,3,4), 
                   policyNumber=c(123,123,123,124),
                   lossDate=as.Date(c("2012-2-1","2012-8-15","2013-1-1","2013-10-31")),
                   claimAmount=c(10,20,20,15))
> claims
   claimNumber policyNumber   lossDate claimAmount
1:           1          123 2012-02-01          10
2:           2          123 2012-08-15          20
3:           3          123 2013-01-01          20
4:           4          124 2013-10-31          15

保单表确实包含保单条款,因为每一行都由保单编号和生效日期唯一标识。

我想以一种将索赔与保单条款相关联的方式合并这两个表。如果索赔具有相同的保单编号,并且索赔的 lossDate 在保单期限的生效日期和到期日期内(生效日期为包含范围,到期日期为不包含范围),则索赔与保单条款相关联。我这样合并表格?

这应该类似于左外连接。结果应该是这样的

   policyNumber       EFDT       EXDT claimNumber   lossDate claimAmount
1:          123 2012-01-01 2013-01-01           1 2012-02-01          10
2:          123 2012-01-01 2013-01-01           2 2012-08-15          20
3:          123 2013-01-01 2014-01-01           3 2013-01-01          20
4:          124 2013-01-01 2014-01-01           4 2013-10-31          15
5:          125 2013-02-01 2014-02-01          NA       <NA>          NA

【问题讨论】:

  • 当你真正想做的是匹配一个范围时,我在使用 roll 时遇到了麻烦。如果您无法获得您期望的结果,一种方法是将范围转换为每个可能值的唯一行。一个例子就是这个问题,奇怪的是,eddi 回答了这个问题。 stackoverflow.com/questions/16423817/…
  • @DeanMacGregor 那是在我学会如何使用 roll 之前写的:) 你可以在那里看到 cmets 的另一个 SO 帖子,其中使用 roll 解决了一个非常相似的问题。
  • @eddi 我知道roll 应该可以解决这些问题,但“旧方法”似乎更适合以奇怪的方式重叠的数据。我遇到了其他问题,roll 应该可以工作,但我认为我的数据集可能有奇怪的重叠,或者由于其他原因,它没有给出我预期的结果。长话短说,肯定让roll 第一次尝试,但如果你因为某些东西不匹配而将头撞到墙上,那么预转换可能是一种解决方法。
  • @eddi roll 似乎删除了我的 lossDate 列。你知道我怎样才能在结果中保留那一列吗?

标签: r data.table


【解决方案1】:

版本 1(针对 data.table v1.9.4+ 更新)

试试这个:

# Policies table; I've added policyNumber 126:
policies<-data.table(policyNumber=c(123,123,124,125,126), 
                     EFDT=as.Date(c("2012-01-01","2013-01-01","2013-01-01","2013-02-01","2013-02-01")), 
                     EXDT=as.Date(c("2013-01-01","2014-01-01","2014-01-01","2014-02-01","2014-02-01")))

# Claims table; I've added two claims for 126 that are before and after the policy dates:
claims<-data.table(claimNumber=c(1,2,3,4,5,6), 
                   policyNumber=c(123,123,123,124,126,126),
                   lossDate=as.Date(c("2012-2-1","2012-8-15","2013-1-1","2013-10-31","2012-06-01","2014-03-01")),
                   claimAmount=c(10,20,20,15,5,25))

# Set the keys for policies and claims so we can join them:
setkey(policies,policyNumber,EFDT)
setkey(claims,policyNumber,lossDate)

# Join the tables using roll
# ans<-policies[claims,list(EFDT,EXDT,claimNumber,lossDate,claimAmount,inPolicy=F),roll=T][,EFDT:=NULL] ## This worked with earlier versions of data.table, but broke when they updated the by-without-by behavior...
ans<-policies[claims,list(.EFDT=EFDT,EXDT,claimNumber,lossDate,claimAmount,inPolicy=F),by=.EACHI,roll=T][,`:=`(EFDT=.EFDT, .EFDT=NULL)]

# The claim should have inPolicy==T where lossDate is between EFDT and EXDT:
ans[lossDate>=EFDT & lossDate<=EXDT, inPolicy:=T]

# Set the keys again, but this time we'll join on both dates:
setkey(ans,policyNumber,EFDT,EXDT)
setkey(policies,policyNumber,EFDT,EXDT)

# Union the ans table with policies that don't have any claims:
ans<-rbindlist(list(ans, ans[policies][is.na(claimNumber)]))

ans
#   policyNumber       EFDT       EXDT claimNumber   lossDate claimAmount inPolicy
#1:          123 2012-01-01 2013-01-01           1 2012-02-01          10     TRUE
#2:          123 2012-01-01 2013-01-01           2 2012-08-15          20     TRUE
#3:          123 2013-01-01 2014-01-01           3 2013-01-01          20     TRUE
#4:          124 2013-01-01 2014-01-01           4 2013-10-31          15     TRUE
#5:          126       <NA>       <NA>           5 2012-06-01           5    FALSE
#6:          126 2013-02-01 2014-02-01           6 2014-03-01          25    FALSE
#7:          125 2013-02-01 2014-02-01          NA       <NA>          NA       NA

第 2 版

@Arun 建议使用 data.table 中的新 foverlaps 函数。我在下面的尝试似乎更难,而不是更容易,所以请告诉我如何改进它。

## The foverlaps function requires both tables to have a start and end range, and the "y" table to be keyed
claims[, lossDate2:=lossDate]  ## Add a redundant lossDate column to use as the end range for claims
setkey(policies, policyNumber, EFDT, EXDT) ## Set the key for policies ("y" table)

## Find the overlaps, remove the redundant lossDate2 column, and add the inPolicy column:
ans2 <- foverlaps(claims, policies, by.x=c("policyNumber", "lossDate", "lossDate2"))[, `:=`(inPolicy=T, lossDate2=NULL)]

## Update rows where the claim was out of policy:
ans2[is.na(EFDT), inPolicy:=F]

## Remove duplicates (such as policyNumber==123 & claimNumber==3),
##   and add policies with no claims (policyNumber==125):
setkey(ans2, policyNumber, claimNumber, lossDate, EFDT) ## order the results
setkey(ans2, policyNumber, claimNumber) ## set the key to identify unique values
ans2 <- rbindlist(list(
  unique(ans2), ## select only the unique values
  policies[!.(ans2[, unique(policyNumber)])] ## policies with no claims
), fill=T)

ans2
##    policyNumber       EFDT       EXDT claimNumber   lossDate claimAmount inPolicy
## 1:          123 2012-01-01 2013-01-01           1 2012-02-01          10     TRUE
## 2:          123 2012-01-01 2013-01-01           2 2012-08-15          20     TRUE
## 3:          123 2012-01-01 2013-01-01           3 2013-01-01          20     TRUE
## 4:          124 2013-01-01 2014-01-01           4 2013-10-31          15     TRUE
## 5:          126       <NA>       <NA>           5 2012-06-01           5    FALSE
## 6:          126       <NA>       <NA>           6 2014-03-01          25    FALSE
## 7:          125 2013-02-01 2014-02-01          NA       <NA>          NA       NA

第 3 版

使用foverlaps(),另一个版本:

require(data.table) ## 1.9.4+
setDT(claims)[, lossDate2 := lossDate]
setDT(policies)[, EXDTclosed := EXDT-1L]
setkey(claims, policyNumber, lossDate, lossDate2)
foverlaps(policies, claims, by.x=c("policyNumber", "EFDT", "EXDTclosed"))

foverlaps() 需要 startend 范围/间隔。因此,我们将lossDate 列复制到lossDate2

由于EXDT需要为开区间,我们从中减去一个,并将其放入新列EXDTclosed

现在,我们设置密钥。 foverlaps() 要求最后两个键列是间隔。所以它们是最后指定的。我们还希望通过policyNumber 重叠加入第一次匹配。因此,它也在键中指定。

我们需要在claims 上设置密钥(检查?foverlaps)。我们不必在policies 上设置密钥。但是如果你愿意,你可以(然后你可以跳过by.x 参数,因为它默认采用键值)。由于我们没有在这里为policies 设置键,我们将在by.x 参数中明确指定相应的列。默认情况下,重叠类型为any,我们不必更改(因此未指定)。这导致:

#    policyNumber claimNumber   lossDate claimAmount  lossDate2       EFDT       EXDT EXDTclosed
# 1:          123           1 2012-02-01          10 2012-02-01 2012-01-01 2013-01-01 2012-12-31
# 2:          123           2 2012-08-15          20 2012-08-15 2012-01-01 2013-01-01 2012-12-31
# 3:          123           3 2013-01-01          20 2013-01-01 2013-01-01 2014-01-01 2013-12-31
# 4:          124           4 2013-10-31          15 2013-10-31 2013-01-01 2014-01-01 2013-12-31
# 5:          125          NA       <NA>          NA       <NA> 2013-02-01 2014-02-01 2014-01-31

【讨论】:

  • 感谢您的回复。第一次设置ans 时会抛出错误。找不到对象“EFDT”
  • 嗯...想知道data.table 版本是否有所不同(我使用的是 1.8.11)?作为一种解决方法,看看如果你从那行代码中删除最后一点 ([,EFDT:=NULL]) 会发生什么。
  • @dnkbrky,是的,在 1.8.11 中,在 by-without-by 期间,j 中的键列也可见。它或多或少与this FR有关。
  • @BenGorman,当您运行 policies[claims] 时,生成的列名称是什么?
  • +1。 @dnlbrky,使用 1.9.4+ 中的 foverlaps() 函数可以使这变得更简单。你想试试吗?
【解决方案2】:

我认为这主要是你想要的。我需要运行,所以没有时间添加没有索赔的政策并清理列,但我认为解决了困难的问题:

setkey(policies, policyNumber, EXDT)
policies[, EXDT2:=EXDT]
policies[claims[, list( policyNumber, lossDate, lossDate, claimNumber, claimAmount)], roll=-Inf]
#    policyNumber       EXDT       EFDT      EXDT2   lossDate claimNumber claimAmount
# 1:          123 2012-02-01 2012-01-01 2013-01-01 2012-02-01           1          10
# 2:          123 2012-08-15 2012-01-01 2013-01-01 2012-08-15           2          20
# 3:          123 2013-01-01 2012-01-01 2013-01-01 2013-01-01           3          20
# 4:          124 2013-10-31 2013-01-01 2014-01-01 2013-10-31           4          15

另外,请注意,从该结果中删除/突出显示保单日期之外的索赔是微不足道的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多