R data.table按组获取N个最频繁的值
比如说,我想为每个购物类别查找前三位最常见的邮政编码。在本例中,类别为住宅、联排别墅和公寓。我有如下事务数据:R data.table按组获取N个最频繁的值,r,data.table,R,Data.table,比如说,我想为每个购物类别查找前三位最常见的邮政编码。在本例中,类别为住宅、联排别墅和公寓。我有如下事务数据: set.seed(1234) d <- data.table(purch_id = 1:3e6, purch_cat = sample(x = c('home','townhouse','condo'), size = 3e6, replace=TRUE),
set.seed(1234)
d <- data.table(purch_id = 1:3e6,
purch_cat = sample(x = c('home','townhouse','condo'),
size = 3e6, replace=TRUE),
purch_zip = formatC( sample(x = 1e4:9e4, size = 3e6, replace=TRUE),
width = 5, format = "d", flag = "0") )
set.seed(1234)
d编辑:改进。
我认为你的思路是正确的。但是,您缺少的一个关键功能是函数frank
,该函数已经过优化,应该可以大大提高代码的速度(在3m行样本数据上几乎同时运行):
一种方法是
d[ , .N, by=.(purch_cat, purch_zip)][
order(-N),
.SD[ N >= unique(N)[3] ]
,by=purch_cat]
给
purch_cat purch_zip N
1: townhouse 39587 33
2: townhouse 80365 30
3: townhouse 37360 30
4: townhouse 83099 28
5: townhouse 33518 28
6: townhouse 59347 28
7: townhouse 22402 28
8: condo 39169 32
9: condo 15725 31
10: condo 75768 30
11: condo 72023 30
12: home 71294 30
13: home 56053 30
14: home 57971 29
15: home 77521 29
16: home 70124 29
17: home 25302 29
18: home 65292 29
19: home 39488 29
20: home 81754 28
21: home 43426 28
22: home 16943 28
23: home 88978 28
24: home 43003 28
25: home 76501 28
purch_cat purch_zip N
purch_cat purch_zip N
1: townhouse 39587 33
2: townhouse 80365 30
3: townhouse 37360 30
4: condo 39169 32
5: condo 15725 31
6: condo 75768 30
7: condo 72023 30
8: home 71294 30
9: home 56053 30
10: home 57971 29
11: home 77521 29
12: home 70124 29
13: home 25302 29
14: home 65292 29
15: home 39488 29
要实现OP打破平局的规则,可以这样做
d[ , .N, by=.(purch_cat,purch_zip)][
order(-N),
.SD[ N >= unique(N)[3] ][
.N - frank(N, ties.method='max') < 3 ]
, by=purch_cat]
根据@MichaelChirico的回答,这种方法增加了一个frank
步骤。@frank实际上似乎是正确的,用于现实世界。。。如果有ties@Frank这只是打破平局的逻辑,但没什么大不了的。我只是想要purch_rank@C8H10N4O2实际上,请参见编辑。您的回答基本正确。frank()
在下一个版本中也将进行内部优化[以避免eval()
惩罚](希望)…再次感谢frank--顺便说一句,这似乎是三种解决方案中最快的一种。根据我的基准测试,OP=.555秒,你的是.370秒,@MichaelChirico的是3.5秒。等等——看看他的更新我明白为什么N>=unique(N)[3]
,但我想我的想法是,如果第一个有五个邮政编码,我就不需要看第六个邮政编码(及其联系,然后是第六个邮政编码及其联系)。我知道我说过我对各种打破僵局的方法持开放态度,但我觉得如果有很多领带的话,其他的方法会更接近我想要的。再次感谢,我从这个(+1)@C8H10N4O2中学到了很多谢谢。我知道你从哪里来,并会更新答案,如果我找到了一种方法,会让你知道(因为我也有兴趣这样做);我只是还没来得及看一看。@C8H10N4O2已更新。事实证明,只需一个额外的步骤,就可以以相同的方式编写。@C8H10N4O2您可以通过限制领带的数量,在计算所有拉链上的frank
之前,在第二步中进行子集设置,从而加快编写速度。如果有很多拉链将被忽略,这将非常昂贵。非常感谢(+1). 在data.table 1.9.5中,@Frank的速度稍快(330秒对370秒,对我笔记本电脑上的OP.500秒),先发布,可能不那么笨重,但你的教了我一个新功能,所以我接受这个。
purch_cat purch_zip N
1: townhouse 39587 33
2: townhouse 80365 30
3: townhouse 37360 30
4: townhouse 83099 28
5: townhouse 33518 28
6: townhouse 59347 28
7: townhouse 22402 28
8: condo 39169 32
9: condo 15725 31
10: condo 75768 30
11: condo 72023 30
12: home 71294 30
13: home 56053 30
14: home 57971 29
15: home 77521 29
16: home 70124 29
17: home 25302 29
18: home 65292 29
19: home 39488 29
20: home 81754 28
21: home 43426 28
22: home 16943 28
23: home 88978 28
24: home 43003 28
25: home 76501 28
purch_cat purch_zip N
d[ , .N, by=.(purch_cat,purch_zip)][
order(-N),
.SD[ N >= unique(N)[3] ][
.N - frank(N, ties.method='max') < 3 ]
, by=purch_cat]
purch_cat purch_zip N
1: townhouse 39587 33
2: townhouse 80365 30
3: townhouse 37360 30
4: condo 39169 32
5: condo 15725 31
6: condo 75768 30
7: condo 72023 30
8: home 71294 30
9: home 56053 30
10: home 57971 29
11: home 77521 29
12: home 70124 29
13: home 25302 29
14: home 65292 29
15: home 39488 29