Skip to contents

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.

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 to FALSE by default, but can be switched to TRUE to remove NA'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:

  1. Alignment ("center", "left", "right")

  2. Partial windows are allowed

  3. 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

See also

Transformation Functions:

  • slidify_vec() - A simple vectorized function for applying summary functions to rolling windows.

Augmentation Functions (Add Rolling Multiple Columns):

Slider R Package:

Examples

library(tidyverse)
library(tidyquant)
library(tidyr)
library(timetk)

FB <- FANG %>% 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
#> # … with 998 more rows
#> # ℹ Use `print(n = ...)` to see 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
#> # … with 998 more rows
#> # ℹ Use `print(n = ...)` to see 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
#> # … with 998 more rows
#> # ℹ Use `print(n = ...)` to see 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 = 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
#> # … with 998 more rows
#> # ℹ Use `print(n = ...)` to see more rows

# --- GROUPS AND ROLLING ----

# One of the most powerful things about this is that it works with
# groups since `mutate` is being used
data(FANG)

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
#> # … with 998 more rows
#> # ℹ Use `print(n = ...)` to see 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
#> # … with 998 more rows
#> # ℹ Use `print(n = ...)` to see 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
#> # … with 998 more rows
#> # ℹ Use `print(n = ...)` to see 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 %>%
    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…¹ rolli…²
#>    <chr>  <date>     <dbl> <dbl> <dbl> <dbl>    <dbl>    <dbl>     <dbl> <list> 
#>  1 FB     2013-05-13  26.6  27.3  26.5  26.8 29068800     26.8     15838 <lm>   
#>  2 FB     2013-05-14  26.9  27.3  26.8  27.1 24930300     27.1     15839 <lm>   
#>  3 FB     2013-05-15  26.9  27.0  26.4  26.6 30299800     26.6     15840 <lm>   
#>  4 FB     2013-05-16  26.5  26.5  25.9  26.1 35499100     26.1     15841 <lm>   
#>  5 FB     2013-05-17  26.4  26.6  26.2  26.2 29462700     26.2     15842 <lm>   
#>  6 FB     2013-05-20  26.2  26.2  25.7  25.8 42402900     25.8     15845 <lm>   
#>  7 FB     2013-05-21  25.9  26.1  25.6  25.7 26261300     25.7     15846 <lm>   
#>  8 FB     2013-05-22  25.6  25.8  24.9  25.2 45314500     25.2     15847 <lm>   
#>  9 FB     2013-05-23  24.8  25.5  24.8  25.1 37663100     25.1     15848 <lm>   
#> 10 FB     2013-05-24  25.0  25.0  24.1  24.3 58727900     24.3     15849 <lm>   
#> # … with 908 more rows, and abbreviated variable names ¹​numeric_date,
#> #   ²​rolling_lm
#> # ℹ Use `print(n = ...)` to see more rows