Я работаю с данными, сгенерированными пользователями, и хочу подсчитать количество строк / действий, то есть звонков каждого пользователя, сделанных в течение определенного периода времени. Вот макет фрейма данных, похожий на тот, с которым я работаю:
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-часового периода с двумя другими условиями:
- Если в течение 5-часового периода с момента последнего звонка, совершенного пользователем, не было другого звонка, то это будет единичная попытка.
- Если у пользователя есть 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
решение, но я раньше не занимался подобным образом, поэтому не знаю, с чего начать. Заранее большое спасибо :)
ИЗМЕНИТЬ
@Waldi предоставил решение, которое обеспечило почти то, что мне было нужно. Вот решение, которое пока работает лучше всего (с небольшими изменениями из ответа @Waldi): -
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 (data$Status[[i]] == "Answered" | difftime(data$DateTime[[i]],OrigTime,units='hours') > maxdelay | 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","Unknown")), 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,1) }) %>%
bind_rows(.id="UserID")
См. 2 шага, которые я написал до редактирования. На этот раз период составляет 1 час, а не 5 часов.
С решением @Waldi вот когда оно работает на моем реальном df (прошу прощения за цветовую кодировку, которую я использовал, если случайно есть какие-либо дальтоники SO-пользователи): -
Правильный результат
и с решением @Waldi он даст вам следующее: -
что правильно! Это то, к чему я стремлюсь. Однако я хочу проиллюстрировать два примера того, что происходит, когда я запускаю этот код, что дает нежелательный результат:
Неверный результат 1
что дает вам это: -
Это неверно. Это должно быть две строки, по одной для каждой попытки (каждая с окончательным статусом «Отказано»), а не одна строка, поскольку разница во времени между двумя последними строками превышает 60 минут.
Неправильный результат 2
что дает вам это: -
Это неверно. Это должно быть две строки, по одной для каждой попытки (первая строка со статусом "Вовлечена", вторая строка со статусом "ответили").
Я должен отдать должное @Waldi, потому что решение отлично работает для отвеченных звонков. Однако он не принимает во внимание другие типы статуса, например, "Отказано" и "Вовлечено". Возможно, для этих двух статусов недостаточно условий. Как всегда, мы будем благодарны за любую помощь!
dput
два последних примера с ошибками: я думаю, что исправил код, но хотел бы его протестировать. Спасибо. - person Waldi   schedule 25.09.2020