【问题标题】:Elegant way to sum values by time intervals (whilst accounting for missing values)按时间间隔求和值的优雅方法(同时考虑缺失值)
【发布时间】:2018-04-13 08:25:31
【问题描述】:

我正在尝试这样的事情

df <- data.frame(times = c("0915", "0930", "0945", "1000", "1015", "1030", "1045", "1100", "1130", "1145", "1200"),
                 values = c(1,2,3,4,1,2,3,4,1,3,4))

> df
   times values
1   0915      1
2   0930      2
3   0945      3
4   1000      4
5   1015      1
6   1030      2
7   1045      3
8   1100      4
9   1130      1
10  1145      3
11  1200      4
12  1215      1
13  1245      3
14  1300      4
15  1330      2
16  1345      4

然后把它变成这样的东西

> df2
  times values
1   0930      3
2   1000      7
3   1030      3
4   1100      7
5   1130     NA
6   1200      7
7   1230     NA
8   1300      7
9   1330     NA
10  1400     NA

基本上,获取以 15 分钟为间隔测量的值,并将它们转换为以 30 分钟为间隔测量的值(相加就足够了)。

如果我可以确定每半小时阅读两次 15 分钟的读数,我可以想出一个好的解决方案。我可以成对添加元素并得到我想要的。但我无法在我的数据集中确定这一点。正如我的演示还显示的那样,可能缺少多个连续的值。

所以我认为某种数字识别是必要的,例如识别出时间在 9:15 和 9:30 之间,并将这两者相加。所以我已经创建了一个名为 hr2dec 的函数来将这些时间转换为十进制,所以它看起来像这样

> hr2dec(df$times)
 [1]  9.25  9.50  9.75 10.00 10.25 10.50 10.75 11.00 11.50 11.75 12.00

我提到这一点是为了用十进制而不是 4 位时间更容易解决这个问题。

我也有 24 小时和多天的数据。因此,如果我有一个循环的解决方案,它需要在2400 之后重置为0015,因为这些是每天的第一次和最后一次测量。可以像这样生成包含日期的完整数据集(使用小数表示时间,就像我说的那样,对我来说都可以):

set.seed(42)
full_df <- data.frame(date = rep(as.Date(c("2010-02-02", "2010-02-03")), each = 96),
                      dec_times = seq(0.25,24,0.25),
                      values = rnorm(96)
                      )

full_df <- full_df[-c(2,13,15,19,95,131,192),]

到目前为止,我能想到的最佳解决方案是成对比较循环。但即使这样也不完美。

有什么优雅的方式来做我所追求的吗? IE。检查第一个和最后一个值(就日期和时间而言),并计算每半小时的间隔?我对我的循环不满意...

  1. 检查第一个和最后一个日期时间值以计算出半小时的范围
  2. 按顺序检查项目,一次配对以决定我是否有两个属于该半小时周期的值。
  3. 如果我这样做,则求和,如果我不这样做,则为 NA。

【问题讨论】:

  • 我建议您通过包含所有案例来改进您的可重现示例。例如,您在文中提到的午夜时间段。
  • 好建议@Seymour。我已经删除了 2010-02-02 的 2345 时间段,这样第一天的午夜就会变成 NA。
  • 此外,我并不太关心最后的案例,因为我总是可以通过检查来抵消我从有效数据开始的任何解决方案。

标签: r


【解决方案1】:

您应该查看tibbletime package -- 具体来说,您需要查看collapse_by(),它按时间段折叠tbl_time 对象。

library(tibbletime)
library(dplyr)

# create a series of 7 days
# 2018-01-01 to 2018-01-07 by 15 minute intervals
df <- create_series('2018-01-01' ~ '2018-01-07', period = "15 minute")
df$values <- rnorm(nrow(df))
df
#> # A time tibble: 672 x 2
#> # Index: date
#>    date                 values
#>    <dttm>                <dbl>
#>  1 2018-01-01 00:00:00 -0.365 
#>  2 2018-01-01 00:15:00 -0.275 
#>  3 2018-01-01 00:30:00 -1.50  
#>  4 2018-01-01 00:45:00 -1.64  
#>  5 2018-01-01 01:00:00 -0.341 
#>  6 2018-01-01 01:15:00 -1.05  
#>  7 2018-01-01 01:30:00 -0.544 
#>  8 2018-01-01 01:45:00 -1.10  
#>  9 2018-01-01 02:00:00  0.0824
#> 10 2018-01-01 02:15:00  0.477 
#> # ... with 662 more rows

# Collapse into 30 minute intervals, group, and sum
df %>% 
  collapse_by("30 minute") %>%
  group_by(date) %>%
  summarise(sum_values = sum(values))
#> # A time tibble: 336 x 2
#> # Index: date
#>    date                sum_values
#>    <dttm>                   <dbl>
#>  1 2018-01-01 00:15:00     -0.640
#>  2 2018-01-01 00:45:00     -3.14 
#>  3 2018-01-01 01:15:00     -1.39 
#>  4 2018-01-01 01:45:00     -1.64 
#>  5 2018-01-01 02:15:00      0.559
#>  6 2018-01-01 02:45:00      0.581
#>  7 2018-01-01 03:15:00     -1.50 
#>  8 2018-01-01 03:45:00      1.36 
#>  9 2018-01-01 04:15:00      0.872
#> 10 2018-01-01 04:45:00     -0.835
#> # ... with 326 more rows

# Alternatively, you can use clean = TRUE
df %>% 
  collapse_by("30 minute", clean = TRUE) %>%
  group_by(date) %>%
  summarise(sum_values = sum(values))
#> # A time tibble: 336 x 2
#> # Index: date
#>    date                sum_values
#>    <dttm>                   <dbl>
#>  1 2018-01-01 00:30:00     -0.640
#>  2 2018-01-01 01:00:00     -3.14 
#>  3 2018-01-01 01:30:00     -1.39 
#>  4 2018-01-01 02:00:00     -1.64 
#>  5 2018-01-01 02:30:00      0.559
#>  6 2018-01-01 03:00:00      0.581
#>  7 2018-01-01 03:30:00     -1.50 
#>  8 2018-01-01 04:00:00      1.36 
#>  9 2018-01-01 04:30:00      0.872
#> 10 2018-01-01 05:00:00     -0.835
#> # ... with 326 more rows

如果您更喜欢视频(The Future of Time Series and Financial Analysis in the Tidyverse。

【讨论】:

  • 为什么有些sum_values 是负数,也不是整数作为输入数据?另外,你如何处理午夜之间的时间段?
  • 谢谢你,如果我转换数据类型,这看起来确实是一种优雅的折叠时间方式!你知道这如何处理系列中的缺失值吗?
  • @LachlanO NA 值将返回 NA,除非您修改 sum 行以包含 na.rm = TRUE - 例如,summarise(sum_values = sum(values, na.rm = TRUE)。如果你正在处理“时间”,你会想要转换:-)。如果你已经有一个“时间”字段,你应该可以使用df &lt;- tbl_time(df, index = your_date_field)
  • 原始系列中没有 NA,只有缺失的时间。所以对于这个解决方案,我必须用 NA 填充缺失的值才能工作?顺便说一句,这听起来仍然是一个很好的解决方案,只是想完全理解它:)
  • @LachlanO 如果我正确理解您的担忧,您应该没有任何问题 - 也就是说,日期字段中的任何间隙/缺失/NA 值都将得到妥善处理。最好的确定方法是尝试一下! :) 如果您遇到其他问题,请搜索和/或发布新问题。
【解决方案2】:

我是 OP。玩了一会儿后,我得到了一些我认为比我最初拥有的循环更优雅的解决方案。决定发布作为讨论的答案。仍然不介意更优雅的东西。

使用full_df 我创建了一个索引,这只是我预期的所有 15 分钟周期。

index <- data.frame(date = rep(seq(full_df$date[1], full_df$date[nrow(full_df)],by="+1 day"),each=96),
                    dec_times = rep(seq(0.25,24,0.25), length(unique(full_df$date)))
)

然后我通过两个匹配列将其与 full_df 合并,因此它保留不常见的值(即我的缺失值)

index <- merge(full_df, index, by.y=c("date", "dec_times"), all.y=T)

然后我继续创建一个列,使用 plyrround_any 函数列出每 15 分钟间隔属于哪个半小时

index$half_hour <- plyr::round_any(index$dec_times, 0.5, ceiling)

然后我使用plyrddply 函数根据新的half_hour 列求和(利用任何+ NA 就是NA 的事实)。

df2 <- plyr::ddply(index[,c("half_hour","values")], "half_hour", sum)

我相信生成的数据框正是我所追求的。

> df2
   date       half_hour      values
1  2010-02-02       0.5          NA
2  2010-02-02       1.0  0.99599102
3  2010-02-02       1.5  0.29814381
4  2010-02-02       2.0  1.41686296
5  2010-02-02       2.5  1.95570961
6  2010-02-02       3.0  3.59151505
7  2010-02-02       3.5          NA
8  2010-02-02       4.0          NA
9  2010-02-02       4.5 -2.94070834
10 2010-02-02       5.0          NA
11 2010-02-02       5.5 -2.08794703
12 2010-02-02       6.0  1.04275734
13 2010-02-02       6.5  1.46472433
14 2010-02-02       7.0 -2.02043247
15 2010-02-02       7.5 -0.17989752
16 2010-02-02       8.0  1.16028746
17 2010-02-02       8.5  0.42617715
18 2010-02-02       9.0 -1.21205356
19 2010-02-02       9.5 -1.63536660
20 2010-02-02      10.0 -2.37808504
21 2010-02-02      10.5 -0.15505870
22 2010-02-02      11.0  0.03145841
23 2010-02-02      11.5 -0.93546302
24 2010-02-02      12.0  0.63270809
25 2010-02-02      12.5  0.22420168
26 2010-02-02      13.0 -0.46191368
27 2010-02-02      13.5  2.21862683
28 2010-02-02      14.0  0.36631139
29 2010-02-02      14.5  0.76912170
30 2010-02-02      15.0 -2.70820713
31 2010-02-02      15.5 -0.18200408
32 2010-02-02      16.0  1.98156055
33 2010-02-02      16.5  0.57525057
34 2010-02-02      17.0  1.37435422
35 2010-02-02      17.5  1.64160673
36 2010-02-02      18.0 -1.13330533
37 2010-02-02      18.5 -0.33000520
38 2010-02-02      19.0  0.03816768
39 2010-02-02      19.5  1.23194633
40 2010-02-02      20.0 -1.98555720
41 2010-02-02      20.5  1.77062845
42 2010-02-02      21.0 -0.03245631
43 2010-02-02      21.5 -0.58233200
44 2010-02-02      22.0 -0.39989655
45 2010-02-02      22.5  1.75511944
46 2010-02-02      23.0  0.91594245
47 2010-02-02      23.5  2.04145902
48 2010-02-02      24.0          NA
49 2010-02-03       0.5  0.80626028
50 2010-02-03       1.0  0.99599102
51 2010-02-03       1.5  0.29814381
52 2010-02-03       2.0  1.41686296
53 2010-02-03       2.5  1.95570961
54 2010-02-03       3.0  3.59151505
55 2010-02-03       3.5 -1.66764947
56 2010-02-03       4.0  0.50262906
57 2010-02-03       4.5 -2.94070834
58 2010-02-03       5.0 -1.12035358
59 2010-02-03       5.5 -2.08794703
60 2010-02-03       6.0  1.04275734
61 2010-02-03       6.5  1.46472433
62 2010-02-03       7.0 -2.02043247
63 2010-02-03       7.5 -0.17989752
64 2010-02-03       8.0  1.16028746
65 2010-02-03       8.5  0.42617715
66 2010-02-03       9.0          NA
67 2010-02-03       9.5 -1.63536660
68 2010-02-03      10.0 -2.37808504
69 2010-02-03      10.5 -0.15505870
70 2010-02-03      11.0  0.03145841
71 2010-02-03      11.5 -0.93546302
72 2010-02-03      12.0  0.63270809
73 2010-02-03      12.5  0.22420168
74 2010-02-03      13.0 -0.46191368
75 2010-02-03      13.5  2.21862683
76 2010-02-03      14.0  0.36631139
77 2010-02-03      14.5  0.76912170
78 2010-02-03      15.0 -2.70820713
79 2010-02-03      15.5 -0.18200408
80 2010-02-03      16.0  1.98156055
81 2010-02-03      16.5  0.57525057
82 2010-02-03      17.0  1.37435422
83 2010-02-03      17.5  1.64160673
84 2010-02-03      18.0 -1.13330533
85 2010-02-03      18.5 -0.33000520
86 2010-02-03      19.0  0.03816768
87 2010-02-03      19.5  1.23194633
88 2010-02-03      20.0 -1.98555720
89 2010-02-03      20.5  1.77062845
90 2010-02-03      21.0 -0.03245631
91 2010-02-03      21.5 -0.58233200
92 2010-02-03      22.0 -0.39989655
93 2010-02-03      22.5  1.75511944
94 2010-02-03      23.0  0.91594245
95 2010-02-03      23.5  2.04145902
96 2010-02-03      24.0          NA

我喜欢这个解决方案的地方

  • 没有循环
  • 在数据框内工作

我不喜欢这个解决方案的地方

  • 创建索引时过于笨拙

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    • 2020-02-08
    • 1970-01-01
    • 1970-01-01
    • 2016-11-21
    • 1970-01-01
    • 2012-01-09
    相关资源
    最近更新 更多