Как рассчитать уникальные значения условно по группам - PullRequest
0 голосов
/ 10 апреля 2020

У меня есть ежедневные записи пользователей. Каждый день у пользователя может быть несколько записей. Я хотел бы посчитать уникальных пользователей в скользящем 3-дневном окне. Как мне этого добиться?

set.seed(123)
dat<-data.table(day=rep(1:5,sample(6,5)))
dat$id<-sample(10,dat[,.N],replace=T)

> dat
    day id
 1:   1  1
 2:   1  6
 3:   2  9
 4:   2  6
 5:   2  5
 6:   2 10
 7:   3  5
 8:   3  7
 9:   3  6
10:   3  2
11:   3  9
12:   3  3
13:   4  1
14:   4  4
15:   4 10
16:   5  9
17:   5  7
18:   5  7
19:   5 10
20:   5  7

Я хочу получить следующий результат, т. Е. Для каждого дня x я хочу подсчитать количество уникальных идентификаторов в день x, x-1 и x-2.

sqldf('select a.day,count(distinct b.id) as user_cnt 
  from dat as a left join dat as b on a.day<=b.day+2 and a.day>=b.day group by a.day')  

day user_cnt
1     2
2     5
3     8
4     9
5     9

1 Ответ

2 голосов
/ 10 апреля 2020

После того, как вы прочитали ваш вопрос несколько раз и проверили желаемый результат, похоже, что вам нужна адаптивная скользящая «уникальная» функция, выровненная по вправо (то есть предыдущим n дням ), с окном 3 дня.

Использование datatable должно быть возможным, и akrun предоставил неадаптивное решение с align = 'left'. Однако вы хотите выровнять = 'right' (по умолчанию).

library(data.table)

dt[, .(.(id)), day][
   , frollapply(seq_len(.N), n = 3, FUN = function(i) uniqueN(unlist(V1[i])))

[1] NA NA  8  9  9

Обратите внимание, что

dt[, .(.(id)), day]
   day             V1
1:   1            1,6
2:   2     9, 6, 5,10
3:   3    5,7,6,2,9,3
4:   4        1, 4,10
5:   5  9, 7, 7,10, 7

К сожалению, data.table не предоставляет частичное (adaptive = TRUE) опция окна для функции frollapply, в отличие от других (frollmean и frollsum).

Мы можем попробовать ...

nk <- function(x, k) c(seq.int(k), rep(k, x - k))

dt[, .(.(id)), day][
    , frollapply(seq_len(.N), n = nk(.N, 3), FUN = function(i) uniqueN(unlist(V1[i])))

   V1 V2 V3 V4 V5
1:  2 NA NA NA NA
2:  4  5 NA NA NA
3:  6  7  8  8  8
4:  3  9  9  9  9
5:  3  5  9  9  9

Но мы получаем data.table с 5 столбцами с ответами, скрывающимися в диагонали.

В итоге я использовал mapply с пользовательской функцией N_unique для подсчета уникальных значений в списке идентификаторов, возвращаемых скользящим окном. Мы все еще можем использовать частичную ширину окна с функцией nk, определенной выше.

N_unique <- function(i, width, x){
    uniqueN(unlist(x[(i - (width - 1)):i]))
}

dt2 <- dt[, .(.(id)), day][
  , user_cnt := mapply(FUN = N_unique, i = seq_len(.N), 
                       width = nk(.N, 3), MoreArgs = list(x = V1))][, V1:=NULL]
dt2
   day user_cnt
1:   1        2
2:   2        5
3:   3        8
4:   4        9
5:   5        9

Данные :

dput(dt)
structure(list(day = c(1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 
3L, 3L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L), id = c(1, 6, 9, 6, 5, 
10, 5, 7, 6, 2, 9, 3, 1, 4, 10, 9, 7, 7, 10, 7)), row.names = c(NA, 
-20L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x0bae2498>)

Примечание : Команда для создания dt с использованием set.seed привела к чему-то отличному от того, что предоставил OP.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...