Определите количество времени просмотра (или отсутствия просмотра) с несколькими перекрывающимися сеансами - PullRequest
0 голосов
/ 27 мая 2020

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

  user_login       session_start        session_stop observation_name observation_value    observation_time
1      user1 2020-02-24 09:30:00 2020-02-24 09:35:00   session_status             start 2020-02-24 09:30:00
2      user1 2020-02-24 09:30:00 2020-02-24 09:35:00          species              bird 2020-02-24 09:31:00
3      user1 2020-02-24 09:30:00 2020-02-24 09:35:00          species            lizard 2020-02-24 09:32:00
4      user2 2020-02-24 09:33:00 2020-02-24 09:36:00   session_status             start 2020-02-24 09:33:00
5      user2 2020-02-24 09:33:00 2020-02-24 09:36:00          species              bird 2020-02-24 09:34:00
6      user1 2020-02-24 09:30:00 2020-02-24 09:35:00   session_status              stop 2020-02-24 09:35:00
7      user2 2020-02-24 09:33:00 2020-02-24 09:36:00   session_status              stop 2020-02-24 09:36:00
structure(list(user_login = c("user1", "user1", "user1", "user2", 
"user2", "user1", "user2"), session_start = c("2020-02-24 09:30:00", 
"2020-02-24 09:30:00", "2020-02-24 09:30:00", "2020-02-24 09:33:00", 
"2020-02-24 09:33:00", "2020-02-24 09:30:00", "2020-02-24 09:33:00"
), session_stop = c("2020-02-24 09:35:00", "2020-02-24 09:35:00", 
"2020-02-24 09:35:00", "2020-02-24 09:36:00", "2020-02-24 09:36:00", 
"2020-02-24 09:35:00", "2020-02-24 09:36:00"), observation_name = c("session_status", 
"species", "species", "session_status", "species", "session_status", 
"session_status"), observation_value = c("start", "bird", "lizard", 
"start", "bird", "stop", "stop"), observation_time = c("2020-02-24 09:30:00", 
"2020-02-24 09:31:00", "2020-02-24 09:32:00", "2020-02-24 09:33:00", 
"2020-02-24 09:34:00", "2020-02-24 09:35:00", "2020-02-24 09:36:00"
)), class = "data.frame", row.names = c(NA, -7L))

У меня есть несколько сеансов от нескольких пользователей в течение нескольких дней. Я хочу иметь возможность количественно определять для каждого часа каждого дня, сколько из этого часа смотрели (но не смотрели). Проблема в том, что, как показывают данные примера, пользователи иногда перекрывают друг друга.

Я пытался придумать решение tidyverse, но ничего не добился.

Конечная цель состоит в том, чтобы на каждый час было просмотрено количество минут. Итак, для этого подмножества данных это будет выглядеть так:

             hour min_watched
1 2020-02-24 9:00           6
structure(list(hour = "2020-02-24 9:00", min_watched = "6"), class = "data.frame", row.names = c(NA, 
-1L))

1 Ответ

1 голос
/ 27 мая 2020

Это может быть неэффективно, но я считаю, что может быть над чем работать. Он использует lubridate / tidyverse. Я подозреваю, что подход data.table может быть лучше.

Во-первых, убедитесь, что ваше время указано в формате POSIXct. Затем вы можете свернуть свои временные диапазоны из-за перекрытия.

Затем создайте последовательность часовых интервалов на основе минимального и максимального времени из ваших временных диапазонов.

Наконец, чтобы создать свой результат с количеством минут для каждого часа в последовательности часовых интервалов, используйте intersect между интервалами.

Пожалуйста, дайте мне знать, если это близко к тому, что вы имели в виду.

library(tidyverse)
library(lubridate)

# Determine overlapping ranges of times
time_ranges <- df %>%
  mutate_at(c("session_start", "session_stop", "observation_time"), as.POSIXct) %>%
  arrange(session_start) %>% 
  group_by(g = c(0, cumsum(as.numeric(lead(session_start)) > cummax(as.numeric(session_stop)))[-n()])) %>%
  summarise(start = min(session_start), stop = max(session_stop)) %>%
  mutate(interval = interval(start, stop))

# Create hourly intervals needed
hour_start <- seq(from = floor_date(min(time_ranges$start), unit = "hours"), 
                to = ceiling_date(max(time_ranges$stop), unit = "hours"), 
                by = "hours")
hour_int <- interval(hour_start, hour_start + hours(1))

# Determine overlap between hourly intervals and determined time ranges
data.frame(
  hour = hour_start,
  min_watched = sapply(seq_along(hour_int), function(x) sum(as.numeric(as.duration(intersect(hour_int[x], time_ranges$interval)), "minutes"), na.rm = TRUE))
)

Редактировать :

Если вы sh используете 30-минутные интервалы вместо одного часа, вы можете создать последовательность из 30-минутных интервалов:

# Create half hour intervals
half_hour_start <- seq(from = floor_date(min(time_ranges$start), unit = "hours"), 
                to = ceiling_date(max(time_ranges$stop), unit = "hours"), 
                by = "30 min")
half_hour_int <- interval(half_hour_start, half_hour_start + minutes(30))

Я считаю, что остальная часть кода должна быть такой же.

Изменить (10.08.2020) : Чтобы также включить временные диапазоны для каждого часа, попробуйте это при создании окончательных данных .frame:

data.frame(
  hour = hour_start,
  min_watched = sapply(seq_along(hour_int), function(x) sum(as.numeric(as.duration(intersect(hour_int[x], time_ranges$interval)), "minutes"), na.rm = TRUE)),
  time_range = sapply(seq_along(hour_int), function(x) as.character(intersect(hour_int[x], time_ranges$interval)))
)

Или, если вам просто нужно время, вы можете выполнить собственное форматирование и отбросить даты с помощью специальной функции, например:

get_range <- function(hour, interval) {
  time_int <- intersect(hour, interval)
  return(paste(format(int_start(time_int), "%H:%M"), "-", format(int_end(time_int), "%H:%M")))
}

Затем вы можно использовать это при создании окончательного data.frame:

time_range = sapply(seq_along(hour_int), function(x) get_range(hour_int[x], time_ranges$interval))
...