2015-09-01 6 views
3

Предположим, что я хочу найти для каждой категории покупок 3 наиболее часто встречающихся почтовых индекса. В этом примере категории home, таунхаус и кондо. У меня есть данные о транзакциях, как:data.table получить N наиболее часто используемых значений по группе

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")) 

Я знаю, что я могу это сделать:

# there has to be a better way... 
d[,list(purch_count = length(purch_id)), 
    by=list(purch_cat, purch_zip)][, purch_rank := rank(-purch_count, ties.method='min'), 
           by=purch_cat][purch_rank<=3,][order(purch_cat, purch_rank)] 

    purch_cat purch_zip purch_count purch_rank 
1:  condo  39169   32   1 
2:  condo  15725   31   2 
3:  condo  75768   30   3 
4:  condo  72023   30   3 
5:  home  71294   30   1 
6:  home  56053   30   1 
7:  home  57971   29   3 
8:  home  77521   29   3 
9:  home  70124   29   3 
10:  home  25302   29   3 
11:  home  65292   29   3 
12:  home  39488   29   3 
13: townhouse  39587   33   1 
14: townhouse  80365   30   2 
15: townhouse  37360   30   2 

Но это не может быть самый элегантный data.table подход, и, кажется, своего рода медленно.

Любые предложения по сокращению количества проходов? Может быть, что-то использует table()? TYVM!

+0

@Frank, который кажется правильным на самом деле, для использования в реальном мире ... если есть связи – MichaelChirico

+2

@Frank это просто логическая связь, но это не так уж и важно. Я просто хотел purchase_rank <= 3, чтобы получить по крайней мере три результата. Я мог бы получить пять застежек, привязанных для первого (а затем следующего - шестого) и т. Д. Я открыт для различных правил привязки к галстукам. – C8H10N4O2

+1

@ C8H10N4O2 фактически, см. Редактирование. ваш ответ был в основном правильным. – MichaelChirico

ответ

4

Редактировать: улучшено.

Я думаю, что вы были точно на правильном пути. Однако одна ключевая вещь, которую вам не хватает, - это новая функция frank (от 1.9.6+ на CRAN), которая была оптимизирована и должна значительно ускорить работу вашего кода (работает почти мгновенно на ваших примерах данных строки 3):

d[ , .(purch_count = .N), 
    by = .(purch_cat, purch_zip) 
    ][, purch_rank := frank(-purch_count, ties.method = 'min'), 
    keyby = purch_cat 
    ][purch_rank <= 3, 
     ][order(purch_cat, purch_rank)] 
    purch_cat purch_zip purch_count purch_rank 
1:  condo  39169   32   1 
2:  condo  15725   31   2 
3:  condo  75768   30   3 
4:  condo  72023   30   3 
5:  home  71294   30   1 
6:  home  56053   30   1 
7:  home  57971   29   3 
8:  home  77521   29   3 
9:  home  70124   29   3 
10:  home  25302   29   3 
11:  home  65292   29   3 
12:  home  39488   29   3 
13: townhouse  39587   33   1 
14: townhouse  80365   30   2 
15: townhouse  37360   30   2 

Неполный ответ с table (медленно):

Да, один из способов заключается в использовании table.

d[ , {x <- table(purch_zip) 
x <- x[order(-x)] 
names(x[x %in% unique(x)[1:3]]) 
}, keyby = purch_cat] 
    purch_cat V1 
1:  condo 39169 
2:  condo 15725 
3:  condo 72023 
4:  condo 75768 
5:  home 56053 
6:  home 71294 
7:  home 25302 
8:  home 39488 
9:  home 57971 
10:  home 65292 
11:  home 70124 
12:  home 77521 
13:  home 16943 
14:  home 43003 
15:  home 43426 
16:  home 76501 
17:  home 81754 
18:  home 88978 
19: townhouse 39587 
20: townhouse 37360 
21: townhouse 80365 
22: townhouse 22402 
23: townhouse 33518 
24: townhouse 59347 
25: townhouse 83099 
    purch_cat V1 
+0

Они назвали функцию после @Frank?! Думаю, он этого заслуживает ... – C8H10N4O2

+0

@ C8H10N4O2 вы, вероятно, могли бы ускорить его еще немного, ограничив количество связей и подмножество на втором шаге, прежде чем вычислять 'frank' на _all_ zips, что дорого, если там есть много почтовых индексов, которые будут игнорироваться. – MichaelChirico

+0

Большое спасибо за это (+1). На data.table 1.9.5, @ Фрэнк немного быстрее (0,330 против 0,370 с, против OP.500 на моем ноутбуке), опубликованном первым и, возможно, менее неуклюжим, но ваш научил меня новой функции, поэтому я принимаю это. – C8H10N4O2

6

Один подход был бы

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 

Для достижения ничьи правило Ор, в один может сделать

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 

Этот @ ответ MichaelChirico, этот подход добавляет frank шаг.

+0

Еще раз спасибо Фрэнку - кстати, это самое быстрое решение среди трех. Согласно моему тесту, OP = .555 сек, ваш был 0,370, @ MichaelChirico - 3,5 секунды. * wait - глядя на его обновление * – C8H10N4O2

+0

Я понимаю причину для 'N> = unique (N) [3]', но я думаю, что мое мышление состоит в том, что если бы на первом месте была пятисторонняя связь, я бы не стал нужно увидеть шестой почтовый индекс (и его связи, а затем один за ним и его связи). Я знаю, что я сказал, что я открыт для разнообразных методов связывания, но мне кажется, что если бы было много связей, то другой подход был бы ближе к тому, что я искал. Еще раз спасибо, я многому научился от этого (+1) – C8H10N4O2

+0

@ C8H10N4O2 Спасибо. Я получаю, откуда вы пришли, и будет обновлять ответ и сообщать вам, найду ли я способ (как и мне было бы интересно это делать); Я просто еще не успел взглянуть. – Frank

Смежные вопросы