Statistics

algorithms
r
Author
Published

August 7, 2026

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 155.1 160.75   178.546 169.50 188.85     251.3   100
  250 151.1 157.90   181.832 171.05 198.55     273.5   100
  500 148.6 155.25   170.292 162.55 181.75     245.3   100
 1000 137.8 144.15 11811.606 149.55 168.80 1165656.3   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 90.4 115.25 135.149 126.35 156.45 235.8   100
  250 93.2 111.10 139.593 121.65 162.80 301.0   100
  500 86.0 102.80 130.102 114.60 154.80 265.2   100
 1000 83.2  91.35 112.524  98.20 130.10 201.3   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 122.4 131.55 150.605 138.90 156.80 287.0   100
  250 123.7 132.45 157.192 142.90 160.15 338.1   100
  500 122.2 131.25 155.520 142.60 157.00 306.7   100
 1000 117.4 128.40 151.627 138.45 161.00 312.8   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 379.8 429.35 454.423 446.70 474.65 723.1   100
  250 388.3 435.70 465.079 454.55 475.65 698.4   100
  500 278.5 310.90 330.266 320.00 346.60 428.6   100
 1000 269.8 306.65 328.519 321.75 345.05 442.3   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 118.1 144.50 170.106 160.65 190.85 281.1   100
  250 128.6 138.85 171.829 164.85 187.45 299.9   100
  500 114.5 143.80 172.549 163.50 188.60 342.9   100
 1000 120.8 136.20 167.034 155.70 182.80 385.3   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 135.2 143.50 158.469 148.45 170.75 233.0   100
  250 136.0 145.40 168.808 150.40 175.90 367.4   100
  500 128.8 144.55 168.441 152.60 182.05 288.1   100
 1000 134.7 143.80 168.930 156.80 187.55 270.2   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 137.1 142.00 156.701 146.00 154.1 245.3   100
  250 136.9 143.05 162.369 149.75 172.6 270.7   100
  500 137.4 141.30 159.029 146.75 167.1 354.1   100
 1000 133.5 140.10 164.789 145.45 189.0 263.4   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 135.3 155.15 174.544 166.40 185.60 294.1   100
  250 135.9 155.45 176.195 165.40 196.05 265.4   100
  500 135.4 150.90 175.207 161.55 192.60 384.1   100
 1000 135.3 154.15 171.374 162.30 186.05 253.7   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 131.8 138.60 170.475 151.85 175.80 310.3   100
  250 131.9 138.05 167.298 147.15 171.60 294.0   100
  500 129.2 137.40 171.916 151.75 188.65 395.4   100
 1000 125.0 136.60 168.648 148.65 170.40 303.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 1198.4 1285.00 1933.312 1543.75 2567.80 3129.0   100
  250 1207.9 1271.95 1956.695 1996.65 2535.20 3965.8   100
  500 1104.5 1194.30 1830.172 2105.70 2357.95 3134.9   100
 1000  850.3  923.70 1384.385 1078.50 1860.65 2671.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 1188.1 1215.60 1248.079 1231.25 1257.95 1460.4   100
  250 1178.0 1197.85 1243.659 1211.70 1241.55 1746.9   100
  500 1077.9 1114.90 1154.485 1130.90 1169.30 1516.0   100
 1000  802.3  861.20  907.983  878.90  916.30 1363.0   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 155.5 164.10 203.796 171.85 248.90 429.7   100
  250 152.2 160.25 182.548 165.05 179.65 471.0   100
  500 146.9 157.55 194.740 164.50 242.85 368.3   100
 1000 140.5 145.50 171.645 149.90 173.50 390.8   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 158.4 166.1 193.426 173.95 199.55 349.8   100
  250 154.1 165.2 196.453 173.90 211.35 324.8   100
  500 154.4 161.4 194.788 173.65 205.85 379.4   100
 1000 145.8 156.0 189.568 167.75 222.15 393.9   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 175.2 182.30 211.974 190.95 227.40 390.0   100
  250 174.0 184.50 225.264 200.20 269.05 439.9   100
  500 166.3 176.55 214.754 187.80 255.40 476.8   100
 1000 158.4 168.35 210.854 181.75 259.10 358.9   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 962.7 1163.50 1327.955 1275.90 1363.60 7991.6   100
  250 948.8 1139.70 1311.607 1206.20 1263.55 7980.4   100
  500 907.4 1067.45 1261.210 1140.10 1215.50 7784.5   100
 1000 787.0  919.10  997.542 1005.85 1070.05 1581.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 1125.5 1345.25 1546.605 1503.15 1611.10 6494.6   100
  250 1109.2 1267.50 1502.428 1435.40 1560.05 5588.4   100
  500 1045.0 1222.00 1389.189 1326.80 1426.20 6138.6   100
 1000  881.3 1023.95 1181.655 1141.75 1231.15 6248.4   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 827.2 891.10 1032.909 974.75 1081.70 5266.4   100
  250 824.1 883.95 1025.837 969.65 1067.65 5325.7   100
  500 793.1 843.45  929.815 916.60 1008.60 1122.2   100
 1000 737.9 781.05  982.718 810.00  949.45 5166.6   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 3078.4 4493.15 6160.442 5252.95 7823.05 11215.0   100
  250 3120.8 4420.90 5979.924 5009.00 7948.65 11372.0   100
  500 3040.2 4115.60 5756.970 4859.55 7704.40 10901.1   100
 1000 2960.5 4267.30 5925.290 4835.95 7522.10 18233.1   100

References