我已经为这个问题苦苦挣扎了一段时间了,所以我希望有人可以帮助我找到更省时的解决方案。
因此,我有一个ID的数据框,如下所示:
IDinsurer<-c(rep(11,3),rep(12,2),rep(11,2),rep(13,2),11)
ClaimFileNum<-c(rep('AA',3),rep('BB',2),rep('CC',2),rep('DD',2),'EE')
IDdriver<-c(rep(11,3),rep(12,2),rep(21,2),rep(13,2),11)
IDclaimant<-c(31,11,32,12,33,11,34,13,11,11)
IDclaimdriver<-c(41,11,32,12,11,21,34,13,12,11)
dt<-data.frame(ClaimFileNum,IDinsurer,IDdriver,IDclaimant,IDclaimdriver)
ClaimFileNum IDinsurer IDdriver IDclaimant IDclaimdriver
1 AA 11 11 31 41
2 AA 11 11 11 11
3 AA 11 11 32 32
4 BB 12 12 12 12
5 BB 12 12 33 11
6 CC 11 21 11 21
7 CC 11 21 34 34
8 DD 13 13 13 13
9 DD 13 13 11 12
10 EE 11 11 11 11
我想做的是计算单个IDinsurer在其他角色(即不是保险人)中出现的不同索赔文件(ClaimFileNum)的数量。因此,对于每个IDinsurer,我只希望索要索赔文件的数量,他的ID出现在IDdriver,IDclaimant或IDclaimdriver中,而同时他又不是给定索赔文件的IDinsurer。例如,IDinsurer == 11与所有ClaimFileNums一起出现,但是仅在“BB”和“DD”上他不是IDinsurer,这意味着我希望我的程序返回2。
所以这就是我希望我的最终数据框看起来像这样的样子:
ClaimFileNum IDinsurer IDdriver IDclaimant IDclaimdriver N
1 AA 11 11 31 41 2
2 AA 11 11 11 11 2
3 AA 11 11 32 32 2
4 BB 12 12 12 12 1
5 BB 12 12 33 11 1
6 CC 11 21 11 21 2
7 CC 11 21 34 34 2
8 DD 13 13 13 13 0
9 DD 13 13 11 12 0
10 AA 11 11 11 11 2
到目前为止,这是我能想到的:
1)
对于其他三个角色(IDdriver,IDclaimant,IDclaimdriver)中的每一个,我分别计算了一个新的列,其中包含一些数字,这些数字揭示了特定ID仅出现在该角色中的索赔文件数量,但不包括索赔文件,保险人也是保险人(但是,对于IDclaimdriver,更有意义的是排除ID匹配IDclaimant或IDdriver的情况)。这是IDdriver计数的代码:
count.duplicates <- function(dt){ #removing duplicated columns and adding a column with the frequency of duplications
x <- do.call('paste', c(dt[,c("ClaimFileNum","IDdriver")], sep = '\r'))
ox <- order(x)
rl <- rle(x[ox])
cbind(dt[ox[cumsum(rl$lengths)],,drop=FALSE],count = rl$lengths)
}
dt<-count.duplicates(dt)
dt<-data.table(dt)
dt[,same:=ifelse(dt$IDinsurer==dt$IDdriver,0,1)]
dt[,N_IDdriver:=sum(same,na.rm = T),by=list(IDdriver)]
dt[,same:=NULL]
setorder(dt,ClaimFileNum)
dt<-expandRows(dt,"count")
dt<-as.data.frame(dt)
这是我的示例在所有三个计数之后的输出:
ClaimFileNum IDinsurer IDdriver IDclaimant IDclaimdriver N_IDdriver N_IDclaimant N_IDclaimdriver
1 AA 11 11 31 41 0 1 1
2 AA 11 11 11 11 0 1 1
3 AA 11 11 32 32 0 1 0
4 BB 12 12 12 12 0 0 1
5 BB 12 12 33 11 0 1 1
6 CC 11 21 11 21 1 1 0
7 CC 11 21 34 34 1 1 0
8 DD 13 13 13 13 0 0 0
9 DD 13 13 11 12 0 1 1
10 EE 11 11 11 11 0 1 1
2)现在,我首先在整个IDinsurer列上使用了for循环,以使用匹配功能检查insurerID [i]是否出现在其他三个角色ID中。如果找到匹配项,我只是将相应N_列中的计数添加到总计数中。
这是我的for循环:
total<-length(dt$IDinsurer)
for(i in 1:total) {
j<-match(dt$IDinsurer[i],dt$IDdriver,nomatch=0);
k<-match(dt$IDinsurer[i],dt$IDclaimant,nomatch=0);
l<-match(dt$IDinsurer[i],dt$IDclaimdriver,nomatch=0);
dt$N[i]<-ifelse(j==0,0,N_IDdriver[j])+ifelse(k==0,0,N_IDclaimant[k])+ifelse(l==0,0,N_IDclaimdriver[l]);
}
现在,尽管这种方法为我提供了我需要的所有信息,但令人遗憾的是,它非常缓慢,特别是在具有超过200万个案例的数据集(如我将要使用的案例)上。我敢肯定,必须有一个更优雅的解决方案,并且我一直在尝试找出如何使用一些更有效的工具(例如data.table)来做到这一点,但我只是无法掌握它。
编辑:我决定尝试在示例中回答我的问题的两个答案,并将其与我的尝试进行比较,因此这是计算时间:
Thom Quinn的for循环:0.15秒,
我的for循环:0.25秒,
bounyball的方法:0.35秒。
在104.2万行数据集上使用我的循环花了不到10个小时。
最佳答案
众所周知,匹配很慢,在这种情况下不需要。实际上,您已经用英语解决了问题,您只需要将其翻译成计算机术语即可!
因此,对于每个IDinsurer,我只希望索取索赔文件的数量,他的ID出现在IDdriver,IDclaimant或IDclaimdriver中,而同时他不是给定索赔文件的IDinsurer
所以,让我们做到这一点。用伪代码:
for each unique IDinsurer:
count when IDdriver OR IDclaimant OR IDclaimdriver AND NOT IDinsurer
在R中,这是:
for(i in unique(dt$IDinsurer)){
index <- dt$IDinsurer != i & (dt$IDdriver == i | dt$IDclaimant == i | dt$IDclaimdriver == i)
dt[dt$IDinsurer == i, "N"] <- sum(index)
}
关于r - R-数据框的一列中的值在其他列中出现多少次? (最好不使用for循环),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41229105/