Skip to contents

Introduction

This vignette shows two common transformations for economic time series and survey-style data: * create_index(): turn a numeric vector into either a base-100 index (base value = 100) or a base-0 index (which mirrors percent change) (base value = 0). * The base can be selected by position or by name if the vector is named. * create_diffusion_index(): compute a diffusion index from survey-style proportions of respondents increasing, decreasing, or unchanged. The methods the function can execute are: * Federal Reserve: (pct_increased - pct_decreased) * 100 * IHS-PMI: (pct_increased + 0.5 * pct_unchanged) * 100 * Conference Board: encode each unit’s pct_change into 1 / 0.5 / 0 based on a threshold of .0005, then calculate the average and multiply it by 100.

Indexing with create_index()

Basic usage on a numeric vector

x <- c(10, 34, 46, 74, 83, 110, 129)

# Base-100 index (default is to have base be the first element)
idx_100 <- create_index(x)

# Base-0 (this is effectively calculating the percentage change from base and multiplying by 100)
idx_pct <- create_index(x, idx_type = 0)

idx_100
#> [1]  100  340  460  740  830 1100 1290
idx_pct
#> [1]    0  240  360  640  730 1000 1190

Selecting base by position or by name

# By position: use the second element as the base
idx_pos2 <- create_index(x, idx_pos = 2)

# By name: works only if the vector is named
x_named <- c(a = 10, b = 34, c = 46, d = 74, e = 83, f = 110, g = 129)
idx_name_b_100 <- create_index(x_named, idx_pos = "b")          # base-100
idx_name_b_pct <- create_index(x_named, idx_pos = "b", idx_type = 0)  # % from "b"

idx_pos2
#> [1]  29.41176 100.00000 135.29412 217.64706 244.11765 323.52941 379.41176
idx_name_b_100
#>         a         b         c         d         e         f         g 
#>  29.41176 100.00000 135.29412 217.64706 244.11765 323.52941 379.41176
idx_name_b_pct
#>         a         b         c         d         e         f         g 
#> -70.58824   0.00000  35.29412 117.64706 144.11765 223.52941 279.41176

Vectorization works within groups so it can be used inside of [dplyr::mutate()] to calculate indices.

df_state <- tibble::tibble(
  series = rep(c("US", "CA"), each = 5),
  value  = c(80, 82, 85, 84, 90,   100, 98,  99, 101, 105)
)

# Base-100 per series (base = first value within each group)
df_idx <- df_state %>%
  group_by(series) %>%
  mutate(index_100 = create_index(value)) %>%
  ungroup()

df_idx
#> # A tibble: 10 × 3
#>    series value index_100
#>    <chr>  <dbl>     <dbl>
#>  1 US        80      100 
#>  2 US        82      102.
#>  3 US        85      106.
#>  4 US        84      105 
#>  5 US        90      112.
#>  6 CA       100      100 
#>  7 CA        98       98 
#>  8 CA        99       99 
#>  9 CA       101      101 
#> 10 CA       105      105

Diffusion indices with create_diffusion_index()

Diffusion indices are commonly derived from survey responses and used as a way to gauge the breadth and not the amplitude of change in a collection of economic variables like employment, manufacturing, or other business conditions. The create_diffusion_index() function provides three common vectorized ways way to calculate them.

Federal Reserve method

Here the pct_decreased value is simply subtracted from the pct_increased value and then multiplied by 100. This index is centered on 0 where any number above 0 indicates expansion or a positive reading and any number below 0 is a contraction or negative reading.

fed <- create_diffusion_index(
  pct_increased = c(0.60, 0.55, 0.50),
  pct_decreased = c(0.20, 0.25, 0.30),
  idx_type      = "Federal Reserve"
)
fed
#> [1] 40 30 20

IHS-PMI method

This is the sum of the pct_increased value and half of the pct_unchanged value multiplied by 100. This index is centered on 50 where any number above 50 indicates expansion or a positive reading and any number below 50 is a contraction or negative reading.

For example, a diffusion index change of 53 to 59 is accelerating growth, a change from 59 to 51 is decelerating growth, a change from 51 to 44 is accelerating contraction, and a change from 44 to 49 is decelerating contraction.

ihs <- create_diffusion_index(
  pct_increased = c(0.55, 0.45, 0.52),
  pct_unchanged = c(0.30, 0.35, 0.28),
  idx_type      = "IHS-PMI"
)
ihs
#> [1] 70.0 62.5 66.0

Conference Board method

This method encodes each pct_change value as: * 1 if pct_change is greater than .0005. * 0.5 if pct_change is between -.0005 and .0005. * 0 if pct_change is less than -.0005.

Then returns the mean of the encoded values multiplied by 100.

# Example: a small panel of unit-level percent changes for a month
pct_change <- c(0.003, -0.002, 0.0002, 0.0, 0.0011)

conf_board <- create_diffusion_index(
  pct_change = pct_change,
  idx_type   = "Conference Board"
)
conf_board
#> [1] 60

Since the functions are vectorized, they play nice in the tidy syntax in [tibble::tibble()]:

survey <- tibble::tibble(
  month         = as.Date(c("2025-01-01","2025-02-01","2025-03-01")),
  pct_inc       = c(0.60, 0.55, 0.52),
  pct_dec       = c(0.20, 0.22, 0.25),
  pct_unch      = c(0.20, 0.23, 0.23)
)

survey |> 
  transmute(
    fed_idx = create_diffusion_index(
      pct_inc, 
      pct_dec, 
      idx_type = "Federal Reserve"
      ),
    ihs_idx = create_diffusion_index(
      pct_increased = pct_inc, 
      pct_unchanged = pct_unch, 
      idx_type = "IHS-PMI"
      )
  )
#> # A tibble: 3 × 2
#>   fed_idx ihs_idx
#>     <dbl>   <dbl>
#> 1      40    70  
#> 2      33    66.5
#> 3      27    63.5