Catalog by Segment — segmentation according to streaming volume and speed of decay · click a bar to filter
How segments work. Every album is scored on its streaming volume — a blend of its peak day, its lifetime total, and its current (trailing 28-day) streams — ranked against every other album in the catalog. The catalog is then split into three tiers at the natural gaps in that distribution (via clustering, not forced-equal buckets), so tier sizes reflect real structure: emerging (smallest volume), developing (mid), and established (largest). Bigger-volume releases tend to decay more slowly, so an album's tier is a useful proxy for how its streams will hold up over time.
Methodology & formulas
📐 Decay Model
Fit an exponential decay to streams from peak day forward. The model S(t) = S₀ · e^(−λt) gives us a decay constant λ. A higher λ = faster decay.
S(t) = S₀ · exp(−λ · t)
λ = −ln(S_t/S₀) / t
⏱ Half-Life
How many days since peak until streams fall to 50% of peak. Always attempts a floor-anchored two-phase fit first (the transient burn above a settled floor) — this applies whether or not the album has already flattened out, since a settled album's whole peak-to-floor trajectory is fully observed and if anything easier to separate cleanly. Falls back to a naive whole-history fit only when the two-phase fit itself isn't viable (too little post-peak data, or no real decay to fit) — that naive fallback is biased upward by the long flat tail near the floor, so it's a last resort, not a routine path for settled albums. Short half-life = fast burn. Long half-life = sustained catalogue performer.
t½ = ln(2) / λ
≈ 0.693 / λ
📊 Log Returns (μ, σ)
Computed at the data's native grain, never on interpolated values. Daily sources: r_t = ln(S_t / S_{t−1}) on the winsorized daily series. Weekly sources: W_t = streams per day in week t (normalizes partial weeks), r_t = ln(W_t / W_{t−1}) between consecutive weeks only — reporting gaps are excluded, μ/σ annualize by ×52, and μ/σ are withheld below 8 returns. Daily volatility is never derived from weekly totals.
r_t = ln(S_t / S_{t-1}) (native grain)
μ = mean(r) · σ = stdev(r)
🔮 Forecast
Lifecycle curve: pre-release single spikes → street peak → two-phase decay S(t) = A·e^(−λt) + C → floor drifting at g/yr, out to 15 years. Defaults seed from catalog medians. Bands are Monte Carlo (seeded, reproducible): parameter risk on peak/burst/half-life/floor/drift (median-preserving lognormal jitter — deliberately not the classic mean-preserving form, which drags the reported "central" line down as jitter widens) carries the long-run spread; short-run noise is a mean-one AR(1) on log-level calibrated to the observed daily σ. Conservative/Realistic/Optimistic (P5/Median/P95) are empirical per-day quantiles (median, not mean, so a fat right tail of a few extreme paths doesn't skew the "central" line); cumulative bands are quantiles of per-path cumulative sums. A Target slider then picks one point along this same Conservative–Realistic–Optimistic range as the committed export goal, rather than an arbitrary markup on top.
S(t) = A·e^(−λt) + C·e^(g·t/365)
path = S(t) · exp(x_t), x_t = ρx_{t−1} + ε
✂️ Winsorization
Clip daily stream values at the 2nd and 98th percentile before fitting. Prevents anomalous playlist adds or data gaps from skewing decay parameters.
lo = percentile(S, 2)
hi = percentile(S, 98)
S_w = clamp(S, lo, hi)
🔁 Pre-Release Ratio
Compare the highest single-day stream count before release date to the release-week peak. A high ratio indicates a strong singles campaign that may front-load discovery.
ratio = max(S_pre) / max(S_post)
singles_lift = peak_pre / baseline_pre
📈 Forecast Handoff
Once decay parameters (λ, S₀, t½) are established from backtesting, seed a future projection using an observed current level as S₀ and the fitted λ as the forward rate.
S_forecast(t) = S_now · exp(−λ · t)
revenue(t) = S_forecast(t) · rate_per_stream
🔍 Pattern Search
Tag albums by decay shape: sharp-then-flat (catalogue), linear, double-hump (late viral). Use λ, half-life, and pre/post ratio together as a fingerprint to match against future releases.
fingerprint = {λ, t½, pre_ratio,
μ, σ, peak_day_offset}