扩展POSIXct时间序列的意外dplyr :: right_join()行为

时间:2019-03-20 04:33:15

标签: r join dplyr time-series

我有一个数据框,其中包含每天在午夜加上时间戳的一些每日数据,并在一天中的每个小时的开始加上时间戳的一些小时数据。我想扩展数据,使它全部每小时进行一次,我想在一个整齐的“管道链”中进行扩展。

我的想法是创建一个包含完整的每小时时间序列的数据框,然后根据该时间序列dplyr::right_join()收集我的数据。我认为这将在每日数据匹配的地方(午夜)填充适当的值,在没有匹配的地方(午夜以外的任何时间)填充NA。这似乎仅在我的数据中的时间序列仅是每天而不是每天和每小时的值混合时才起作用,这是意外的。为什么正确的联接在一个数据帧中与另一个小时时间序列共存时,却不能扩展每日时间序列?

我在下面生成了一个最小的示例。我要扩展的代表性数据集名为allData,其中包含来自两个不同时间序列变量Daily TSHourly 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会扩大每日时间序列,而在数据。然后,我可以使用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()尚未扩展每日数据。也就是说,allHoursallData中缺少的小时数未保留在结果中,我认为这是使用右联接的全部目的。这是结果的标题:

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 :(得分:0)

我认为问题是您试图过早地将数据帧绑定在一起。我相信这会给您您想要的东西:

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中的不匹配项匹配,但y中的行确实已经与x中的行匹配。实际上有多个匹配项,每天一次,每小时一次,但是right_join()仅返回两个匹配项,而没有扩展每天的时间序列行。

这与this question中的情况不同,在reprex package中,要扩展的日期时间没有出现在左侧数据框中。然后,合并策略将按预期扩展您的结果。

所以这解释了为什么裸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()

https://www.ampproject.org/docs/reference/components/amp-animation(v0.2.0)于2019-03-24创建。