如何为动物园对象的自定义聚合创建索引
How to create index for custom aggregation on zoo object
我正在努力寻找一种方法来将动物园对象汇总到每周结果,每周测量结果之间存在差距。这是在结果上使用 diff
和其他函数(例如 acf
)。
library(zoo)
library(xts)
我正在用我的一小部分数据创建一个动物园对象:
time_data <- structure(list(day = structure(c(14246, 14247, 14248, 14249, 14250, 14277, 14278, 14279, 14280, 14281, 14305, 14306, 14307, 14308, 14309), class = "Date"), n_daily = c(10L, 15L, 2L, 15L, 6L, 4L, 6L, 8L, 6L, 1L, 20L, 5L, 8L, 9L, 4L)), row.names = c(NA, -15L), class = c("tbl_df", "tbl", "data.frame"))
z_td <- read.zoo(time_data)
现在,我想按周汇总。我可以使用 xts
:
td_week_xts <- apply.weekly(z_td, sum)
td_week_xts
#> 2009-01-04 2009-01-06 2009-02-06 2009-03-06
#> 27 21 25 46
以某种方式调用 diff
在这里没有任何意义,因为测量中存在间隙。结果应包括 "empty weeks"。
diff(td_week_xts)
#> 2009-01-06 2009-02-06 2009-03-06
#> -6 4 21
此外,当您想定义一周的开始时,apply.weekly
不是很灵活(至少我没有看到这个选项)。它切断了最后一周。因此,我决定尝试使用我自己的函数进行聚合 weekly
:
weekly <- function(x, week_end = 'sunday') {
days.of.week <- tolower(weekdays(as.Date(3,"1970-01-01",tz="GMT") + 0:6))
index = which(days.of.week == week_end)-1
7 * ceiling(as.numeric(x - index + 4)/7) + zoo::as.Date(index - 4)
}
td_week <- as.zooreg(aggregate(z_td, by = weekly, sum), freq= 52)
td_week
#> 2009-01-04 2009-01-11 2009-02-08 2009-03-08
#> 27 21 25 46
当然还有差距,但现在实际上包含了整周,而且我还可以定义一周应该从哪一天开始。我现在可以制作一个 "strictly regular" 动物园对象:
td_week_strictreg <- as.zooreg(merge(td_week, zoo(, seq(min(time(td_week)), max(time(td_week)), 7)), fill = 0))
td_week_strictreg
#> 2009-01-04 2009-01-11 2009-01-18 2009-01-25 2009-02-01 2009-02-08
#> 27 21 0 0 0 25
#> 2009-02-15 2009-02-22 2009-03-01 2009-03-08
#> 0 0 0 46
diff(td_week)
或 diff(td_week_strictreg)
给出相同的结果:
#> Data:
#> integer(0)
#>
#> Index:
#> Date of length 0
我认为问题在于时间序列参数是如何在 zoo/xts 对象中设置的,例如xts
对象的频率是 1:
frequency(td_week_xts)
#> [1] 1
frequency(td_week)
#> [1] 52
或者它在于索引:(这里以 zoo::as.yearmon
聚合为例,它构成了一个真正的索引,而不是我的自定义函数...
td_month <- as.zooreg(aggregate(z_td, by = as.yearmon, sum), freq= 12)
str(td_month)
#> 'zooreg' series from Jan 2009 to Mar 2009
#> Data: int [1:3] 48 25 46
#> Index: 'yearmon' num [1:3] Jan 2009 Feb 2009 Mar 2009
#> Frequency: 12
str(td_week)
#> 'zooreg' series from 2009-01-04 to 2009-03-08
#> Data: int [1:4] 27 21 25 46
#> Index: Date[1:4], format: "2009-01-04" "2009-01-11" "2009-02-08" "2009-03-08"
#> Frequency: 52
由 reprex package (v0.2.1)
于 2019-04-02 创建
很抱歉这个超长的问题,我知道这不是很好,但我不知道如何更简洁。
我的方法和小函数得到了很多帮助from this fabulous answer
我不确定我是否完全理解你想要做什么,但也许首先用零填充缺失的日期会起作用吗?
time_all_possibilities = data.frame(
day = seq(ymd("2009-01-02"), ymd("2009-03-06"), by = "days"))
time_data = merge(time_data, time_all_possibilities, by = "day", all = T)
time_data$n_daily[is.na(time_data$n_daily)] = 0
将td_week
转换为规则间隔的系列,然后使用diff.xts:
m <- as.xts(merge(td_week, zoo(, seq(start(td_week), end(td_week), 7)), fill = 0))
diff(m)
给予:
x
2009-01-04 NA
2009-01-11 -6
2009-01-18 -21
2009-01-25 0
2009-02-01 0
2009-02-08 25
2009-02-15 -25
2009-02-22 0
2009-03-01 0
2009-03-08 46
原则上你设置 td_week_strictreg
的方式是正确的方法(类似于 @G.Grothendieck 对 xts
所做的)但是 frequency = 52
是不正确的并且混乱事情了。
首先是简单的事情:只需用 as.zoo()
去除 frequency
然后你得到与 xts
相同的结果 - 除了 NA
填充:
td_week_zoo <- as.zoo(td_week_strictreg)
class(td_week_zoo)
## [1] "zoo"
diff(td_week_zoo)
## 2009-01-11 2009-01-18 2009-01-25 2009-02-01 2009-02-08 2009-02-15 2009-02-22
## -6 -21 0 0 25 -25 0
## 2009-03-01 2009-03-08
## 0 46
使用 zooreg
而不是 zoo
没有任何问题,但您需要使用与基础数字时间索引相对应的正确 frequency
。当您使用 daily(而不是 annual)时间索引时,delta 是 7 而不是 1/52!并且频率是 delta 的倒数,即这里的 1/7:
frequency(td_week_zoo) <- 1/7
class(td_week_zoo)
## [1] "zooreg" "zoo"
diff(td_week_zoo)
## 2009-01-11 2009-01-18 2009-01-25 2009-02-01 2009-02-08 2009-02-15 2009-02-22
## -6 -21 0 0 25 -25 0
## 2009-03-01 2009-03-08
## 0 46
如果你想使用一个时间索引,其中 1/52 的步长将你带到下一周,1 的步长将你带到下一年,你需要这样做:
td_week_zooreg2 <- zooreg(coredata(td_week_zoo), start = 2009, frequency = 52)
time(td_week_zooreg2)
## [1] 2009.000 2009.019 2009.038 2009.058 2009.077 2009.096 2009.115 2009.135
## [9] 2009.154 2009.173
diff(td_week_zooreg2)
## 2009(2) 2009(3) 2009(4) 2009(5) 2009(6) 2009(7) 2009(8) 2009(9)
## -6 -21 0 0 25 -25 0 0
## 2009(10)
## 46
原则上,也可以编写专用的 yearweek
class,您可以在其中将每周与一周中的特定日期(例如星期日)和相应的日期相关联。我认为没有人写出这样的 class 的原因(据我所知)是因为一年中并不总是恰好有 52 个星期日。
而您的 td_week_strictreg
没有导致错误的原因是 zooreg
只是检查 52 的频率是否可能。它是:您可以每 1/52 天(大约 27.7 分钟)进行一次观察。然后当你取 diff()
时,它想要取观察值与 27.7 分钟前的相应观察值之间的差异。但由于后者不存在,您只会得到 NA,而这些 NA 会被丢弃,从而导致一个空对象。
我正在努力寻找一种方法来将动物园对象汇总到每周结果,每周测量结果之间存在差距。这是在结果上使用 diff
和其他函数(例如 acf
)。
library(zoo)
library(xts)
我正在用我的一小部分数据创建一个动物园对象:
time_data <- structure(list(day = structure(c(14246, 14247, 14248, 14249, 14250, 14277, 14278, 14279, 14280, 14281, 14305, 14306, 14307, 14308, 14309), class = "Date"), n_daily = c(10L, 15L, 2L, 15L, 6L, 4L, 6L, 8L, 6L, 1L, 20L, 5L, 8L, 9L, 4L)), row.names = c(NA, -15L), class = c("tbl_df", "tbl", "data.frame"))
z_td <- read.zoo(time_data)
现在,我想按周汇总。我可以使用 xts
:
td_week_xts <- apply.weekly(z_td, sum)
td_week_xts
#> 2009-01-04 2009-01-06 2009-02-06 2009-03-06
#> 27 21 25 46
以某种方式调用 diff
在这里没有任何意义,因为测量中存在间隙。结果应包括 "empty weeks"。
diff(td_week_xts)
#> 2009-01-06 2009-02-06 2009-03-06
#> -6 4 21
此外,当您想定义一周的开始时,apply.weekly
不是很灵活(至少我没有看到这个选项)。它切断了最后一周。因此,我决定尝试使用我自己的函数进行聚合 weekly
:
weekly <- function(x, week_end = 'sunday') {
days.of.week <- tolower(weekdays(as.Date(3,"1970-01-01",tz="GMT") + 0:6))
index = which(days.of.week == week_end)-1
7 * ceiling(as.numeric(x - index + 4)/7) + zoo::as.Date(index - 4)
}
td_week <- as.zooreg(aggregate(z_td, by = weekly, sum), freq= 52)
td_week
#> 2009-01-04 2009-01-11 2009-02-08 2009-03-08
#> 27 21 25 46
当然还有差距,但现在实际上包含了整周,而且我还可以定义一周应该从哪一天开始。我现在可以制作一个 "strictly regular" 动物园对象:
td_week_strictreg <- as.zooreg(merge(td_week, zoo(, seq(min(time(td_week)), max(time(td_week)), 7)), fill = 0))
td_week_strictreg
#> 2009-01-04 2009-01-11 2009-01-18 2009-01-25 2009-02-01 2009-02-08
#> 27 21 0 0 0 25
#> 2009-02-15 2009-02-22 2009-03-01 2009-03-08
#> 0 0 0 46
diff(td_week)
或 diff(td_week_strictreg)
给出相同的结果:
#> Data:
#> integer(0)
#>
#> Index:
#> Date of length 0
我认为问题在于时间序列参数是如何在 zoo/xts 对象中设置的,例如xts
对象的频率是 1:
frequency(td_week_xts)
#> [1] 1
frequency(td_week)
#> [1] 52
或者它在于索引:(这里以 zoo::as.yearmon
聚合为例,它构成了一个真正的索引,而不是我的自定义函数...
td_month <- as.zooreg(aggregate(z_td, by = as.yearmon, sum), freq= 12)
str(td_month)
#> 'zooreg' series from Jan 2009 to Mar 2009
#> Data: int [1:3] 48 25 46
#> Index: 'yearmon' num [1:3] Jan 2009 Feb 2009 Mar 2009
#> Frequency: 12
str(td_week)
#> 'zooreg' series from 2009-01-04 to 2009-03-08
#> Data: int [1:4] 27 21 25 46
#> Index: Date[1:4], format: "2009-01-04" "2009-01-11" "2009-02-08" "2009-03-08"
#> Frequency: 52
由 reprex package (v0.2.1)
于 2019-04-02 创建很抱歉这个超长的问题,我知道这不是很好,但我不知道如何更简洁。
我的方法和小函数得到了很多帮助from this fabulous answer
我不确定我是否完全理解你想要做什么,但也许首先用零填充缺失的日期会起作用吗?
time_all_possibilities = data.frame(
day = seq(ymd("2009-01-02"), ymd("2009-03-06"), by = "days"))
time_data = merge(time_data, time_all_possibilities, by = "day", all = T)
time_data$n_daily[is.na(time_data$n_daily)] = 0
将td_week
转换为规则间隔的系列,然后使用diff.xts:
m <- as.xts(merge(td_week, zoo(, seq(start(td_week), end(td_week), 7)), fill = 0))
diff(m)
给予:
x
2009-01-04 NA
2009-01-11 -6
2009-01-18 -21
2009-01-25 0
2009-02-01 0
2009-02-08 25
2009-02-15 -25
2009-02-22 0
2009-03-01 0
2009-03-08 46
原则上你设置 td_week_strictreg
的方式是正确的方法(类似于 @G.Grothendieck 对 xts
所做的)但是 frequency = 52
是不正确的并且混乱事情了。
首先是简单的事情:只需用 as.zoo()
去除 frequency
然后你得到与 xts
相同的结果 - 除了 NA
填充:
td_week_zoo <- as.zoo(td_week_strictreg)
class(td_week_zoo)
## [1] "zoo"
diff(td_week_zoo)
## 2009-01-11 2009-01-18 2009-01-25 2009-02-01 2009-02-08 2009-02-15 2009-02-22
## -6 -21 0 0 25 -25 0
## 2009-03-01 2009-03-08
## 0 46
使用 zooreg
而不是 zoo
没有任何问题,但您需要使用与基础数字时间索引相对应的正确 frequency
。当您使用 daily(而不是 annual)时间索引时,delta 是 7 而不是 1/52!并且频率是 delta 的倒数,即这里的 1/7:
frequency(td_week_zoo) <- 1/7
class(td_week_zoo)
## [1] "zooreg" "zoo"
diff(td_week_zoo)
## 2009-01-11 2009-01-18 2009-01-25 2009-02-01 2009-02-08 2009-02-15 2009-02-22
## -6 -21 0 0 25 -25 0
## 2009-03-01 2009-03-08
## 0 46
如果你想使用一个时间索引,其中 1/52 的步长将你带到下一周,1 的步长将你带到下一年,你需要这样做:
td_week_zooreg2 <- zooreg(coredata(td_week_zoo), start = 2009, frequency = 52)
time(td_week_zooreg2)
## [1] 2009.000 2009.019 2009.038 2009.058 2009.077 2009.096 2009.115 2009.135
## [9] 2009.154 2009.173
diff(td_week_zooreg2)
## 2009(2) 2009(3) 2009(4) 2009(5) 2009(6) 2009(7) 2009(8) 2009(9)
## -6 -21 0 0 25 -25 0 0
## 2009(10)
## 46
原则上,也可以编写专用的 yearweek
class,您可以在其中将每周与一周中的特定日期(例如星期日)和相应的日期相关联。我认为没有人写出这样的 class 的原因(据我所知)是因为一年中并不总是恰好有 52 个星期日。
而您的 td_week_strictreg
没有导致错误的原因是 zooreg
只是检查 52 的频率是否可能。它是:您可以每 1/52 天(大约 27.7 分钟)进行一次观察。然后当你取 diff()
时,它想要取观察值与 27.7 分钟前的相应观察值之间的差异。但由于后者不存在,您只会得到 NA,而这些 NA 会被丢弃,从而导致一个空对象。