Statistics

algorithms
r
Author
Published

November 27, 2025

Usage

options(microbenchmark.unit = "us")
n_vars <- 10
n_obs <- 1000
weights <- 0.9 ^ (n_obs:1)

x <- matrix(rnorm(n_obs * n_vars), nrow = n_obs, ncol = n_vars)
y <- matrix(rnorm(n_obs), nrow = n_obs, ncol = 1)
x_lgl <- x < 0

Rolling any

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_any(x_lgl, width = 125, min_obs = 1),
  "250" = roll::roll_any(x_lgl, width = 250, min_obs = 1),
  "500" = roll::roll_any(x_lgl, width = 500, min_obs = 1),
  "1000" = roll::roll_any(x_lgl, width = 1000, min_obs = 1)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq     max neval
  125 122.1 139.45 150.118 143.70 156.30   322.3   100
  250 131.0 138.35 885.063 148.20 162.15 73523.7   100
  500 128.6 135.25 149.147 143.65 160.35   191.1   100
 1000 120.3 127.70 143.784 134.30 156.20   367.0   100

Rolling all

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_all(x_lgl, width = 125, min_obs = 1),
  "250" = roll::roll_all(x_lgl, width = 250, min_obs = 1),
  "500" = roll::roll_all(x_lgl, width = 500, min_obs = 1),
  "1000" = roll::roll_all(x_lgl, width = 1000, min_obs = 1)
)
print(result)
Unit: microseconds
 expr  min    lq    mean median     uq   max neval
  125 82.4 90.15 114.051  94.70 134.00 228.1   100
  250 80.6 88.95 110.066  94.95 134.35 216.0   100
  500 75.7 85.60 108.501  89.40 135.15 292.7   100
 1000 72.8 78.70  94.912  82.30 106.70 181.8   100

Rolling sums

\[ \begin{aligned} &\text{Expanding window} \\ &\bullet\text{sum}_{x}\leftarrow\lambda\times\text{sum}_{x}+\text{w}_{new}\times\text{x}_{new}\\ &\text{Rolling window}\\ &\bullet\text{sum}_{x}\leftarrow\lambda\times\text{sum}_{x}+\text{w}_{new}\times\text{x}_{new}-\lambda\times\text{w}_{old}\times\text{x}_{old} \end{aligned} \]

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_sum(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_sum(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_sum(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_sum(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr  min     lq    mean median     uq   max neval
  125 95.5 107.60 127.335 114.25 130.00 278.5   100
  250 94.2 106.95 130.752 113.75 136.90 328.1   100
  500 95.3 106.80 132.233 117.80 148.15 296.6   100
 1000 91.6 101.80 127.463 109.20 138.10 291.1   100

Rolling products

\[ \begin{aligned} &\text{Expanding window}\\ &\bullet\text{prod}_{w}\leftarrow\text{prod}_{w}\times\text{w}_{new}\\ &\bullet\text{prod}_{x}\leftarrow\text{prod}_{x}\times\text{x}_{new}\\ &\text{Rolling window}\\ &\bullet\text{prod}_{x}\leftarrow\text{prod}_{x}\times\text{x}_{new}/\text{x}_{old} \end{aligned} \]

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_prod(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_prod(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_prod(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_prod(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min    lq    mean median     uq   max neval
  125 207.2 219.7 236.949 228.60 241.55 374.9   100
  250 207.8 224.7 240.577 236.35 247.40 350.6   100
  500 136.6 154.1 175.491 165.55 180.55 388.8   100
 1000 135.6 155.0 178.438 165.30 176.85 557.2   100

Rolling means

\[ \begin{aligned} &\text{Expanding window}\\ &\bullet\text{sum}_{w}\leftarrow\text{sum}_{w}+\text{w}_{new}\\ &\bullet\text{sum}_{x}\leftarrow\lambda\times\text{sum}_{x}+\text{w}_{new}\times\text{x}_{new}\\ &\text{Rolling window}\\ &\bullet\text{sum}_{x}\leftarrow\lambda\times\text{sum}_{x}+\text{w}_{new}\times\text{x}_{new}-\lambda\times\text{w}_{old}\times \text{x}_{old} \end{aligned} \]

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_mean(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_mean(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_mean(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_mean(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq   max neval
  125  99.3 107.25 133.575 113.50 147.65 305.0   100
  250 101.8 109.90 134.233 115.55 149.90 268.7   100
  500  99.2 106.25 128.771 112.30 143.80 246.4   100
 1000  96.5 102.70 129.033 115.45 143.80 217.0   100

Rolling minimums

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_min(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_min(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_min(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_min(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min    lq    mean median     uq   max neval
  125 131.5 147.0 167.555 154.40 180.05 268.4   100
  250 117.8 145.8 163.208 154.75 167.00 304.5   100
  500 139.9 149.8 173.730 159.40 184.25 366.6   100
 1000 134.7 147.1 181.030 162.50 194.20 911.0   100

Rolling maximums

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_max(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_max(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_max(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_max(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq   max neval
  125 102.6 115.70 131.797 123.20 134.20 233.4   100
  250 101.7 114.05 136.393 122.15 137.10 256.8   100
  500 105.0 113.95 133.193 121.95 135.75 332.2   100
 1000 101.4 111.05 130.468 117.40 136.45 261.7   100

Rolling index of minimums

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_idxmin(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_idxmin(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_idxmin(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_idxmin(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq   max neval
  125 101.0 116.00 132.410 130.55 142.85 301.5   100
  250 106.5 120.05 138.459 132.95 148.00 320.5   100
  500  98.8 119.50 136.189 133.15 144.40 333.5   100
 1000  97.2 116.05 131.241 128.35 141.80 194.9   100

Rolling index of maximums

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_idxmax(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_idxmax(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_idxmax(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_idxmax(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq   max neval
  125 103.2 111.45 129.493 118.15 137.95 241.2   100
  250 100.1 111.30 130.007 116.80 132.60 274.4   100
  500 103.8 111.30 130.981 117.00 134.80 346.0   100
 1000  97.0 110.70 133.250 117.05 148.35 274.4   100

Rolling medians

# "'online' is only supported for equal 'weights'"
result <- microbenchmark::microbenchmark(
  "125" = roll::roll_median(x, width = 125, min_obs = 1),
  "250" = roll::roll_median(x, width = 250, min_obs = 1),
  "500" = roll::roll_median(x, width = 500, min_obs = 1),
  "1000" = roll::roll_median(x, width = 1000, min_obs = 1)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq    max neval
  125 746.3 807.45 910.488 832.10 866.85 7271.2   100
  250 740.6 810.15 851.863 834.15 924.65  963.7   100
  500 679.8 735.50 800.554 761.80 790.10 3382.7   100
 1000 518.2 566.25 598.112 589.10 610.20 1407.7   100

Rolling quantiles

# "'online' is only supported for equal 'weights'"
result <- microbenchmark::microbenchmark(
  "125" = roll::roll_quantile(x, width = 125, min_obs = 1),
  "250" = roll::roll_quantile(x, width = 250, min_obs = 1),
  "500" = roll::roll_quantile(x, width = 500, min_obs = 1),
  "1000" = roll::roll_quantile(x, width = 1000, min_obs = 1)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq    max neval
  125 788.7 820.50 849.449 834.00 845.80 1378.2   100
  250 779.8 806.10 829.049 821.20 837.90 1052.1   100
  500 728.5 751.65 774.702 766.60 783.90 1007.6   100
 1000 552.5 583.80 604.054 598.05 612.75  783.1   100

Rolling variances

\[ \begin{aligned} &\text{Expanding window}\\ &\bullet\text{sum}_{w}\leftarrow\text{sum}_{w}+\text{w}_{new}\\ &\bullet\text{sumsq}_{w}\leftarrow\text{sumsq}_{w}+\text{w}_{new}^{2}\\ &\bullet\text{sumsq}_{x}\leftarrow\lambda\times\text{sumsq}_{x}+\text{w}_{new}\times (\text{x}_{new}-\text{mean}_{x})(\text{x}_{new}-\text{mean}_{prev_x})\\ &\text{Rolling window}\\ &\bullet\text{sumsq}_{x}\leftarrow\lambda\times\text{sumsq}_{x}+\text{w}_{new}\times (\text{x}_{new}-\text{mean}_{x})(\text{x}_{new}-\text{mean}_{prev_x})-\\ &\lambda\times\text{w}_{old}\times (\text{x}_{old}-\text{mean}_{x})(\text{x}_{old}-\text{mean}_{prev_x}) \end{aligned} \]

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_var(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_var(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_var(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_var(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq   max neval
  125 118.9 137.50 154.914 147.30 156.65 290.7   100
  250 117.5 135.75 151.086 148.65 155.30 298.6   100
  500 120.0 132.40 150.285 143.05 151.80 373.9   100
 1000 108.1 125.20 138.725 134.70 143.15 275.0   100

Rolling standard deviations

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_sd(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_sd(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_sd(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_sd(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median     uq   max neval
  125 122.1 142.80 161.970 153.45 164.25 326.4   100
  250 132.7 141.90 160.125 151.60 165.45 296.8   100
  500 129.9 137.45 152.858 146.75 158.05 313.0   100
 1000 109.0 132.35 156.075 142.70 159.25 406.1   100

Rolling scaling and centering

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_scale(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_scale(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_scale(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_scale(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq    mean median    uq   max neval
  125 145.1 152.90 178.613 164.30 191.1 569.6   100
  250 145.1 150.90 176.003 159.40 178.7 642.6   100
  500 141.7 148.50 173.040 162.25 190.2 351.5   100
 1000 135.0 141.05 161.846 154.95 168.9 280.1   100

Rolling covariances

\[ \begin{aligned} &\text{Expanding window}\\ &\bullet\text{sum}_{w}\leftarrow\text{sum}_{w}+\text{w}_{new}\\ &\bullet\text{sumsq}_{w}\leftarrow\text{sumsq}_{w}+\text{w}_{new}^{2}\\ &\bullet\text{sumsq}_{xy}\leftarrow\lambda\times\text{sumsq}_{xy}+\text{w}_{new}\times (\text{x}_{new}-\text{mean}_{x})(\text{y}_{new}-\text{mean}_{prev_y})\\ &\text{Rolling window}\\ &\bullet\text{sumsq}_{xy}\leftarrow\lambda\times\text{sumsq}_{xy}+\text{w}_{new}\times (\text{x}_{new}-\text{mean}_{x})(\text{y}_{new}-\text{mean}_{prev_y})-\\ &\lambda\times\text{w}_{old}\times (\text{x}_{old}-\text{mean}_{x})(\text{y}_{old}-\text{mean}_{prev_y}) \end{aligned} \]

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_cov(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_cov(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_cov(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_cov(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min     lq     mean median      uq    max neval
  125 742.8 911.80 1038.197 972.50 1036.05 7535.8   100
  250 672.6 855.65 1017.200 935.00 1020.50 6087.4   100
  500 653.2 836.15  956.936 905.35  969.30 6041.5   100
 1000 564.3 732.95  823.702 762.20  813.55 6017.8   100

Rolling correlations

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_cor(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_cor(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_cor(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_cor(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min      lq     mean  median      uq    max neval
  125 861.5 1029.60 1078.781 1077.90 1126.75 1490.8   100
  250 774.2  980.30 1126.301 1064.40 1147.30 4535.5   100
  500 739.7  895.50 1000.387  974.70 1042.45 4175.1   100
 1000 641.4  772.45  909.451  828.05  865.25 4145.3   100

Rolling crossproducts

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_crossprod(x, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_crossprod(x, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_crossprod(x, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_crossprod(x, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr   min    lq    mean median     uq    max neval
  125 551.4 624.5 678.036 675.05 711.45 1168.1   100
  250 547.9 605.9 755.236 659.45 717.90 3993.3   100
  500 531.5 579.8 737.054 635.25 680.05 3927.9   100
 1000 501.6 537.8 623.640 562.30 618.70 3822.3   100

Rolling linear models

\[ \begin{aligned} &\text{coef}=\text{cov}_{xx}^{-1}\times\text{cov}_{xy}\\ &\text{intercept}=\text{mean}_{y}-\text{coef}\times\text{mean}_{x}\\ &\text{rsq}=\frac{\text{coef}^{T}\times\text{cov}_{xx}\times\text{coef}}{\text{var}_{y}}\\ &\text{var}_{resid}=\frac{(1-\text{rsq})(\text{var}_{y})(\text{sum}_{w}-\text{sumsq}_{w}/\text{sum}_{w})}{\text{n}_{rows}-\text{n}_{cols}}\\ &\text{xx}=\text{cov}_{xx}\times(\text{sum}_{w}-\text{sumsq}_{w}/\text{sum}_{w})\\ &\text{se}_{coef}=\sqrt{\text{var}_{resid}\times\text{diag}(\text{xx}^{-1})}\\ &\text{se}_{intercept}=\sqrt{\text{var}_{resid}\left(1/\text{sum}_{w}+\text{mean}_{x}^{T}\text{xx}^{-1}\text{mean}_{x}\right)} \end{aligned} \]

result <- microbenchmark::microbenchmark(
  "125" = roll::roll_lm(x, y, width = 125, min_obs = 1, weights = weights),
  "250" = roll::roll_lm(x, y, width = 250, min_obs = 1, weights = weights),
  "500" = roll::roll_lm(x, y, width = 500, min_obs = 1, weights = weights),
  "1000" = roll::roll_lm(x, y, width = 1000, min_obs = 1, weights = weights)
)
print(result)
Unit: microseconds
 expr    min      lq     mean  median      uq    max neval
  125 2140.9 2342.15 2510.482 2432.95 2549.65 4350.2   100
  250 2078.6 2328.70 2521.667 2442.40 2631.50 3821.7   100
  500 2092.6 2276.50 2448.099 2384.40 2523.95 3547.1   100
 1000 2030.9 2235.15 2496.523 2331.20 2457.30 6381.6   100

References