Подсчитать строки (условно) в течение указанного периода времени по группам в R - PullRequest
0 голосов
/ 03 августа 2020
• 1000 Вот фиктивный фрейм данных, аналогичный тому, с чем я работаю:
library(ids)#for generating the UserID variable
library(wakefield)#for generating the Status variable
library(dplyr)

set.seed(123)
UserID<-random_id(n=10, bytes = 5)
DateTime<-seq.POSIXt(from = as.POSIXct("2020-08-01 01:00:00", tz = Sys.timezone()), length.out = 70, by = "15 mins")
df<-cbind(UserID,DateTime)
df<-as.data.frame(df)
df$Status<-r_sample_factor(x = c("Answered", "Abandoned", "Engaged"), n=70)
df$DateTime<-seq.POSIXt(from = as.POSIXct("2020-08-01 01:00:00", tz = Sys.timezone()), 
                        length.out = 70, by = "15 mins")#re-doing this again as it annoyingly converts to numeric each time 

df<-df%>%arrange(UserID,DateTime)
head(df)


      #UserID            DateTime    Status
#1 0a5f3a2a8b 2020-08-01 02:00:00   Engaged
#2 0a5f3a2a8b 2020-08-01 04:30:00   Engaged
#3 0a5f3a2a8b 2020-08-01 07:00:00   Engaged
#4 0a5f3a2a8b 2020-08-01 09:30:00   Engaged
#5 0a5f3a2a8b 2020-08-01 12:00:00   Engaged
#6 0a5f3a2a8b 2020-08-01 14:30:00 Abandoned

Я хочу подсчитать количество вызовов по UserID в течение 5 часов с двумя другими условиями:

  1. Если в течение 5-часового периода с момента последнего звонка, сделанного пользователем, не было другого звонка, то он считается однократной «попыткой»
  2. Если у пользователя есть N Вызовы в течение 5-часового периода, пока они не получат ответ «Ответил», тогда это считается «успешной» попыткой. В противном случае он будет считаться «неудачным»

Вот чего я пытаюсь достичь: -

UserId          OrigTime       LastTime          Calls  Status       Successful
0a5f3a2a8b  2020-08-01 02:00:00 2020-08-01 07:00:00 3   Engaged          No
16db61d2bc  2020-08-01 03:15:00 2020-08-01 03:15:00 1   Answered         Yes
6355f7700d  2020-08-01 01:00:00 2020-08-01 06:00:00 3   Answered         Yes
9b9fab9789  2020-08-01 04:15:00 2020-08-01 09:15:00 3   Answered         Yes
...

Итак, OrigTime - это время их первого звонка в единственная попытка, а LastTime - время их последнего вызова в рамках той же единственной попытки. Столбец Calls подсчитывает количество вызовов, совершенных пользователем в рамках этой попытки, Status - это статус последнего вызова в рамках попытки, а «Успешно» может быть логичным, указывая, был ли отвечен последний вызов в этой попытке или нет. .

Любые указатели в правильном направлении были бы здорово. Я предполагаю, что есть какое-то решение data.table или dplyr, но я раньше не делал много такого рода деятельности, поэтому не уверен, с чего начать. Заранее большое спасибо :)

ДОБАВЛЕНИЕ

Я хочу повторно загрузить образец данных, который больше похож на данные, которые я я в настоящее время работаю с; Я работаю с данными журнала вызовов с горячей линии телефона, поэтому я предоставил «историю», чтобы помочь проиллюстрировать, чего я хочу достичь. Я включил еще один пример предполагаемого результата, чтобы прояснить ситуацию.

Справочная информация: каждая строка в df представляет один звонок, сделанный на горячую линию с помощью UserId; Каждая строка в callattempts представляет собой попытку (попытка может состоять из множества вызовов) дозвониться до службы.

Вот фрейм фиктивных данных: -

#create mock data frame=====================================

UserId<-c("104b0cfb910f7c9de318c05c3c305be2", "104b0cfb910f7c9de318c05c3c305be2", 
           "1320319e0b65f95b71ce5650b592039b", "54eca3518ca03ca0ae883ff3aaad79fe", 
           "73ae725b039b4ca0d3727bb2497a717d", "816194b2aa6ae09dea56876b68bb8244", 
           "e1bd4a520d490a4151882e0a136d3f28", "e1bd4a520d490a4151882e0a136d3f28", 
           "e1bd4a520d490a4151882e0a136d3f28", "e1bd4a520d490a4151882e0a136d3f28", 
           "e1bd4a520d490a4151882e0a136d3f28", "e1bd4a520d490a4151882e0a136d3f28", 
           "e1bd4a520d490a4151882e0a136d3f28", "e1bd4a520d490a4151882e0a136d3f28", 
           "e1bd4a520d490a4151882e0a136d3f28", "e1bd4a520d490a4151882e0a136d3f28", 
           "e1bd4a520d490a4151882e0a136d3f28", "e1bd4a520d490a4151882e0a136d3f28", 
           "e1bd4a520d490a4151882e0a136d3f28", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "eb431db274a6b6d2b2c7e455f4e5780b", 
           "eb431db274a6b6d2b2c7e455f4e5780b", "f4904b5d962d3e8be1dfcc237fbb7e70", 
           "f4904b5d962d3e8be1dfcc237fbb7e70", "f4904b5d962d3e8be1dfcc237fbb7e70", 
           "f4904b5d962d3e8be1dfcc237fbb7e70")


DateTime<-structure(c(1546300966, 1546301009, 1546299594, 1546300866, 1546300596, 
                       1546300596, 1546338093, 1546339236, 1546339555, 1546341083, 1546341105, 
                       1546341697, 1546342267, 1546343070, 1546344801, 1546346046, 1546366255, 
                       1546366269, 1546366284, 1546300762, 1546300827, 1546300831, 1546300888, 
                       1546300952, 1546300958, 1546300967, 1546301006, 1546301034, 1546301039, 
                       1546301148, 1546301177, 1546301195, 1546301226, 1546301284, 1546301418, 
                       1546301437, 1546301453, 1546301463, 1546301468, 1546301038, 1546301076, 
                       1546301098, 1546315566), class = c("POSIXct", "POSIXt"), tzone = "UTC")


Status<-c("Abandoned", "Answered", "Answered", "Abandoned", "Answered", 
          "Answered", "Engaged", "Abandoned", "Engaged", "Abandoned", "Abandoned", 
          "Abandoned", "Abandoned", "Engaged", "Abandoned", "Abandoned", 
          "Abandoned", "Abandoned", "Answered", "Abandoned", "Abandoned", 
          "Abandoned", "Abandoned", "Abandoned", "Abandoned", "Engaged", 
          "Abandoned", "Abandoned", "Abandoned", "Abandoned", "Abandoned", 
          "Abandoned", "Engaged", "Abandoned", "Abandoned", "Abandoned", 
          "Abandoned", "Abandoned", "Answered", "Abandoned", "Engaged", 
          "Abandoned", "Answered")

df<-as.data.frame(cbind(UserId,DateTime,Status))
df$DateTime<-as.numeric(df$DateTime)
df$DateTime<-as.POSIXct(df$DateTime, origin = "1970-01-01 00:00:00")
View(df)

Это чтобы напоминать фрейм данных, с которым я в настоящее время работаю: ниже показан предполагаемый результат: -


#The intented result


UserId<-c("816194b2aa6ae09dea56876b68bb8244", "73ae725b039b4ca0d3727bb2497a717d", 
          "eb431db274a6b6d2b2c7e455f4e5780b", "104b0cfb910f7c9de318c05c3c305be2", 
          "e1bd4a520d490a4151882e0a136d3f28", "e1bd4a520d490a4151882e0a136d3f28", 
          "f4904b5d962d3e8be1dfcc237fbb7e70", "f4904b5d962d3e8be1dfcc237fbb7e70", 
          "54eca3518ca03ca0ae883ff3aaad79fe", "1320319e0b65f95b71ce5650b592039b")


OrigTime<-structure(c(1546300596, 1546300596, 1546300762, 1546300966, 1546338093, 
                      1546366255, 1546301038, 1546315566, 1546300866, 1546299594), 
                    class = c("POSIXct", "POSIXt"), tzone = "UTC")


LastTime<-structure(c(1546300596, 1546300596, 1546301468, 1546301009, 1546346046, 
                      1546366284, 1546301098, 1546315566, 1546300866, 1546299594), 
                    class = c("POSIXct", "POSIXt"), tzone = "UTC")




Calls<-c(1, 1, 22, 2, 10, 3, 3, 1, 1, 1)



Status<-c("Answered", "Answered", "Answered", "Answered", "Abandoned", 
          "Answered", "Abandoned", "Answerered", "Abandoned", "Answered")



Successful<-c("Y", "Y", "Y", "Y", "N", "Y", "N", "Y", "N", "Y")

callattempts<-as.data.frame(cbind(UserId,OrigTime,LastTime,Calls,Status,Successful))
callattempts$OrigTime<-as.numeric(callattempts$OrigTime)
callattempts$OrigTime<-as.POSIXct(callattempts$OrigTime, origin = "1970-01-01 00:00:00")
callattempts$LastTime<-as.numeric(callattempts$LastTime)
callattempts$LastTime<-as.POSIXct(callattempts$LastTime, origin = "1970-01-01 00:00:00")

head(callattempts,10)

                     UserId            OrigTime            LastTime          Calls     Status Successful
1  816194b2aa6ae09dea56876b68bb8244 2018-12-31 23:56:36 2018-12-31 23:56:36     1   Answered          Y
2  73ae725b039b4ca0d3727bb2497a717d 2018-12-31 23:56:36 2018-12-31 23:56:36     1   Answered          Y
3  eb431db274a6b6d2b2c7e455f4e5780b 2018-12-31 23:59:22 2019-01-01 00:11:08    22   Answered          Y
4  104b0cfb910f7c9de318c05c3c305be2 2019-01-01 00:02:46 2019-01-01 00:03:29     2   Answered          Y
5  e1bd4a520d490a4151882e0a136d3f28 2019-01-01 10:21:33 2019-01-01 12:34:06    10  Abandoned          N
6  e1bd4a520d490a4151882e0a136d3f28 2019-01-01 18:10:55 2019-01-01 18:11:24     3   Answered          Y
7  f4904b5d962d3e8be1dfcc237fbb7e70 2019-01-01 00:03:58 2019-01-01 00:04:58     3  Abandoned          N
8  f4904b5d962d3e8be1dfcc237fbb7e70 2019-01-01 04:06:06 2019-01-01 04:06:06     1 Answerered          Y
9  54eca3518ca03ca0ae883ff3aaad79fe 2019-01-01 00:01:06 2019-01-01 00:01:06     1  Abandoned          N
10 1320319e0b65f95b71ce5650b592039b 2018-12-31 23:39:54 2018-12-31 23:39:54     1   Answered          Y


Вы можете видеть, что некоторые UserId находятся в фрейме данных callattempts дважды. Например, UserId «e1bd4a520d490a4151882e0a136d3f28» сделал 10 последовательных вызовов в период с 10:21:33 1 января 2019 г. по 12:34:06 1 января 2019 г. через оператора (N). Эти 10 вызовов go рассматриваются как 1 единственная попытка дозвониться до оператора, поскольку UserId «e1bd4a520d490a4151882e0a136d3f28» не выполняет еще один вызов службы в течение 60 минут после его последнего вызова, который произошел в 2019-01-01 12:34:06 .

Однако UserId «e1bd4a520d490a4151882e0a136d3f28» делает еще одну попытку дозвониться до оператора почти через 6 часов, делает 3 звонка и на 3-й звонок переходит к оператору (Отвечено; Успешно = Y). Эти 3 последовательных вызова засчитываются как 1 попытка, потому что этот пользователь, наконец, получает ответ на 3-й вызов, следовательно, завершает это.

В случае UserId «816194b2aa6ae09dea56876b68bb8244» этот пользователь вызывает службу и переходит к оператору в их первый вызов (Отвечен; Успешно = Y), который засчитывается как 1 попытка дозвониться.

Напротив, UserId «54eca3518ca03ca0ae883ff3aaad79fe» вызывает службу один раз и немедленно кладет трубку (Отказано; Успешно = N); поскольку этот вызывающий абонент не пытается снова позвонить на горячую линию в течение 60 минут после последнего звонка, последний звонок преобразуется как 1 попытка.

Есть ли способ достичь того, что представлено в callattempts кадре данных ? Будем очень признательны, если есть!

1 Ответ

1 голос
/ 06 августа 2020

Вы можете использовать purrr для разделения данных по пользователям и использовать простую функцию for-l oop для реализации logi c, которое вы ищете:

library(purrr)

CondCount <- function(data,maxdelay){
  result <- list()
  row <- 0
  calls <- 0
  OrigTime <- NA
  n <- nrow(data)
  
  for (i in 1:n) {
    if (is.na(OrigTime)) {
      OrigTime <- data$DateTime[[i]]
      calls <- 0
    }
    calls = calls + 1
    if (difftime(data$DateTime[[i]],OrigTime,units='hours') > maxdelay) {
      row <- row + 1
      result[[row]] <- data.frame(OrigTime = OrigTime, LastTime = data$DateTime[[i-1]], calls = calls, Status = factor(data$Status[[i-1]],levels=c("Answered" ,"Abandoned" ,"Engaged")), Successful = ifelse(data$Status[[i]]=="Answered",'Y','N')  )
      OrigTime <- data$DateTime[[i]]
    } 
    if ((data$Status[[i]] !="Engaged") | i == n) {
      row <- row + 1
      result[[row]] <- data.frame(OrigTime = OrigTime, LastTime = data$DateTime[[i]], calls = calls, Status = factor(data$Status[[i]],levels=c("Answered" ,"Abandoned" ,"Engaged")), Successful = ifelse(data$Status[[i]]=="Answered",'Y','N')  )
      OrigTime <- NA
    }
  } 
  dplyr::bind_rows(result)
}



df %>% arrange(UserID,DateTime) %>%
  split(.$UserID) %>%
  map(function(data) {CondCount(data,5) }) %>%
  bind_rows(.id="UserID")

       UserID            OrigTime            LastTime calls    Status Successful
1  022098d3cf 2020-08-01 03:15:00 2020-08-01 03:15:00     1  Answered          Y
2  022098d3cf 2020-08-01 05:45:00 2020-08-01 05:45:00     1  Answered          Y
3  022098d3cf 2020-08-01 08:15:00 2020-08-01 08:15:00     1 Abandoned          N
4  022098d3cf 2020-08-01 10:45:00 2020-08-01 10:45:00     1  Answered          Y
5  022098d3cf 2020-08-01 13:15:00 2020-08-01 13:15:00     1 Abandoned          N
6  022098d3cf 2020-08-01 15:45:00 2020-08-01 15:45:00     1 Abandoned          N
7  022098d3cf 2020-08-01 18:15:00 2020-08-01 18:15:00     1 Abandoned          N
8  18f13c3972 2020-08-01 01:15:00 2020-08-01 03:45:00     2 Abandoned          N
9  18f13c3972 2020-08-01 06:15:00 2020-08-01 06:15:00     1  Answered          Y
10 18f13c3972 2020-08-01 08:45:00 2020-08-01 13:45:00     3  Answered          Y

Если l oop должен быть очень быстрым, его можно легко преобразовать в R cpp.

NB: по какой-то причине set.seed (123) не работает будет достаточно для получения воспроизводимых результатов.

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