Неожиданное поведение dplyr :: right_join () для расширения временных рядов POSIXct - PullRequest
1 голос
/ 20 марта 2019

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

Я думал создать фрейм данных, содержащий полный почасовой временной ряд, а затем dplyr::right_join() мои данные по этому временному ряду.Я думал, что это будет заполнять правильные значения там, где было совпадение для ежедневных данных (в полночь) и заполнять NA везде, где не было совпадения (любой час, кроме полуночи).Кажется, это работает только тогда, когда временные ряды в моих данных представлены только по дням, а не как сочетание ежедневных и часовых значений, что было неожиданным.Почему правильное объединение не расширяет дневной временной ряд, когда он сосуществует во фрейме данных вместе с другим часовым временным рядом?

Я создал минимальный пример ниже.Мой репрезентативный набор данных, который я хочу расширить, называется allData и содержит набор ежедневных и часовых наборов данных из двух разных переменных временных рядов, Daily TS и Hourly TS.

dailyData <- data.frame( 
  DateTime = seq.POSIXt(lubridate::ymd_hms('2019-01-01', truncated=3), 
                        lubridate::ymd_hms('2019-01-07', truncated=3), 
                        by='day'),
  Name = 'Daily TS'
  )

allHours <- data.frame(
  DateTime = seq.POSIXt(lubridate::ymd_hms('2019-01-01', truncated=3), 
                        lubridate::ymd_hms('2019-01-07 23:00:00'), 
                        by='hour')
)

hourlyData <- allHours %>%
  dplyr::mutate( Name = 'Hourly TS' )

allData <- rbind( dailyData, hourlyData )

Это дает

head( allData, n=15 )
              DateTime      Name
1  2019-01-01 00:00:00  Daily TS
2  2019-01-02 00:00:00  Daily TS
3  2019-01-03 00:00:00  Daily TS
4  2019-01-04 00:00:00  Daily TS
5  2019-01-05 00:00:00  Daily TS
6  2019-01-06 00:00:00  Daily TS
7  2019-01-07 00:00:00  Daily TS
8  2019-01-01 00:00:00 Hourly TS
9  2019-01-01 01:00:00 Hourly TS
10 2019-01-01 02:00:00 Hourly TS
11 2019-01-01 03:00:00 Hourly TS
12 2019-01-01 04:00:00 Hourly TS
13 2019-01-01 05:00:00 Hourly TS
14 2019-01-01 06:00:00 Hourly TS
15 2019-01-01 07:00:00 Hourly TS

Теперь я подумал, что dplyr::right_join() полной часовой последовательности значений POSIXct против allData$DateTime расширило бы дневной временной ряд, оставив значения NA для любых часов, явно не представленных в данных,Затем я мог бы использовать tidyr::fill(), чтобы заполнить их в течение дня.Однако следующий код не ведет себя таким образом:

expanded_BAD <- allData %>%
  dplyr::right_join( allHours, by='DateTime' ) %>%
  tidyr::fill( dplyr::everything(), .direction='down' ) %>%
  dplyr::arrange( Name, DateTime )

expanded_BAD показывает, что ежедневные данные не были расширены на right_join().То есть, часы в allHours, пропущенные из allData, не были сохранены в результате, что, как я думал, и было целью использования правильного соединения.Вот заголовок результата:

head(expanded_BAD, n=15)
              DateTime      Name
1  2019-01-01 00:00:00  Daily TS
2  2019-01-02 00:00:00  Daily TS
3  2019-01-03 00:00:00  Daily TS
4  2019-01-04 00:00:00  Daily TS
5  2019-01-05 00:00:00  Daily TS
6  2019-01-06 00:00:00  Daily TS
7  2019-01-07 00:00:00  Daily TS
8  2019-01-01 00:00:00 Hourly TS
9  2019-01-01 01:00:00 Hourly TS
10 2019-01-01 02:00:00 Hourly TS
11 2019-01-01 03:00:00 Hourly TS
12 2019-01-01 04:00:00 Hourly TS
13 2019-01-01 05:00:00 Hourly TS
14 2019-01-01 06:00:00 Hourly TS
15 2019-01-01 07:00:00 Hourly TS

Интересно, что если мы выполним точно такое же правое объединение только с ежедневными данными, мы получим желаемый результат:

dailyData_expanded_GOOD <- dailyData %>%
  dplyr::right_join( allHours, by='DateTime' ) %>%
  tidyr::fill( dplyr::everything(), .direction='down' )

Вот заголовок:

head(dailyData_expanded_GOOD, n=15)
              DateTime    Value
1  2019-01-01 00:00:00 Daily TS
2  2019-01-01 01:00:00 Daily TS
3  2019-01-01 02:00:00 Daily TS
4  2019-01-01 03:00:00 Daily TS
5  2019-01-01 04:00:00 Daily TS
6  2019-01-01 05:00:00 Daily TS
7  2019-01-01 06:00:00 Daily TS
8  2019-01-01 07:00:00 Daily TS
9  2019-01-01 08:00:00 Daily TS
10 2019-01-01 09:00:00 Daily TS
11 2019-01-01 10:00:00 Daily TS
12 2019-01-01 11:00:00 Daily TS
13 2019-01-01 12:00:00 Daily TS
14 2019-01-01 13:00:00 Daily TS
15 2019-01-01 14:00:00 Daily TS

Почему правильное объединение влияет на полные данные по сравнению с ежедневными данными?

1 Ответ

0 голосов
/ 20 марта 2019

Я думаю, что проблема в том, что вы пытаетесь связать кадры данных слишком рано. Я считаю, что это дает вам то, что вы хотите:

result <- bind_rows(dailyData_expanded_GOOD, hourlyData)
head(result)
#>              DateTime     Name
#> 1 2019-01-01 00:00:00 Daily TS
#> 2 2019-01-01 01:00:00 Daily TS
#> 3 2019-01-01 02:00:00 Daily TS
#> 4 2019-01-01 03:00:00 Daily TS
#> 5 2019-01-01 04:00:00 Daily TS
#> 6 2019-01-01 05:00:00 Daily TS

Причина, по которой right_join() не работает, заключается в том, что allHours идеально соответствует строки в allData для часовых временных рядов. С? Right_join

возвращает все строки из y и все столбцы из x и y. Строки в y без совпадения по x будут иметь значения NA в новых столбцах. Если между x и y есть несколько совпадений, возвращаются все комбинации совпадений.

Вы надеетесь, что строки в x без совпадения по y будут иметь значения NA, но строки в y уже соответствуют строкам в x. На самом деле существует несколько совпадений, одно для ежедневного и одно для часового, но right_join() просто возвращает оба без расширения строк дневного временного ряда.

Это отличается от ситуации в в этом вопросе , где расширяемые даты и время не встречаются в левом фрейме данных. Тогда стратегия слияния расширит ваш результат, как и ожидалось.

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

tidyr::expand()

expandedData <- allData %>% 
  tidyr::expand( DateTime, Name ) %>% 
  dplyr::arrange( Name, DateTime )

Это работает, но только при наличии обоих временных рядов. Если есть только dailyData, то результат не расширяется.

Кухонная раковина

expandedData1 <- allData %>% 
  dplyr::right_join(allHours, by = 'DateTime') %>% 
  tidyr::fill(everything()) %>% 
  tidyr::expand( DateTime, Name) %>% 
  dplyr::arrange( Name, DateTime )

Как указано в комментариях, это работает для всех случаев - оба типа, только ежедневные данные, только почасовые данные. Это решение и следующее поколение предупреждения, если вы не используете stringsAsFactors = FALSE в data.frame() звонки выше.

Единственная проблема с этим решением заключается в том, что fill() и right_join() только там, чтобы иметь дело с крайними случаями. Я не знаю, если это реальная проблема или нет.

«Раскол» в трубе

Простое решение разделяет набор данных, и это можно сделать внутри труба в пару способов.

expandedData2 <- allData %>% 
  tidyr::nest(-Name) %>% 
  mutate(data = purrr::map(data, ~right_join(., allHours, by = 'DateTime'))) %>% 
  tidyr::unnest()

Другой способ будет использовать base::split(), а затем purrr::map_dfr()

Создано в 2019-03-24 по представ пакет (v0.2.0).

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