Skip to contents

Introduction

This vignette demonstrates how to compute change over time with two core functions:

  • percent_change(start_value, end_value): computes the simple relative change.
  • annualize_change(start_values, end_values, time_elapsed, time_unit, year_length = 365.2425): converts growth over a period to an annualized rate.

A small monthly time series to illustrate computations:

set.seed(42)

df <- tibble::tibble(
  date  = seq(as.Date("2025-01-01"), by = "month", length.out = 12),
  value = round(100 * cumprod(1 + rnorm(12, mean = 0.01, sd = 0.01)), 2)
)

df
#> # A tibble: 12 × 2
#>    date       value
#>    <date>     <dbl>
#>  1 2025-01-01  102.
#>  2 2025-02-01  103.
#>  3 2025-03-01  104.
#>  4 2025-04-01  106.
#>  5 2025-05-01  107.
#>  6 2025-06-01  108.
#>  7 2025-07-01  111.
#>  8 2025-08-01  112.
#>  9 2025-09-01  115.
#> 10 2025-10-01  117.
#> # ℹ 2 more rows

Percent Change

percent_change() expects starting and ending values. A natural workflow is using [dplyr::lag()] to use different values in the same vector in a vectorized manner:

df_pct <- df |> 
  arrange(date) |> 
  mutate(
    # vectorized, preserves NA on first row
    pct_mom = percent_change(lag(value), value)
  )
#> Warning: There was 1 warning in `mutate()`.
#>  In argument: `pct_mom = percent_change(lag(value), value)`.
#> Caused by warning:
#> ! NA values detected; returning NA for those positions.

df_pct |> select(date, value, pct_mom)
#> # A tibble: 12 × 3
#>    date       value  pct_mom
#>    <date>     <dbl>    <dbl>
#>  1 2025-01-01  102. NA      
#>  2 2025-02-01  103.  0.00440
#>  3 2025-03-01  104.  0.0136 
#>  4 2025-04-01  106.  0.0163 
#>  5 2025-05-01  107.  0.0141 
#>  6 2025-06-01  108.  0.00894
#>  7 2025-07-01  111.  0.0251 
#>  8 2025-08-01  112.  0.00900
#>  9 2025-09-01  115.  0.0302 
#> 10 2025-10-01  117.  0.00935
#> # ℹ 2 more rows

Annualized Change

The annualize_change() function is versatile enough to be able to calculate the annualized change for differing time units (e.g. monthly, weekly, daily) as well as periods of those units. This also plays nicely with [dplyr::lag()]:

Annualized rates from 1-month and 3-month intervals.

df_ann <- df |> 
  arrange(date) |> 
  mutate(
    ann_1m = annualize_change(
      lag(value), 
      value, 
      time_elapsed = 1, 
      time_unit = "monthly"
      ),
    ann_3m = annualize_change(
      lag(value, 3), 
      value, 
      time_elapsed = 3, 
      time_unit = "monthly"
      )
  )

df_ann |> select(date, value, ann_1m, ann_3m)
#> # A tibble: 12 × 4
#>    date       value  ann_1m ann_3m
#>    <date>     <dbl>   <dbl>  <dbl>
#>  1 2025-01-01  102. NA      NA    
#>  2 2025-02-01  103.  0.0540 NA    
#>  3 2025-03-01  104.  0.176  NA    
#>  4 2025-04-01  106.  0.214   0.146
#>  5 2025-05-01  107.  0.182   0.191
#>  6 2025-06-01  108.  0.113   0.169
#>  7 2025-07-01  111.  0.346   0.210
#>  8 2025-08-01  112.  0.114   0.186
#>  9 2025-09-01  115.  0.430   0.289
#> 10 2025-10-01  117.  0.118   0.212
#> # ℹ 2 more rows