"Spread" na.locf数据为不规则时间数据

"Spread" na.locf data for irregular time data

我有如下数据:

data <- tibble(time = c(ymd_hms("2019-11-01 09:33:00"),
                        ymd_hms("2019-11-01 09:35:00"),
                        ymd_hms("2019-11-01 09:40:00"),
                        ymd_hms("2019-11-01 09:52:00")),
               data = c(1250, 900, 4000, 9000))
data
##  A tibble: 4 x 2
#   time                 data
#   <dttm>              <dbl>
# 1 2019-11-01 09:33:00  1250
# 2 2019-11-01 09:35:00   900
# 3 2019-11-01 09:40:00  4000
# 4 2019-11-01 09:52:00  9000

我希望在第一次和最后一次观察之间的每一分钟对 data 列进行重新采样,并且我希望 data 的值成为下一个非 NAN 值除以 1 +从先前的非 nan 值到下一个非 nan 值的 nan 值的数量(即 data 的值是 "spread" 从其给定样本点到先前给定样本点的分钟数。

例如,在这种情况下,我希望得到以下结果

> result
# A tibble: 20 x 2
   time                 data
   <dttm>              <dbl>
 1 2019-11-01 09:33:00  1250
 2 2019-11-01 09:34:00   450
 3 2019-11-01 09:35:00   450
 4 2019-11-01 09:36:00   800
 5 2019-11-01 09:37:00   800
 6 2019-11-01 09:38:00   800
 7 2019-11-01 09:39:00   800
 8 2019-11-01 09:40:00   800
 9 2019-11-01 09:41:00   750
10 2019-11-01 09:42:00   750
11 2019-11-01 09:43:00   750
12 2019-11-01 09:44:00   750
13 2019-11-01 09:45:00   750
14 2019-11-01 09:46:00   750
15 2019-11-01 09:47:00   750
16 2019-11-01 09:48:00   750
17 2019-11-01 09:49:00   750
18 2019-11-01 09:50:00   750
19 2019-11-01 09:51:00   750
20 2019-11-01 09:52:00   750

我该怎么做?


我在 Zoo 中看到如何使用 na.locf 来做 几乎 我想要的,但我不知道如何将这个 "spreading"数据而不是仅仅填充最后一个值或进行线性插值。

我也尝试过使用 xts 并使用一些自定义逻辑合并两个系列(其中一个是不规则日期),但这对我来说很有挑战性。

我们可以使用 dplyrjoin 具有所有时间步长的数据帧。

然后我们可以使用 tidyrfill 向上,最后除以每组中的记录数(即缺少的时间步数 + 1)

library(dplyr)
library(lubridate)
library(tidyr)
data <- tibble(time = c(ymd_hms("2019-11-01 09:33:00"),
                        ymd_hms("2019-11-01 09:35:00"),
                        ymd_hms("2019-11-01 09:40:00"),
                        ymd_hms("2019-11-01 09:52:00")),
               data = c(1250, 900, 4000, 9000))
tibble(time = seq.POSIXt(from = min(data$time),
                         to = max(data$time), by="min")) %>%
  left_join(., data, by="time") %>% 
  group_by(id = cumsum(is.na(data) & !is.na(lag(data)))) %>% 
  fill(data, .direction = "up") %>% 
  mutate(data = data/ n())
#> # A tibble: 20 x 3
#> # Groups:   id [4]
#>    time                 data    id
#>    <dttm>              <dbl> <int>
#>  1 2019-11-01 09:33:00  1250     0
#>  2 2019-11-01 09:34:00   450     1
#>  3 2019-11-01 09:35:00   450     1
#>  4 2019-11-01 09:36:00   800     2
#>  5 2019-11-01 09:37:00   800     2
#>  6 2019-11-01 09:38:00   800     2
#>  7 2019-11-01 09:39:00   800     2
#>  8 2019-11-01 09:40:00   800     2
#>  9 2019-11-01 09:41:00   750     3
#> 10 2019-11-01 09:42:00   750     3
#> 11 2019-11-01 09:43:00   750     3
#> 12 2019-11-01 09:44:00   750     3
#> 13 2019-11-01 09:45:00   750     3
#> 14 2019-11-01 09:46:00   750     3
#> 15 2019-11-01 09:47:00   750     3
#> 16 2019-11-01 09:48:00   750     3
#> 17 2019-11-01 09:49:00   750     3
#> 18 2019-11-01 09:50:00   750     3
#> 19 2019-11-01 09:51:00   750     3
#> 20 2019-11-01 09:52:00   750     3

1) zoo 转换为 zoo 对象 z,使用 merge 插入 NA,然后将组 g 定义为连续位置除了组中的最后一个之外,它们都是 NA。然后计算所需的比率并使用 fortify.zoo 转换为数据框。如果动物园系列结果没问题,最后一行可以省略。

library(zoo)

z <- read.zoo(data)
m <- merge(z, zoo(, seq(start(z), end(z), 60)))
g <- head(c(0, cumsum(!is.na(m))), -1)
data2 <- na.locf0(m, fromLast = TRUE) /  ave(m, g, FUN = length)
fortify.zoo(data2)

给予:

                 Index data2
1  2019-11-01 09:33:00  1250
2  2019-11-01 09:34:00   450
3  2019-11-01 09:35:00   450
4  2019-11-01 09:36:00   800
5  2019-11-01 09:37:00   800
6  2019-11-01 09:38:00   800
7  2019-11-01 09:39:00   800
8  2019-11-01 09:40:00   800
9  2019-11-01 09:41:00   750
10 2019-11-01 09:42:00   750
11 2019-11-01 09:43:00   750
12 2019-11-01 09:44:00   750
13 2019-11-01 09:45:00   750
14 2019-11-01 09:46:00   750
15 2019-11-01 09:47:00   750
16 2019-11-01 09:48:00   750
17 2019-11-01 09:49:00   750
18 2019-11-01 09:50:00   750
19 2019-11-01 09:51:00   750
20 2019-11-01 09:52:00   750

2) base 这是一个更短的基本解决方案。我们定义了一个函数 ratiofun,它为一组给定其长度(以分钟为单位)和右端点的值生成数据。然后展开时间,应用函数。

ratiofun <- function(minutes, data) rep(data/minutes, minutes)
with(data, data.frame(time = seq(min(time), max(time), 60),
  data = unlist(mapply(ratiofun, c(1, diff(time)), data))))

给予:

                  time data
1  2019-11-01 09:33:00 1250
2  2019-11-01 09:34:00  450
3  2019-11-01 09:35:00  450
4  2019-11-01 09:36:00  800
5  2019-11-01 09:37:00  800
6  2019-11-01 09:38:00  800
7  2019-11-01 09:39:00  800
8  2019-11-01 09:40:00  800
9  2019-11-01 09:41:00  750
10 2019-11-01 09:42:00  750
11 2019-11-01 09:43:00  750
12 2019-11-01 09:44:00  750
13 2019-11-01 09:45:00  750
14 2019-11-01 09:46:00  750
15 2019-11-01 09:47:00  750
16 2019-11-01 09:48:00  750
17 2019-11-01 09:49:00  750
18 2019-11-01 09:50:00  750
19 2019-11-01 09:51:00  750
20 2019-11-01 09:52:00  750