时间序列分析工具箱——tibbletime
翻译自《Demo Week: Tidy Time Series Analysis with tibbletime》
原文链接:www.business-science.io/code-tools/2017/10/26/demo_week_tibbletime.html
注意:由于软件包的版本变化,部分代码被修改,文字有删减
时间序列分析工具箱——tibbletime
tibbletime
的用途
- tidy 时间序列分析的未来:基于
tbl
的新类——tbl_time
,为tibble
对象添加时间轴,赋予处理时间的能力。 - 时间序列函数:为
tbl_time
对象专门设计的一系列函数,例如:filter_time()
:根据日期简便快捷地过滤一个tbl_time
对象。as_period()
:转换时间周期(例如月度变为年度),让用户能将数据聚合到低粒度水平上。time_collapse()
:当使用time_collapse
时,tbl_time
对象中落入相同周期的索引将被修改成相同的日期。rollify()
:修改一个函数,使其能够在特定时间区间上计算一个或一组值。可以用来计算滚动均值,或其他tidyverse
框架下的滚动计算。create_series()
:根据规则时间序列,用简化标记快速初始化一个带有date
列tbl_time
对象。
加载包
tibbletime
目前还在活跃开发阶段,可以用常规方法安装,也可以借助 devtools
从 github 上安装最新开发版。
# Get tibbletime version with latest features
devtools::install_github("business-science/tibbletime")
安装完成后,加载下面的包:
tibbletime
:创建带时间轴的tibble
对象,可以使用tbl_time
函数。tidyquant
:加载tidyverse
框架,用tq_get()
获取数据。
# Load libraries
library(tibbletime) # Version: 0.1.1, Future of tidy time series analysis
library(tidyquant) # Loads tidyverse, tq_get()
数据
用 tq_get()
下载 FANG(脸书、亚马逊、网飞、谷歌)每天的股票价格。
# Stock Prices from Yahoo! Finance
FANG_symbols <- c("FB", "AMZN", "NFLX", "GOOG")
FANG_tbl_d <- FANG_symbols %>%
tq_get(
get = "stock.prices",
from = "2014-01-01",
to = "2016-12-31")
FANG_tbl_d <- FANG_tbl_d %>%
group_by(symbol)
FANG_tbl_d
## # A tibble: 3,024 x 8
## # Groups: symbol [4]
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 FB 2014-01-02 54.83 55.22 54.19 54.71 43195500 54.71
## 2 FB 2014-01-03 55.02 55.65 54.53 54.56 38246200 54.56
## 3 FB 2014-01-06 54.42 57.26 54.05 57.20 68852600 57.20
## 4 FB 2014-01-07 57.70 58.55 57.22 57.92 77207400 57.92
## 5 FB 2014-01-08 57.60 58.41 57.23 58.23 56682400 58.23
## 6 FB 2014-01-09 58.65 58.96 56.65 57.22 92253300 57.22
## 7 FB 2014-01-10 57.13 58.30 57.06 57.94 42449500 57.94
## 8 FB 2014-01-13 57.91 58.25 55.38 55.91 63010900 55.91
## 9 FB 2014-01-14 56.46 57.78 56.10 57.74 37503600 57.74
## 10 FB 2014-01-15 57.98 58.57 57.27 57.60 33663400 57.60
## # ... with 3,014 more rows
我们设计了一个函数来按股票代码分块绘图,可以在本文中重复使用。没有必要深究这些代码,只要认识到我们正在创建一个 ggplot2
对象,它通过指定数据框、x、y 和 group(如果存在)等要素来创建根据“symbol”分块的信息图。
# Setup plotting function that can be reused later
ggplot_facet_by_symbol <- function(data,
mapping)
{
if (is.null(mapping$group))
{
# No groups
g <- data %>%
ggplot(
mapping = mapping) +
labs(x = quo_name(mapping$x),
y = quo_name(mapping$y))
}
else
{
# Deal with groups
g <- data %>%
ggplot(
mapping = mapping) +
labs(x = quo_name(mapping$x),
y = quo_name(mapping$y),
group = quo_name(mapping$group))
}
# Add faceting and theme
g <- g +
geom_line() +
facet_wrap(
~ symbol, ncol = 2, scales = "free_y") +
scale_color_tq() +
theme_tq()
return(g)
}
我们可以使用绘图函数 ggplot_facet_by_symbol
快速可视化我们的数据。让我们看一下“除权调整的”股票价格。
# Plot adjusted vs date
FANG_tbl_d %>%
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
labs(
title = "FANG Stocks: Adjusted Prices 2014 through 2016")
上图所显示就是我们要处理的数据,下面让我们进入 tibbletime
的教程。
教程:tibbletime
本教程将介绍下列函数的用法:
filter_time
:对时间索引的过滤as_period
:改变数据的周期rollify
:将任意函数转换成为滚动函数
初始化一个 tbl_time
对象
在我们使用这些新函数之前,我们需要创建一个 tbl_time
对象。新类的操作几乎与普通的 tibble
对象相同。然而,它会在背后自动跟踪时间信息。
使用 as_tbl_time()
函数初始化对象。指定 index = date
,这告诉 tbl_time
对象要跟踪哪个索引。
# Convert to tbl_time
FANG_tbl_time_d <- FANG_tbl_d %>%
as_tbl_time(index = date)
我们可以打印 tbl_time
对象。看起来几乎与分组的 tibble
相同。请注意,“Index: date”通知我们“time tibble”已正确初始化。
# Show the tbl_time object we created
FANG_tbl_time_d
## # A time tibble: 3,024 x 8
## # Index: date
## # Groups: symbol [4]
## symbol date open high low close volume adjusted
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 FB 2014-01-02 54.83 55.22 54.19 54.71 43195500 54.71
## 2 FB 2014-01-03 55.02 55.65 54.53 54.56 38246200 54.56
## 3 FB 2014-01-06 54.42 57.26 54.05 57.20 68852600 57.20
## 4 FB 2014-01-07 57.70 58.55 57.22 57.92 77207400 57.92
## 5 FB 2014-01-08 57.60 58.41 57.23 58.23 56682400 58.23
## 6 FB 2014-01-09 58.65 58.96 56.65 57.22 92253300 57.22
## 7 FB 2014-01-10 57.13 58.30 57.06 57.94 42449500 57.94
## 8 FB 2014-01-13 57.91 58.25 55.38 55.91 63010900 55.91
## 9 FB 2014-01-14 56.46 57.78 56.10 57.74 37503600 57.74
## 10 FB 2014-01-15 57.98 58.57 57.27 57.60 33663400 57.60
## # ... with 3,014 more rows
我们可以使用绘图函数 ggplot_facet_by_symbol()
绘制它,我们看到 tbl_time
对象与 tbl
对象的反应相同。
# Plot the tbl_time object
FANG_tbl_time_d %>%
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
labs(
title = "Working with tbltime: Reacts same as tbl class")
时间序列函数
让我们看看可以用新的 tbl_time
对象做些什么。
filter_time
filter_time()
函数根据按日期简便快捷地过滤 tbl_time
对象,它使用一个函数格式(例如 'date_operator_start' ~ 'date_operator_end'
)。我们使用标准日期格式 YYYY-MM-DD + HH:MM:SS
指定日期运算符,但也有强大的简化标记来更有效地指定日期子集。
假设我们想要过滤出 2014-06-01
和 2014-06-15
之间的所有观察结果。我们可以使用函数标记 filter_time('2014-06-01' ~ '2014-06-15')
来完成。
# filter_time by day
FANG_tbl_time_d %>%
filter_time('2014-06-01' ~ '2014-06-15') %>%
# Plotting
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
geom_point() +
labs(
title = "Time Filter: Use functional notation to quickly subset by time",
subtitle = "2014-06-01 ~ 2014-06-15")
我们可以按月完成同样的工作。假设我们只想在 2014 年 3 月进行观察。使用简化函数标记 ~ '2014-03'
。
# filter_time by month
FANG_tbl_time_d %>%
filter_time(~ '2014-03') %>%
# Plotting
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
geom_point() +
labs(
title = "Time Filter: Use shorthand for even easier subsetting",
subtitle = "~ 2014-03")
tbl_time
对象也响应括号符号运算符——[
。在这里,我们提取 2014 年所有日期的数据。
# time filter bracket [] notation
FANG_tbl_time_d[~ '2014'] %>%
# Plotting
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
labs(
title = "Time Filter: Bracket Notation Works Too",
subtitle = "FANG_tbl_time_d[~ 2014]")
filter_time()
有许多功能和简化标记,感兴趣的读者可以查看 filter_time vignette 和 filter_time function documentation。
as_period
函数 as_period()
可以改变 tbl_time
对象的周期。与传统方法相比,使用此方法有两个优点:
- 函数标记非常灵活:
yearly == y == 1 y
- 函数标记提供了无数周期转换的可能,例如:
15 d
:以 15 天为一周期2 m
:以 2 月为一周期4 m
:以 4 月为一周期6 m
:以半年为一周期
首先,让我们做一个简单的月度周期性变化。
# Convert from daily to monthly periodicity
FANG_tbl_time_d %>%
as_period(period = "month") %>%
# Plotting
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
labs(
title = "Periodicity Change from Daily to Monthly") +
geom_point()
让我们提升一个档次。那么每两个月一次呢? 只需使用函数标记 2 m
即可。
# Convert from daily to bi-monthly periodicity
FANG_tbl_time_d %>%
as_period(period = '2 m') %>%
# Plotting
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
labs(
title = "Periodicity Change to Daily to Bi-Monthly",
subtitle = "2~m") +
geom_point()
让我们继续。那么每半年一次呢? 只需使用 6 m
即可。
# Convert from daily to bi-annually periodicity
FANG_tbl_time_d %>%
as_period(period = '6 m') %>%
# Plotting
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
labs(
title = "Periodicity Change to Daily to Bi-Annually",
subtitle = "6~m") +
geom_point()
函数标记几乎提供了无限可能,感兴趣的话可以查看 vignette on periodicity change with tibbletime。
rollify
rollify()
函数是一个副词(tidyverse
中的一种特殊类型的函数,用于修改另一个函数)。rollify()
的作用是将任何函数转换为自身的滚动版本。
# Rolling 60-day mean
roll_mean_60 <- rollify(
mean, window = 60)
FANG_tbl_time_d %>%
mutate(
mean_60 = roll_mean_60(adjusted)) %>%
select(-c(open:volume)) %>%
# Plot
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = adjusted, color = symbol)) +
geom_line(
aes(y = mean_60),
color = palette_light()[[6]]) +
labs(
title = "Rolling 60-Day Mean with rollify")
我们甚至可以做出更复杂的滚动功能,例如相关性。我们在 rollify()
中使用函数形式 .f = ~fun(.x,.y,...)
。
# Rolling correlation
roll_corr_60 <- rollify(
~ cor(.x, .y, use = "pairwise.complete.obs"),
window = 60)
FANG_tbl_time_d %>%
mutate(
cor_60 = roll_corr_60(
open, close)) %>%
select(-c(open:adjusted)) %>%
# Plot
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = cor_60, color = symbol)) +
labs(
title = "Rollify: 60-Day Rolling Correlation Between Open and Close Prices")
我们甚至可以返回多个结果。例如,我们可以创建滚动分位数。
首先,创建一个返回分位数的函数。
# Quantile tbl function
quantile_tbl <- function(x)
{
q <- quantile(x)
tibble(
quantile_name = names(q),
quantile_value = q)
}
# Test the function
quantile_tbl(1:100)
## # A tibble: 5 x 2
## quantile_name quantile_value
## <chr> <dbl>
## 1 0% 1.00
## 2 25% 25.75
## 3 50% 50.50
## 4 75% 75.25
## 5 100% 100.00
很好,它可以工作。接下来,使用 rollify
创建滚动版本。我们设置 unlist = FALSE
来返回列表列。
# Rollified quantile function
roll_quantile_60 <- rollify(
quantile_tbl, window = 60, unlist = FALSE)
接下来,在 mutate()
中应用滚动分位数函数来获得滚动分位数。确保你已经用 select()
、filter()
和 unnest()
删除了不必要的列,过滤了 NA
值,并展开列表列。现在每个日期有五个分位数值。
# Apply rolling quantile
FANG_quantile_60 <- FANG_tbl_time_d %>%
mutate(
rolling_quantile = roll_quantile_60(adjusted)) %>%
select(-c(open:adjusted)) %>%
filter(!is.na(rolling_quantile)) %>%
unnest()
FANG_quantile_60
## # A time tibble: 13,940 x 4
## # Index: date
## # Groups: symbol [4]
## symbol date quantile_name quantile_value
## * <chr> <date> <chr> <dbl>
## 1 FB 2014-03-28 0% 53.5300
## 2 FB 2014-03-28 25% 57.8750
## 3 FB 2014-03-28 50% 64.2100
## 4 FB 2014-03-28 75% 68.6275
## 5 FB 2014-03-28 100% 72.0300
## 6 FB 2014-03-31 0% 53.5300
## 7 FB 2014-03-31 25% 57.9350
## 8 FB 2014-03-31 50% 64.2100
## 9 FB 2014-03-31 75% 68.6275
## 10 FB 2014-03-31 100% 72.0300
## # ... with 13,930 more rows
最后,画出结果。
FANG_quantile_60 %>%
ggplot_facet_by_symbol(
mapping = aes(
x = date, y = quantile_value,
color = symbol, group = quantile_name)) +
labs(
title = "Rollify: Create Rolling Quantiles")
如果想继续探索 rollify
的用法,可以查看 vignette on rolling functions with rollify。