Quick Start Guide¶
Streaming Raking¶
StreamingRaker weights each row as it arrives and keeps only running
totals, so memory does not grow with the stream:
from onlinerake import StreamingRaker, Targets
raker = StreamingRaker(Targets(female=0.5), outcomes=["spend"])
for female, spend in [(1, 10.0), (1, 12.0), (1, 11.0), (0, 20.0)] * 50:
weight = raker.partial_fit({"female": female}, outcomes={"spend": spend})
print(round(raker.margins["female"], 2)) # 0.52
print(round(raker.estimate("spend"), 1)) # 15.3
- calibration_strength
Penalty on the running gap between weighted and target totals (default 0.3). Larger values match the targets more closely and make weights more variable.
- design_weight
Pass
design_weight=topartial_fitfor rows with base weights, in any units. Returned weights are relative to the mean design weight so far, and the boundsmin_weightandmax_weightapply to the ratio of a row’s weight to its design weight.- Units
The penalty divides design weights by their running mean and continuous features by the running root mean square of their deviations from target, so results are the same for income in dollars or in thousands.
- trim
Cap each weight at this multiple of the mean weight assigned so far, as in Potter’s weight trimming; 3 to 6 is common. Trimming lowers the variance of estimates and can bias them when large design weights carry information the margins do not. Off by default.
- keep_weights
StreamingRaker(..., keep_weights=True)stores each assigned weight in.weights. Memory then grows with the stream.
When the rows can be stored, refitting BatchIPF periodically is exact at
each refit and is the better choice for survey-sized streams.
Online Classes That Store Every Row¶
OnlineRakingSGD and OnlineRakingMWU store every row and re-optimize
all weights at each arrival, so their cost per row grows with the stream:
SGD Raking - Stochastic gradient descent with smooth updates
MWU Raking - Multiplicative weights with exponential updates
Both follow the same API pattern:
from onlinerake import OnlineRakingSGD, OnlineRakingMWU, Targets
# Define target population proportions
targets = Targets(
age=0.52, # 52% over 35 years old
gender=0.51, # 51% female
education=0.35, # 35% college educated
region=0.19 # 19% rural
)
raker = OnlineRakingSGD(targets, learning_rate=3.0)
observations = [
{"age": 1, "gender": 0, "education": 1, "region": 0},
{"age": 0, "gender": 1, "education": 0, "region": 1},
]
for obs in observations:
raker.partial_fit(obs)
print(f"Weighted margins: {raker.margins}")
print(f"Effective sample size: {raker.effective_sample_size}")
print(f"Loss: {raker.loss}")
Key Concepts¶
- Targets
Population proportions you want to match. Each field represents the proportion with indicator value 1 (e.g., female=1, male=0).
- Observations
Binary demographic indicators, provided as dictionaries or objects with
age,gender,education,regionattributes.- Margins
Current weighted proportions after processing all observations so far.
- Effective Sample Size
Measure of how “concentrated” the weights are. Higher is better.
- Loss
Squared error between current margins and targets. Lower is better.
Algorithm Choice¶
Use SGD when: - You prefer additive weight adjustments - Smooth weight trajectories are important - You can tune learning rates appropriately
Use MWU when: - You prefer multiplicative (percentage-based) adjustments - You want multiplicative updates with explicit weight bounds
Parameter Tuning¶
Learning Rate - SGD: Start with 3.0-5.0, increase if convergence is slow - MWU: Start with 1.0-1.5, decrease if weights become unstable
Weight Bounds
- min_weight: Prevents weights from collapsing (default: 1e-3)
- max_weight: Prevents runaway weights (default: 100.0)
Update Steps
- n_sgd_steps (both methods): More steps = more aggressive updates (default: 3)
Next Steps¶
See Examples for worked examples
Check API Reference for complete parameter descriptions
Browse examples/ for realistic end-to-end use cases