R: Create a rolling (sliding) version of any function

slidify {timetk} | R Documentation

## Create a rolling (sliding) version of any function

### Description

`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

- 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 powers `timetk::slidify()`

### Examples

    library(tidyverse)
    library(tidyquant)
    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))

    # 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))

    # 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)
        )

    # 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)
        )

    # --- 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)

    # --- 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))

    # 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))

    # 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))

    # --- RETURNING MULTIPLE VALUES (SUMMARY) ----

    FB <- FANG %>% filter(symbol == "FB")

    # If the function returns >1 value, set the `.unlist = FALSE` argument
    # Running 5 number summary
    summary_roll <- slidify(summary,
                            .period = 5, .unlist = FALSE, .align = "right")

    FB_summarised <- FB %>% mutate(summary_roll = summary_roll(adjusted))

    FB_summarised$summary_roll[[5]]

    # dplyr::bind_rows() is often helpful in these cases to get
    # meaningful output

    summary_roll <- slidify(~ bind_rows(summary(.)),
                            .period = 5, .unlist = FALSE, .align = "right")

    FB %>%
        mutate(summary_roll = summary_roll(adjusted)) %>%
        filter(!is.na(summary_roll)) %>%
        unnest(summary_roll)

    # --- ROLLING REGRESSIONS ----

    # Rolling regressions are easy to implement using `.unlist = FALSE`
    lm_roll <- slidify(~lm(.x ~ .y), .period = 90, .unlist = FALSE, .align = "right")

    FB %>%
        mutate(numeric_date = as.numeric(date)) %>%
        mutate(rolling_lm = lm_roll(adjusted, numeric_date)) %>%
        filter(!is.na(rolling_lm))

[Package timetk version 2.0.0 Index]