Derive artifact-free, perfectly nested multiscale analysis windows from any discrete data. Also includes every major bin width estimation method in one place.
pip install binjaminStandard multiscale analysis requires choosing window sizes. Those choices introduce boundary artifacts, inconsistent nesting, and results that depend on the analyst. Binjamin derives the measurement space from arithmetic — the windows are forced by the math, not chosen.
import binjamin as bj
geo = bj.lattice([10, 30, 60, 360])That's it. You get a complete measurement space:
geo.cbin # 10 — computational resolution (gcd of windows)
geo.horizon # 360 — lcm of windows
geo.windows # (10, 12, 15, 18, 20, 30, 36, 40, 60, 90, 120, 180, 360)
geo.prime_basis # {2: 2, 3: 2} — the geometry of scale
Every window divides the horizon. Every window nests perfectly into every larger window. No boundary artifacts. The structure comes from the divisibility lattice — the unique maximal family of window sizes with these properties.
# EEG at 256 Hz — windows in samples
geo = bj.lattice([256, 1024, 4096, 15360], data=sample_indices)
geo.data_grain # estimated from data
geo.cbin # 256 — analysis resolution
geo.grain # 1 — can zoom finer laterbj.freedman_diaconis(intervals) # robust, no assumption
bj.auto(intervals) # good default
edges, counts = bj.bin(data) # bin and countDerive a complete measurement space from analysis windows.
geo = bj.lattice(
windows=[10, 30, 60, 360], # desired analysis scales
grain=1, # finest resolution (optional)
cbin=10, # materialized resolution (optional)
data=orders, # data for grain estimation (optional)
horizon=720, # override horizon (optional)
)Usually you just pass windows. Everything else is derived. See docs/lattice.md for details.
LatticeGeometry fields:
| Field | Meaning |
|---|---|
data_grain |
Estimated from data (raw cadence) |
grain |
Finest admissible resolution |
cbin |
Materialized resolution (divides all windows) |
horizon |
Outer boundary of coordinate space |
windows |
All valid scales in the active domain |
prime_basis |
Prime factorization of horizon // cbin |
coordinates |
Prime exponent vector per window |
members |
Full set of lattice members |
Every integer has a unique prime factorization. Binjamin exposes this as a coordinate system.
bj.factorize(360) # {2: 3, 3: 2, 5: 1}
bj.divisors(12) # (1, 2, 3, 4, 6, 12)
bj.lattice_members(360, 10) # (10, 20, 30, ..., 360)
bj.smallest_divisor_gte(360, 50) # 60
bj.to_int({2: 3, 3: 2, 5: 1}) # 360Vector arithmetic mirrors integer operations:
bj.vec_add({2: 1}, {3: 1}) # {2: 1, 3: 1} — multiplication
bj.vec_sub({2: 2, 3: 1}, {2: 1}) # {2: 1, 3: 1} — division
bj.vec_le({2: 1}, {2: 2, 3: 1}) # True — divisibility
bj.vec_min({2: 3}, {2: 1, 5: 2}) # {2: 1} — gcd
bj.vec_max({2: 3}, {2: 1, 5: 2}) # {2: 3, 5: 2} — lcmSee docs/coordinates.md for the full API and the math behind it.
Every major method. All scalar methods take a 1-D array, return a float.
| Method | Best for |
|---|---|
auto |
General default |
freedman_diaconis |
Unknown distribution, outliers |
scott |
Near-normal data |
sturges |
Small, near-normal |
rice |
Simple, no assumption |
sqrt |
Quick exploratory |
doane |
Skewed or multimodal |
stone |
Accuracy over speed |
knuth |
Optimal uniform bins |
gcd_interval |
Integer sequences, regular data |
bayesian_blocks |
Non-stationary event data (variable-width) |
See docs/methods.md for formulas and usage guidance.
bj.grain_from_orders(orders) # Freedman-Diaconis default
bj.grain_from_orders(orders, method="gcd_interval") # exact for regular data
bj.suggest_cbin(grain=60, windows=[3600, 86400]) # → 60| Doc | What it covers |
|---|---|
| Lattice geometry | LatticeGeometry, derivation logic, grain/cbin/horizon |
| Coordinates | Prime factorization, divisors, vector arithmetic |
| Methods | Bin width estimation formulas and guidance |
SignalForge — multiscale signal analysis on the p-adic divisibility lattice. Binjamin provides the mathematical substrate; SignalForge provides signals, surfaces, and the exploration tools.