Создать последовательность временных различий на основе условия

#r #dplyr #lapply #posixct

#r #dplyr #lapply #posixct

Вопрос:

TLDR: необходимо создать последовательность отдельных строк, но возникают проблемы с временными последовательностями

У меня есть список фреймов данных, каждый из которых выглядит примерно так (df1):

 sector1 = data.frame(date = rep(seq.POSIXt(as.POSIXct("2001-01-01 00:00:00"),format = "%Y/%m/%d %H:%M:%S",
                                           as.POSIXct("2001-01-01  04:00:00"),format = "%Y/%m/%d  %H:%M:%S","hour"),
                                length.out = 7), order = rep(1,length.out = 7))


sector2 = data.frame(date = rep(seq.POSIXt(as.POSIXct("2001-02-01 04:30:00"),format = "%Y/%m/%d %H:%M:%S",
                                           as.POSIXct("2001-02-01  06:00:00"),format = "%Y/%m/%d  %H:%M:%S","hour"),
                                length.out = 7), order = rep(2,length.out = 7))


sector3 = data.frame(date = rep(seq.POSIXt(as.POSIXct("2001-03-01 06:30:00"),format = "%Y/%m/%d %H:%M:%S",
                                           as.POSIXct("2001-03-01  10:00:00"),format = "%Y/%m/%d  %H:%M:%S","hour"),
                                length.out = 7), order = rep(3,length.out = 7))


# binding sectors
df1 = rbind(sector1,sector2,sector3) %>% distinct(date,order)

  

В основном у них есть «порядок» и дата (а также другие столбцы). Что мне нужно, так это извлечь последовательность строк с самой ранней датой, на которую порядок переходит из одного состояния в другое (в конечном итоге возвращая только уникальный порядок; таким образом, в этом случае я ожидал бы 3 строки), а затем вычислить, сколько времени потребовалось для изменения этого состояния. Для целей этого примера я буду выполнять действия с одним фреймом данных, но каким бы ни был ответ, имейте в виду, что он будет применен с использованием lapply к списку.

Настройка фрейма данных repex:

 #adding spurious row with order 3 but date that precedes order 2
df1[12,] = data.frame(date = as.POSIXct("2001-02-01 03:30:00"), order = 3)

# extracting rows of length(unique(df1$order))
df2 = df1 %>% group_by(order) %>% slice_min(order_by = date, n = 1) 

df2 =  df2 %>% arrange(date)

  

Первоначально я достиг этого, хотя и довольно медленно, используя:

 df2 %>% group_by(order) %>% slice_min(order_by = date, n = 1) %>% 
  as.data.frame() %>%  mutate(time_between = as.numeric(date-lag(date), units = 'hours'))
  

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

Это результат:

                  date order time_between
1 2001-01-01 00:00:00     1           NA
2 2001-02-01 04:30:00     2        748.5
3 2001-02-01 03:30:00     3         -1.0
  

Хотя вышеуказанное работает в большинстве случаев (это довольно медленно), возникают проблемы, когда дата из последующего заказа (3 в приведенном выше примере) помечается по времени до предыдущего заказа (2 в приведенном выше случае). Это означает, что у меня отрицательное значение времени (-1.0), что не имеет смысла.

То, что я хотел бы сделать, это вместо простой группировки по порядку, тогда нарезка первой строки — это своего рода логическая операция, при которой, если дата / время строки, которая должна была быть извлечена, предшествует строке предыдущего порядка, она отбрасывается, и первая строка после выбора времени, в этом случаев случае, если это будет 2001-03-01 06:30:00 3

                  date order time_between
1 2001-01-01 00:00:00     1           NA
2 2001-02-01 04:30:00     2        748.5
3 2001-03-01 06:30:00     3        674.0
  

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

 lapply(list1, function(x) {x %>% group_by(order) %>% slice_min(order_by = date, n = 1) %>% ungroup()})
lapply(list1, function(x) {x %>% mutate(time_between = as.numeric(date-lag(date), units = 'hours'))})
  

Дополнительный пример фрейма данных:

 df1 = data.frame(datetime = as.POSIXct(c("2019-04-11 21:46:55",
                                     "2019-04-13 00:19:23",
                                     "2019-04-15 01:20:41",
                                     "2019-04-15 04:18:12",
                                     "2019-04-23 00:50:45",
                                     "2019-04-22 08:44:41",
                                     "2019-04-24 05:54:17",
                                     "2019-04-23 07:21:36")), order = c(1,3,4,5,6,7,9,7))

  

Ответ №1:

Я не уверен точно, какой шаг замедляет процесс, но, начиная с df1 этого, вы можете сохранить одну строку для каждой даты, используя distinct , а затем вычесть время, используя lag и as.numeric .

 library(dplyr)

df1 %>%
  mutate(date = lubridate::ymd_hms(date))  %>%
  arrange(order, date) %>%
  distinct(order, .keep_all = TRUE) %>%
  mutate(time_between = as.numeric(date - lag(date), units = 'hours'))

#                 date order  only_date time_between
#1 2001-01-01 00:00:00     1 2001-01-01           NA
#2 2001-02-01 04:30:00     2 2001-02-01        748.5
#3 2001-03-01 06:30:00     3 2001-03-01        674.0
  

Очевидно, что для списка фреймов данных используйте его с lapply / map :

 lapply(list1, function(x) {
 x %>%
    mutate(date = lubridate::ymd_hms(date))  %>%
    arrange(order, date) %>%
    distinct(order, .keep_all = TRUE) %>%
    mutate(time_between = as.numeric(date - lag(date), units = 'hours'))
})
  

Комментарии:

1. Ronak. Спасибо за ввод. Я должен был быть более ясным. Меня интересует не только дата, но и время. Таким образом, строка для определенного порядка может иметь более раннее время (чем строка для предыдущего порядка), если это так, я хочу проигнорировать это и использовать первую строку, чтобы иметь время после этого. Другими словами, мне нужно, чтобы порядок был последовательным, а время — последовательным, так как тогда я могу успешно вычислить разницу. Извините, это довольно запутанно.

2. Медленная часть — это то, где я сгруппировал по порядку и нарезал min ()

3. @Dasr Хорошо, так что добавление arrange(date) %>% перед distinct помощью? Чтобы вы всегда получали минимальное время для каждой даты при использовании distinct ?

4. Хорошо, я попытаюсь описать, используя цели: 1) Мне нужно извлечь все отдельные заказы, появляющиеся в фрейме данных (в этом примере 3) 2) Мне нужна одна строка для каждого заказа, который будет отображаться в отфильтрованном фрейме данных 3) Мне нужно, чтобы разница во времени вычислялась между строкой до и строкой после (исключая первую строку, для которой это невозможно) 4) каждая строка должна быть репрезентативной для самого раннего времени, когда конкретный порядок присутствовал в нефильтрованном фрейме данных, если это время не предшествует предыдущей строке, если это так, оно должно бытьпосмотрите ниже в исходном df и выберите другую строку с …

5. тот же порядок, но более позднее время