slidify
returns a rolling (sliding) version of the input function, with a
rolling (sliding) .period
specified by the user.
Usage
slidify(
.f,
.period = 1,
.align = c("center", "left", "right"),
.partial = FALSE,
.unlist = TRUE
)
Arguments
- .f
A function, formula, or vector (not necessarily atomic).
If a function, it is used as is.
If a formula, e.g.
~ .x + 2
, it is converted to a function. There are three ways to refer to the arguments:For a single argument function, use
.
For a two argument function, use
.x
and.y
For more arguments, use
..1
,..2
,..3
etc
This syntax allows you to create very compact anonymous functions. Note that formula functions conceptually take dots (that's why you can use
..1
etc). They silently ignore additional arguments that are not used in the formula expression.If character vector, numeric vector, or list, it is converted to an extractor function. Character vectors index by name and numeric vectors index by position; use a list to index by position and name at different levels. If a component is not present, the value of
.default
will be returned.- .period
The period size to roll over
- .align
One of "center", "left" or "right".
- .partial
Should the moving window be allowed to return partial (incomplete) windows instead of
NA
values. Set toFALSE
by default, but can be switched toTRUE
to removeNA
's.- .unlist
If the function returns a single value each time it is called, use
.unlist = TRUE
. If the function returns more than one value, or a more complicated object (like a linear model), use.unlist = FALSE
to create a list-column of the rolling results.
Details
The slidify()
function is almost identical to tibbletime::rollify()
with 3 improvements:
Alignment ("center", "left", "right")
Partial windows are allowed
Uses
slider
under the hood, which improves speed and reliability by implementing code at C++ level
Make any function a Sliding (Rolling) Function
slidify()
turns a function into a sliding version
of itself for use inside of a call to dplyr::mutate()
, however it works
equally as well when called from purrr::map()
.
Because of it's intended use with dplyr::mutate()
, slidify
creates a function that always returns output with the same length of the
input
Alignment
Rolling / Sliding functions generate .period - 1
fewer values than the incoming vector.
Thus, the vector needs to be aligned. Alignment of the vector follows 3 types:
center (default):
NA
or.partial
values are divided and added to the beginning and end of the series to "Center" the moving average. This is common in Time Series applications (e.g. denoising).left:
NA
or.partial
values are added to the end to shift the series to the Left.right:
NA
or.partial
values are added to the beginning to shift the series to the Right. This is common in Financial Applications (e.g moving average cross-overs).
Allowing Partial Windows
A key improvement over tibbletime::slidify()
is that timetk::slidify()
implements
.partial
rolling windows. Just set .partial = TRUE
.
References
The Tibbletime R Package by Davis Vaughan, which includes the original
rollify()
Function
See also
Transformation Functions:
slidify_vec()
- A simple vectorized function for applying summary functions to rolling windows.
Augmentation Functions (Add Rolling Multiple Columns):
tk_augment_slidify()
- For easily adding multiple rolling windows to you data
Slider R Package:
slider::pslide()
- The workhorse function that powerstimetk::slidify()
Examples
library(dplyr)
FB <- FANG %>% dplyr::filter(symbol == "FB")
# --- ROLLING MEAN (SINGLE ARG EXAMPLE) ---
# Turn the normal mean function into a rolling mean with a 5 row .period
mean_roll_5 <- slidify(mean, .period = 5, .align = "right")
FB %>%
mutate(rolling_mean_5 = mean_roll_5(adjusted))
#> # A tibble: 1,008 × 9
#> symbol date open high low close volume adjusted rolling_mean_5
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 FB 2013-01-02 27.4 28.2 27.4 28 69846400 28 NA
#> 2 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8 NA
#> 3 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8 NA
#> 4 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4 NA
#> 5 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1 28.6
#> 6 FB 2013-01-09 29.7 30.6 29.5 30.6 104787700 30.6 29.1
#> 7 FB 2013-01-10 30.6 31.5 30.3 31.3 95316400 31.3 29.8
#> 8 FB 2013-01-11 31.3 32.0 31.1 31.7 89598000 31.7 30.4
#> 9 FB 2013-01-14 32.1 32.2 30.6 31.0 98892800 31.0 30.7
#> 10 FB 2013-01-15 30.6 31.7 29.9 30.1 173242600 30.1 30.9
#> # ℹ 998 more rows
# Use `partial = TRUE` to allow partial windows (those with less than the full .period)
mean_roll_5_partial <- slidify(mean, .period = 5, .align = "right", .partial = TRUE)
FB %>%
mutate(rolling_mean_5 = mean_roll_5_partial(adjusted))
#> # A tibble: 1,008 × 9
#> symbol date open high low close volume adjusted rolling_mean_5
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 FB 2013-01-02 27.4 28.2 27.4 28 69846400 28 28
#> 2 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8 27.9
#> 3 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8 28.2
#> 4 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4 28.5
#> 5 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1 28.6
#> 6 FB 2013-01-09 29.7 30.6 29.5 30.6 104787700 30.6 29.1
#> 7 FB 2013-01-10 30.6 31.5 30.3 31.3 95316400 31.3 29.8
#> 8 FB 2013-01-11 31.3 32.0 31.1 31.7 89598000 31.7 30.4
#> 9 FB 2013-01-14 32.1 32.2 30.6 31.0 98892800 31.0 30.7
#> 10 FB 2013-01-15 30.6 31.7 29.9 30.1 173242600 30.1 30.9
#> # ℹ 998 more rows
# There's nothing stopping you from combining multiple rolling functions with
# different .period sizes in the same mutate call
mean_roll_10 <- slidify(mean, .period = 10, .align = "right")
FB %>%
select(symbol, date, adjusted) %>%
mutate(
rolling_mean_5 = mean_roll_5(adjusted),
rolling_mean_10 = mean_roll_10(adjusted)
)
#> # A tibble: 1,008 × 5
#> symbol date adjusted rolling_mean_5 rolling_mean_10
#> <chr> <date> <dbl> <dbl> <dbl>
#> 1 FB 2013-01-02 28 NA NA
#> 2 FB 2013-01-03 27.8 NA NA
#> 3 FB 2013-01-04 28.8 NA NA
#> 4 FB 2013-01-07 29.4 NA NA
#> 5 FB 2013-01-08 29.1 28.6 NA
#> 6 FB 2013-01-09 30.6 29.1 NA
#> 7 FB 2013-01-10 31.3 29.8 NA
#> 8 FB 2013-01-11 31.7 30.4 NA
#> 9 FB 2013-01-14 31.0 30.7 NA
#> 10 FB 2013-01-15 30.1 30.9 29.8
#> # ℹ 998 more rows
# For summary operations like rolling means, we can accomplish large-scale
# multi-rolls with tk_augment_slidify()
FB %>%
select(symbol, date, adjusted) %>%
tk_augment_slidify(
adjusted, .period = 5:10, .f = mean, .align = "right",
.names = stringr::str_c("MA_", 5:10)
)
#> # A tibble: 1,008 × 9
#> symbol date adjusted MA_5 MA_6 MA_7 MA_8 MA_9 MA_10
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 FB 2013-01-02 28 NA NA NA NA NA NA
#> 2 FB 2013-01-03 27.8 NA NA NA NA NA NA
#> 3 FB 2013-01-04 28.8 NA NA NA NA NA NA
#> 4 FB 2013-01-07 29.4 NA NA NA NA NA NA
#> 5 FB 2013-01-08 29.1 28.6 NA NA NA NA NA
#> 6 FB 2013-01-09 30.6 29.1 28.9 NA NA NA NA
#> 7 FB 2013-01-10 31.3 29.8 29.5 29.3 NA NA NA
#> 8 FB 2013-01-11 31.7 30.4 30.1 29.8 29.6 NA NA
#> 9 FB 2013-01-14 31.0 30.7 30.5 30.3 29.9 29.7 NA
#> 10 FB 2013-01-15 30.1 30.9 30.6 30.4 30.2 30.0 29.8
#> # ℹ 998 more rows
# --- GROUPS AND ROLLING ----
# One of the most powerful things about this is that it works with
# groups since `mutate` is being used
mean_roll_3 <- slidify(mean, .period = 3, .align = "right")
FANG %>%
group_by(symbol) %>%
mutate(mean_roll = mean_roll_3(adjusted)) %>%
slice(1:5)
#> # A tibble: 20 × 9
#> # Groups: symbol [4]
#> symbol date open high low close volume adjusted mean_roll
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 AMZN 2013-01-02 256. 258. 253. 257. 3271000 257. NA
#> 2 AMZN 2013-01-03 257. 261. 256. 258. 2750900 258. NA
#> 3 AMZN 2013-01-04 258. 260. 257. 259. 1874200 259. 258.
#> 4 AMZN 2013-01-07 263. 270. 263. 268. 4910000 268. 262.
#> 5 AMZN 2013-01-08 267. 269. 264. 266. 3010700 266. 265.
#> 6 FB 2013-01-02 27.4 28.2 27.4 28 69846400 28 NA
#> 7 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8 NA
#> 8 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8 28.2
#> 9 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4 28.6
#> 10 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1 29.1
#> 11 GOOG 2013-01-02 719. 727. 717. 723. 5101500 361. NA
#> 12 GOOG 2013-01-03 725. 732. 721. 724. 4653700 361. NA
#> 13 GOOG 2013-01-04 729. 741. 728. 738. 5547600 369. 364.
#> 14 GOOG 2013-01-07 735. 739. 731. 735. 3323800 367. 366.
#> 15 GOOG 2013-01-08 736. 736. 724. 733. 3364700 366. 367.
#> 16 NFLX 2013-01-02 95.2 95.8 90.7 92.0 19431300 13.1 NA
#> 17 NFLX 2013-01-03 92.0 97.9 91.5 96.6 27912500 13.8 NA
#> 18 NFLX 2013-01-04 96.5 97.7 95.5 96.0 17761100 13.7 13.6
#> 19 NFLX 2013-01-07 96.4 102. 96.1 99.2 45550400 14.2 13.9
#> 20 NFLX 2013-01-08 100. 101. 96.8 97.2 24714900 13.9 13.9
# --- ROLLING CORRELATION (MULTIPLE ARG EXAMPLE) ---
# With 2 args, use the purrr syntax of ~ and .x, .y
# Rolling correlation example
cor_roll <- slidify(~cor(.x, .y), .period = 5, .align = "right")
FB %>%
mutate(running_cor = cor_roll(adjusted, open))
#> # A tibble: 1,008 × 9
#> symbol date open high low close volume adjusted running_cor
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 FB 2013-01-02 27.4 28.2 27.4 28 69846400 28 NA
#> 2 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8 NA
#> 3 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8 NA
#> 4 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4 NA
#> 5 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1 0.749
#> 6 FB 2013-01-09 29.7 30.6 29.5 30.6 104787700 30.6 0.805
#> 7 FB 2013-01-10 30.6 31.5 30.3 31.3 95316400 31.3 0.859
#> 8 FB 2013-01-11 31.3 32.0 31.1 31.7 89598000 31.7 0.884
#> 9 FB 2013-01-14 32.1 32.2 30.6 31.0 98892800 31.0 0.667
#> 10 FB 2013-01-15 30.6 31.7 29.9 30.1 173242600 30.1 0.379
#> # ℹ 998 more rows
# With >2 args, create an anonymous function with >2 args or use
# the purrr convention of ..1, ..2, ..3 to refer to the arguments
avg_of_avgs <- slidify(
function(x, y, z) (mean(x) + mean(y) + mean(z)) / 3,
.period = 10,
.align = "right"
)
# Or
avg_of_avgs <- slidify(
~(mean(..1) + mean(..2) + mean(..3)) / 3,
.period = 10,
.align = "right"
)
FB %>%
mutate(avg_of_avgs = avg_of_avgs(open, high, low))
#> # A tibble: 1,008 × 9
#> symbol date open high low close volume adjusted avg_of_avgs
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 FB 2013-01-02 27.4 28.2 27.4 28 69846400 28 NA
#> 2 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8 NA
#> 3 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8 NA
#> 4 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4 NA
#> 5 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1 NA
#> 6 FB 2013-01-09 29.7 30.6 29.5 30.6 104787700 30.6 NA
#> 7 FB 2013-01-10 30.6 31.5 30.3 31.3 95316400 31.3 NA
#> 8 FB 2013-01-11 31.3 32.0 31.1 31.7 89598000 31.7 NA
#> 9 FB 2013-01-14 32.1 32.2 30.6 31.0 98892800 31.0 NA
#> 10 FB 2013-01-15 30.6 31.7 29.9 30.1 173242600 30.1 29.7
#> # ℹ 998 more rows
# Optional arguments MUST be passed at the creation of the rolling function
# Only data arguments that are "rolled over" are allowed when calling the
# rolling version of the function
FB$adjusted[1] <- NA
roll_mean_na_rm <- slidify(~mean(.x, na.rm = TRUE), .period = 5, .align = "right")
FB %>%
mutate(roll_mean = roll_mean_na_rm(adjusted))
#> # A tibble: 1,008 × 9
#> symbol date open high low close volume adjusted roll_mean
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 FB 2013-01-02 27.4 28.2 27.4 28 69846400 NA NA
#> 2 FB 2013-01-03 27.9 28.5 27.6 27.8 63140600 27.8 NA
#> 3 FB 2013-01-04 28.0 28.9 27.8 28.8 72715400 28.8 NA
#> 4 FB 2013-01-07 28.7 29.8 28.6 29.4 83781800 29.4 NA
#> 5 FB 2013-01-08 29.5 29.6 28.9 29.1 45871300 29.1 28.8
#> 6 FB 2013-01-09 29.7 30.6 29.5 30.6 104787700 30.6 29.1
#> 7 FB 2013-01-10 30.6 31.5 30.3 31.3 95316400 31.3 29.8
#> 8 FB 2013-01-11 31.3 32.0 31.1 31.7 89598000 31.7 30.4
#> 9 FB 2013-01-14 32.1 32.2 30.6 31.0 98892800 31.0 30.7
#> 10 FB 2013-01-15 30.6 31.7 29.9 30.1 173242600 30.1 30.9
#> # ℹ 998 more rows
# --- ROLLING REGRESSIONS ----
# Rolling regressions are easy to implement using `.unlist = FALSE`
lm_roll <- slidify(~lm(.x ~ .y), .period = 90, .unlist = FALSE, .align = "right")
FB %>%
tidyr::drop_na() %>%
mutate(numeric_date = as.numeric(date)) %>%
mutate(rolling_lm = lm_roll(adjusted, numeric_date)) %>%
filter(!is.na(rolling_lm))
#> # A tibble: 918 × 10
#> symbol date open high low close volume adjusted numeric_date
#> <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 FB 2013-05-13 26.6 27.3 26.5 26.8 29068800 26.8 15838
#> 2 FB 2013-05-14 26.9 27.3 26.8 27.1 24930300 27.1 15839
#> 3 FB 2013-05-15 26.9 27.0 26.4 26.6 30299800 26.6 15840
#> 4 FB 2013-05-16 26.5 26.5 25.9 26.1 35499100 26.1 15841
#> 5 FB 2013-05-17 26.4 26.6 26.2 26.2 29462700 26.2 15842
#> 6 FB 2013-05-20 26.2 26.2 25.7 25.8 42402900 25.8 15845
#> 7 FB 2013-05-21 25.9 26.1 25.6 25.7 26261300 25.7 15846
#> 8 FB 2013-05-22 25.6 25.8 24.9 25.2 45314500 25.2 15847
#> 9 FB 2013-05-23 24.8 25.5 24.8 25.1 37663100 25.1 15848
#> 10 FB 2013-05-24 25.0 25.0 24.1 24.3 58727900 24.3 15849
#> # ℹ 908 more rows
#> # ℹ 1 more variable: rolling_lm <list>