The assignment functions return NumPy arrays. Every stochastic function accepts either a NumPy Generator or an integer seed through rng; no function mutates NumPy’s global random state.
Complete versus simple assignment
Under simple assignment, units draw conditions independently. Under complete assignment, arm totals are fixed. If a requested probability does not imply an integer arm total, complete assignment stochastically rounds the total so that each unit’s marginal assignment probability remains exactly the requested value.
import altair as altimport numpy as npimport polars as plimport declaredesign as ddrng = np.random.default_rng(2026)records = []for simulation inrange(600): records.extend([ {"design": "Simple","treated": int(dd.simple_ra(101, prob=0.35, rng=rng).sum()), }, {"design": "Complete","treated": int(dd.complete_ra(101, prob=0.35, rng=rng).sum()), }, ])assignment_counts = pl.DataFrame(records)assignment_counts.group_by("design").agg( pl.col("treated").mean().alias("mean treated"), pl.col("treated").std().alias("sd treated"),)
shape: (2, 3)
design
mean treated
sd treated
str
f64
f64
"Simple"
35.201667
4.909634
"Complete"
35.328333
0.469999
Complete random assignment removes arm-size variation without changing marginal assignment probabilities.
block_and_cluster_ra() assigns whole clusters within blocks. Sampling analogues use the *_rs suffix and return zero-one inclusion arrays. Reusable declare_ra() and declare_rs() objects store a design separately from a draw.