This page documents our benchmark methodology to ensure transparency and reproducibility. All benchmarks can be reproduced using the provided scripts.
YOCO Compression + Sparse Matrices
leanfe uses the YOCO (You Only Compress Once) strategy from Wong et al. (2021) combined with sparse matrices for all standard error types (IID, HC1, and clustered):
For IID/HC1 Standard Errors
Compress: GROUP BY (regressors + fixed effects) → compute n, sum(y), sum(y²) per group
Build sparse design matrix: FE dummies as scipy.sparse (Python) or Matrix (R)
Solve sparse WLS: Weighted least squares with sparse operations
Standard errors: Compute from grouped residual sum of squares
For Clustered Standard Errors (Section 5.3.1)
Clustered SEs require additional steps to aggregate scores within clusters:
Within-cluster compression: GROUP BY (regressors + fixed effects + cluster_id) — each compressed record belongs to exactly one cluster
Build sparse design matrix: Same as above
Solve sparse WLS: Same as above
Compute ẽ⁰: Sum of residuals per group = sum_y - n × ŷ
**Build sparse cluster matrix W̃_C**: Indicator matrix (G × C) mapping groups to clusters
The sparse cluster matrix W̃_C enables vectorized aggregation across all clusters simultaneously, avoiding the traditional loop over C clusters.
This is lossless - coefficients and standard errors are identical to the full computation, but up to 23x faster for discrete regressors at scale.
Why Sparse Matrices? (leanfe’s Contribution)
The original YOCO paper by Wong et al. (2021) describes the compression strategy and the mathematical framework for computing coefficients and standard errors from sufficient statistics. However, the paper focuses on the statistical methodology and does not address the practical implementation challenge of how to efficiently represent the design matrix when fixed effects are included.
Specifically, the paper:
✅ Describes compression via GROUP BY on features (M̃, ỹ⁰, ỹ⁰⁰, ñ)
✅ Shows how to compute β̂ and V(β̂) from compressed data
✅ Covers IID, HC1, and clustered standard errors
❌ Does not discuss how to handle FE dummy variables efficiently
❌ Does not mention sparse matrices
leanfe extends YOCO with sparse matrices for the FE dummy variables. This is a natural and mathematically equivalent extension that provides significant performance benefits.
The Problem: Dense FE Dummies Are Wasteful
After YOCO compression, we have G groups (compressed rows) and need to estimate:
where \(D_g\) is a matrix of FE dummy variables. With F total FE levels across all FEs, \(D\) has dimensions G × (F-1). For example:
G = 50,000 compressed groups
F = 600 FE levels (500 + 100)
Dense \(D\) matrix: 50,000 × 599 = 30 million entries
But each row of \(D\) has only one 1 per FE (and zeros elsewhere). With 2 FEs, each row has exactly 2 non-zero entries out of 599 columns — that’s 99.7% zeros.
The Solution: Sparse Matrix Representation
Sparse matrices store only non-zero entries as (row, column, value) triplets:
Representation
Storage for G=50K, F=600
Dense matrix
30 million floats (~240 MB)
Sparse matrix
100K triplets (~2.4 MB)
Savings
~100x less memory
Why This Works Mathematically
The key insight is that sparse matrix operations are mathematically identical to dense operations — they just skip the zero multiplications:
Since nnz_per_row (non-zeros per row) equals the number of FEs (typically 2-3), sparse operations are O(F) faster than dense operations.
Correctness Guarantee
The sparse implementation produces bit-identical results to a dense implementation because:
Same linear algebra: \(X'X\), \(X'y\), and \((X'X)^{-1}\) are computed identically
Same sufficient statistics: YOCO’s n, sum(y), sum(y²) are unchanged
Same standard errors: The sandwich formula uses the same matrices
We validate this in our test suite by comparing sparse vs dense results.
Performance Impact
Operation
Dense
Sparse
Speedup
Build design matrix
O(G × F)
O(G × n_fe)
~100x
Matrix multiply X’X
O(G × F²)
O(G × n_fe × F)
~100x
Memory allocation
O(G × F)
O(G × n_fe)
~100x
Where n_fe is the number of FE columns (typically 2-3) and F is total FE levels (can be 1000s).
Implementation Details
Python uses scipy.sparse.csr_matrix:
from scipy import sparse# Build in COO format (fast construction)X_fe = sparse.coo_matrix((data, (rows, cols)), shape=(G, F-1))# Convert to CSR for fast arithmeticX_fe = X_fe.tocsr()# Combine with regressorsX = sparse.hstack([X_reg_sparse, X_fe], format='csr')