【问题标题】:Unexpected dplyr::right_join() behavior for expanding POSIXct time series扩展 POSIXct 时间序列的意外 dplyr::right_join() 行为
【发布时间】:2019-03-20 04:33:14
【问题描述】:

我有一个数据框,其中包含一些在每天午夜标记的每日数据和一些在全天每个小时开始时标记的每小时数据。我想扩展数据,所以它都是每小时一次,我想在一个 tidyverse“管道链”中这样做。

我的想法是创建一个包含完整的每小时时间序列的数据框,然后针对该时间序列创建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

现在,我认为 POSIXct 值与 allData$DateTime 的完整小时序列中的 dplyr::right_join() 会扩展每日时间序列,从而使数据中未明确存在的任何时间的 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() 扩展。也就是说,allData 中缺少的allHours 中的小时数没有保留在结果中,我认为这是使用右连接的全部目的。这是结果的头部:

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

与仅对每日数据相比,为什么正确的联接对完整数据执行不同的操作?

【问题讨论】:

  • 您的示例显示了length(setdiff(allHours$Datetime , allData$DateTime))# [1] 0
  • @akrun 是这样,因为每小时时间序列已经包含了所有这些时间。然而,每日时间序列没有。是否满足此条件导致连接短路?我不希望right_join 对每小时数据做任何事情,但我希望它能够扩展每日数据。关于如何以整洁的方式扩展数据的任何想法?
  • 你可以检查expandcomplete,但是它只能在一个by中运行,否则必须拆分数据并单独执行
  • expand 的问题在于它在allData 仅包含每日值的边缘情况下不起作用。我认为expandedData &lt;- dailyData %&gt;% right_join( allHours, by='DateTime' ) %&gt;% tidyr::fill( everything() ) %&gt;% expand( DateTime, Name ) %&gt;% dplyr::arrange( Name, DateTime ) 可能是处理 allData 可能包含小时数据、同时小时和每日数据或的所有情况的最简洁方法i> 每日数据。对于所有这三种情况,此代码都会生成所需的扩展每小时数据集。
  • 如果您期望输出约束,或者如果您选择的查询表达式依赖于输入约束,那么您需要描述输入约束。 PS 了解什么是左/右连接返回:行内连接加上由空值扩展的不匹配的左/右表行。始终知道作为外部联接的一部分您想要什么内部联接。 PS 较小的示例所需的输出会很好。 PS请通过编辑而不是cmets来澄清。

标签: r join dplyr time-series


【解决方案1】:

我认为问题在于您试图将数据框绑定在一起太快了。我相信这会给你你想要的:

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() 只是返回两者而不扩展每日时间序列行。

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

这就解释了为什么裸 right_join() 不起作用,但不能解决 问题是因为您必须手动拆分数据,这将 如果有不同数量的时间序列,就会快速变老。 cmets 中有几个解决方案,然后我将在下面添加一个附加解决方案。

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 )

正如 cmets 中所指出的,这适用于所有情况 - 两种类型, 只有每日数据,只有每小时数据。这个解决方案和下一个生成 警告,除非您在 data.frame() 中使用 stringsAsFactors = FALSE 上面的电话。

此解决方案的唯一问题是 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()

reprex package (v0.2.0) 于 2019 年 3 月 24 日创建。

【讨论】:

  • 您将allHours 绑定到dailyData_expanded_GOOD 并没有完全实现我所追求的,因为它在每小时数据的Name 列中缺少适当的值。 rbind( dailyData_expanded_GOOD, hourlyData) 满足了我真正想要的。但是,我试图找到一种矢量化且紧凑的方法来实现这一点,而无需先拆分数据。看起来expand 可以解决问题,但仅限于同时存在每日和每小时数据的情况。如果只有每日数据,我需要先right_join all hours,然后再展开。
猜你喜欢
  • 2017-05-03
  • 2019-09-01
  • 1970-01-01
  • 2015-04-03
  • 1970-01-01
  • 2020-09-20
  • 2017-03-30
  • 2023-03-11
  • 2020-12-07
相关资源
最近更新 更多