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 169.6 237.0  265.438 255.80 286.15   426.8   100
  250 175.0 233.5 1214.944 255.85 283.30 95219.4   100
  500 166.4 213.4  246.517 233.95 263.10   507.9   100
 1000 152.5 210.3  239.109 234.90 264.75   350.8   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 121.8 220.10 264.719 254.75 300.80 587.3   100
  250 156.0 224.35 272.212 257.10 303.35 637.0   100
  500 117.4 213.75 256.065 241.15 285.00 727.3   100
 1000 163.9 197.65 240.048 229.10 265.70 687.2   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 139.1 203.00 240.811 239.25 266.50 401.5   100
  250 157.9 211.80 260.370 245.15 293.95 479.2   100
  500 145.1 206.90 250.757 238.50 279.60 740.6   100
 1000 151.0 196.95 247.057 231.35 273.95 537.6   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 316.1 377.1 438.589 413.45 469.50 911.9   100
  250 297.5 369.4 440.955 402.85 490.65 723.5   100
  500 193.9 257.2 307.931 293.45 336.30 664.3   100
 1000 207.1 258.8 314.956 300.50 364.70 623.4   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 153.4 195.85 231.694 223.95 260.80 379.4   100
  250 152.2 197.55 241.089 233.00 277.80 466.5   100
  500 142.0 196.60 242.061 239.80 273.25 460.7   100
 1000 161.1 198.60 239.121 217.70 263.25 581.7   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 180.8 227.95 265.547  258.7 299.70 440.0   100
  250 169.1 216.45 253.690  251.5 284.15 395.2   100
  500 181.6 222.25 269.239  250.8 303.85 494.3   100
 1000 177.2 226.95 261.888  260.4 287.25 398.1   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 204.5 252.15 283.285 271.45 300.70 518.4   100
  250 204.0 255.65 294.771 284.55 317.40 617.3   100
  500 197.5 257.25 295.003 284.00 320.35 490.3   100
 1000 210.8 249.65 293.701 287.90 319.85 520.0   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 157.1 213.70 245.118 241.35 274.35 369.2   100
  250 153.5 207.30 239.541 235.00 262.05 356.0   100
  500 159.7 217.95 246.032 241.45 268.10 458.3   100
 1000 149.2 213.00 246.664 233.85 265.40 479.0   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 169.9 219.6 253.905 251.90 277.70 635.6   100
  250 166.5 220.2 257.903 253.45 283.05 589.4   100
  500 165.0 204.0 246.169 235.35 273.55 538.5   100
 1000 175.2 216.2 252.060 246.55 281.95 393.1   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 1015.7 1407.55 1510.976 1460.65 1571.50 2991.3   100
  250  944.8 1407.90 1602.424 1496.05 1594.20 9238.9   100
  500 1179.0 1292.25 1445.453 1386.75 1466.20 4604.7   100
 1000  908.3  989.35 1130.926 1069.65 1200.85 2500.1   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 1234.9 1385.45 1493.890 1458.15 1565.30 2304.0   100
  250 1232.2 1362.25 1480.285 1448.05 1542.70 2414.7   100
  500 1113.8 1263.60 1377.638 1320.20 1422.50 2247.9   100
 1000  868.9  983.95 1076.049 1034.45 1111.45 1899.6   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 179.9 228.20 267.682 264.65 299.20 393.6   100
  250 182.2 220.55 269.527 264.30 296.70 666.5   100
  500 167.5 212.90 256.175 248.95 283.80 507.4   100
 1000 165.5 208.65 244.637 236.60 265.05 396.4   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 179.2 239.00 288.371 283.35 322.70 599.1   100
  250 180.2 229.35 264.676 258.85 293.65 604.2   100
  500 190.0 233.55 269.849 269.65 294.35 622.2   100
 1000 171.7 219.75 258.615 254.00 291.25 494.3   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 204.6 242.80 291.714 282.55 326.45 443.6   100
  250 216.2 246.30 280.026 273.35 297.00 471.9   100
  500 198.4 234.40 283.826 272.90 322.70 446.9   100
 1000 183.0 229.25 274.412 268.00 297.60 670.8   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 1138.0 1395.40 1616.938 1512.00 1622.00  8713.2   100
  250 1060.6 1325.70 1589.562 1427.30 1576.75  9154.4   100
  500 1030.7 1260.25 1471.899 1371.75 1487.80 10952.9   100
 1000  885.6 1102.10 1280.072 1218.10 1310.90  7998.7   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 1118.0 1573.40 1933.720 1769.45 1925.6 7065.3   100
  250 1134.5 1577.05 1807.487 1704.25 1842.4 8204.9   100
  500 1137.4 1434.05 1592.441 1553.50 1723.4 2764.2   100
 1000  921.1 1232.00 1333.853 1340.35 1424.5 1734.0   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 836.2 1013.25 1167.567 1103.1 1191.20 6366.2   100
  250 720.5 1014.70 1172.648 1106.0 1203.65 5342.8   100
  500 674.6  912.40 1132.177 1044.5 1134.85 6861.3   100
 1000 626.5  869.15  963.815  959.9 1056.70 1301.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 3870.5 4477.65 4889.767 4832.80 5240.55  6851.2   100
  250 3473.9 4511.85 4986.355 4749.55 5225.55 14647.6   100
  500 3341.3 4304.60 4765.339 4646.40 5023.50 10687.0   100
 1000 3168.7 4237.00 4576.689 4555.70 4894.95  6243.3   100

References