📟  Data Analytics layer · backfill

how-to-biz-monitoring

Everrest watches 15 daily revenue series. On Monday morning, which of last week's movements are real enough to act on, and what actually caused them? This page builds the monitor in the order that lets you answer that - baseline, then calibration, then multiplicity, then the threshold - and scores every rung against changes planted at known times, so recall is measured instead of claimed. The measured answer is not flattering, and it is on the page.

raw dump→lake→warehouse→marts→the thing that watches the marts
seed = 42 pandas · scipy · matplotlib 15 series · 365 days · 17 charts 7 rungs · 20 replicates · 240 planted changes regression suite 31/31
01
Step 1

Input - a board, and the decision of what belongs on it

The marts hold a year of delivered orders across 8 categories and 6 regions. The obvious board is every combination - 48 slices plus a total - and it is the wrong board. Which series you watch is a monitoring decision with a right answer, and it is made before any threshold exists.

the board 15 series
  • TOTAL · delivered GMV per day
  • 8 categories · median $1,565/day
  • 6 regions · median $1,932/day
  • 365 days, 01 Jul 2025 to 30 Jun 2026
  • every one has volume every day
excluded 48 slices
  • category × region
  • median 17% of days are empty
  • worst slice 41% empty
  • median $167/day, CV 1.29
  • count data wearing a revenue label
read from stage 3
  • orders · 50,600 rows
  • order_items · 109,154 rows
  • merchants / customers
  • delivered status only
  • GMV; platform take is 11%
Why 48 slices were thrown away

A z-score assumes a continuous quantity with a stable spread. Run one over a series that is empty on a sixth of its days and it manufactures alerts out of arithmetic - the model is simply wrong about what kind of number it is looking at. Those slices need a count model, which this skill does not ship, so they are excluded loudly rather than monitored badly. They come back in Step 5 for attribution, where the maths is exact arithmetic on sums and sparsity does not matter.

02
Step 2

Sample data - a real defect, and 240 changes planted on purpose

Two kinds of data are needed and they do different jobs. The real marts show what an on-call would actually have received. They cannot show what was missed, because nobody knows - so a second, simulated panel is fitted to the real one's seasonality, weekday shape and noise, and then changes of known size are planted in it at known times. That is the only place on this page where recall is a measured quantity.

In the real marts
A duplicate load

600 order ids appear twice, all in March 2026. The join to order_items fans them out.

$125,738 of phantom GMV
In the real marts
A planned annual peak

November runs about 2.2x baseline. Everybody in the business knew it was coming.

Known, real, and not an incident
Planted, simulated
Steps and ramps

Level shifts of -30%, -20% and +25% over 45 days; ramps to -35% and +30% over 28.

The sustained-change cases
Planted, simulated
One-day spikes

A single day at +120%. A different detection problem from everything above it.

The transient case
A defect the build nearly created

The items mart also holds 1,261 exactly duplicated rows, and deduplicating those looks like extra hygiene. It is not: order_items has no line-level key, so two identical lines on one order are a legitimate shape, and those rows are spread evenly across all twelve months at about one percent - tracking volume, not the March incident. Dropping them stripped $50,647 of real revenue from the year and contaminated the baseline the entire bench is fitted on. Only the duplicated order ids are the defect.

03
Step 3

Objective - a decision, not a technique

"Which of last week's movements are real enough to act on, and what actually caused them?"

04
Step 4

Find-skills - what this needs, and what it deliberately does not

Almost every anomaly-detection library available would have produced a working monitor faster. None of them would have produced a measured recall, which is the whole point, so the detectors here are written out in full and the libraries are used for the parts that are genuinely solved.

scipy.stats

Student-t fitting for the empirical null, the normal survival function, and nothing else. The multiple-testing step is written out rather than imported so it can be tested against hand-computed cases.

pandas · numpy

The panel, the trailing windows, the exact decompositions. Every window is positional over a complete daily index.

how-to-data-quality sibling

The reconciliation gate that actually catches the March duplicate load. Step 5 shows why no threshold on a metric ever will.

how-to-metric-to-scorecard sibling

Owns the metric definitions and the 11% take rate. This page reports GMV and says so, rather than inventing a second definition of revenue.

statsmodels not used

seasonal_decompose is centered by default, which puts the day under test inside its own expectation. Step 5 measures what that costs instead of asserting it.

Prophet, ARIMA not used

A better forecast is not the bottleneck here. The bottleneck is that a 30% drop is smaller than one day of noise, and no forecaster fixes that.

05
Step 5

Build - a ladder, a bench, and an answer nobody wants

One script runs everything: the ladder on the real year, the ladder against planted truth, the cadence measurement, and the attribution. Every number below comes out of that run.

A. What the year actually looked like

15 series is 15 chances a day to be surprised, and a percent-change rule takes every one of them.

The 15 watched series over a year, with the total scaled to share the axis
The board. One total, eight categories, six regions. The total is divided by 14 to share the axis; the November peak is visible in every series at once, which is the first hint that these tests are not independent.
Alerts per year for each rung of the ladder on the real Everrest year, log scale
3,796 alerts, or 36. A week-over-week 15 percent rule fires about ten times every day of the year across this board. The calibrated, FDR-controlled rung brings the same year to 36 - roughly one a fortnight, which a person can actually read.
Alerts by month, with November and December carrying most of them
A monitor reporting the calendar. Two months carry 35 percent of the year's alerts, and they are the two months anybody in the business could have named in advance.
November actual against expected with a 3-sigma band, alerts marked
November is real, known, and fires anyway. A trailing baseline learns an annual event only after it has paid for it once. Known events belong in a calendar the monitor can read - which this skill recommends and does not ship.
The loudest defect of the year is invisible to every rung

The March duplicate load inflated the month by $125,738 of GMV - 18 percent of the month as booked. It lands on all 31 days at a median 24 percent, so it is a sustained uniform shift rather than a spike, and the baseline simply learns it. Removing the duplicates moves March from 18 alerts to 18 on the robust rung, and 15 to 13 on the FDR rung. Anomaly detection is not data quality. This class of defect is caught by a reconciliation check on row counts and key uniqueness, and never by a threshold on a metric.

March as loaded versus deduplicated, and the alert counts before and after, which barely move

All money on this page is GMV. Everrest takes 11 percent, so the March overstatement is about $13,800 of platform revenue - a mart defect to reconcile, not a ledger entry.

B. Two fixes that cost one line each

Before any threshold is chosen, two things about the residual decide how the monitor will behave.

False alarm rate and residual skew for additive versus log residuals
Divide, do not subtract. Revenue is multiplicative and right-skewed, so actual - expected is skewed +6.6 and trips a nominal 3-sigma threshold on 5.5% of series-days. log(actual / expected) is skewed +0.15 and trips it on 1.1% - 5.1x fewer false alarms, before any threshold is touched.
Distribution of z on anomaly-free data against the assumed normal, and exceedance rate versus threshold
3-sigma fires 4x more than it promises - on data with no anomaly in it. And the cause is over-dispersion, not fat tails: the fitted shape is near-normal, but z has a standard deviation of 1.13 rather than 1, because the denominator is a MAD from a few dozen points. The fix is to estimate the scale, not to reach for a heavier-tailed distribution.
Recall by injection kind for trailing versus centered baselines
The baseline must be causal. A centered window puts the day under test inside its own expectation. Trailing wins by 17 points on steps and 10 on ramps - but loses 2 on one-day spikes, which is inside noise. The honest rule is narrower than the usual one: never let the day into its own baseline when the change you fear is sustained.

C. The ladder, and why its table cannot pick a winner

Seven rungs, each fixing one named defect in the one below. Scored against 240 planted changes across 20 replicates.

rungthe defect it fixesrecallprecisionlift vs chancelag (days)false alarms/yr
D1 week-over-week 15%what most teams ship98%7.5%0.99x04,121
D2 3-sigma on levelno notion of variability48%7.4%0.98x10145
D3 3-sigma on residualthe level is seasonal23%10.2%1.35x1433
D4 robust 3-sigmasd is dragged by the last incident23%8.2%1.08x1458
D5 calibrated null3-sigma is not a 0.27% rule6%9.6%1.27x2110
D6 calibrated + FDRmany series, many chances7%8.4%1.11x2113
D7 FDR + persistencea one-day wobble is not an incident0%---1

D7 is dead on this board - under two alerts a year, detecting nothing. It is reported rather than dropped, because a rung that is silent is an outcome and hiding it would flatter the ladder.

The number that decides whether any of this works

7.6 percent of live series-days sit inside a planted change, so a detector alerting at random scores 7.6 percent precision. Measured precision across the ladder runs 7.4 to 10.2 percent. The best lift over chance is 1.35x and two rungs score below chance. Precision in the single digits reads as weak but real until you put the base rate next to it - at which point no rung here has demonstrated meaningful discriminative power. That is the finding, not a caveat.

Recall and precision per rung
Recall falls as the rungs get stricter. Precision barely moves, because it is pinned near the base rate throughout.
False alarms per year per rung, injected run and null run, log scale
The null run is the honest column. These replicates contain no planted change at all, and the noisiest rung still fires 4,452 alerts a year on them. A bench that cannot report a bad number is not a bench.
Recall per rung split by injection kind
A single headline recall hides the blind spot. Averaging an easy 30 percent step with a slow ramp and a one-day spike flatters whichever mix happens to be planted.
Median detection lag per rung
Precision is bought with days. Every rung that raises its bar also arrives later, and a page showing only precision would be advertising a monitor that is quiet because it is late.

D. The same ladder at a matched alert budget

The table above compares seven rules at seven arbitrary thresholds, and recall moves with the threshold. Retuning every rung to the same false-alarm budget is the only comparison that isolates the idea from its operating point - and it changes the answer.

Recall per rung when each is retuned to about 35 false alarms per year
Held to 35 false alarms a year, the ordering reverses

D2 on the raw level reaches 32 percent recall at k=5, D3 reaches 22 percent, and the calibrated rungs land at 17 and 16. The week-over-week rule cannot be tuned onto the budget at all within a sane range - at a 250 percent threshold it still fires 145 times a year, which is itself the finding: a rule with no usable operating point is not a strict rule, it is the wrong rule. Comparing detectors at their default thresholds measures the thresholds.

E. Why the whole ladder is fighting the wrong battle

Before tuning anything, compute how large your real incident is in units of one bucket's noise.

Signal-to-noise of a 30 percent drop and residual scale per cadence
A 30 percent revenue drop is 0.29 daily sigma

A genuine commercial emergency is worth less than a third of one day's noise on this board. No threshold separates that from an ordinary Tuesday, and lowering the threshold buys every Tuesday instead. At a fortnightly bucket the same drop is 1.02 sigma. Two real mechanisms contribute: the noise per bucket falls from 0.52 to 0.20 log units, and the trailing window stops eating the signal - 57 percent of a 45-day step has already been learned away at daily cadence. A monitor that stops alerting is not evidence the incident ended. The first monitoring decision is the bucket, not the threshold, and it is usually made by accident, by inheriting a daily dashboard.

End-to-end recall by cadence is deliberately not reported. One year of Everrest yields 26 fortnightly buckets against a 44-bucket warmup, so a fortnightly monitor on this history never becomes live at all - any recall comparison would measure how much history each cadence had. The signal-to-noise above is a property of the data and survives that objection; a recall number would not.

F. When an alert is real, say what moved

Detection needs a distributional assumption and so has to respect sparsity. Decomposition is exact arithmetic on sums and does not, so attribution runs at the fine grain the monitor refuses.

The eight largest category-by-region contributions to November growth
November added $482,301 of GMV, and it is broad. 47 of 48 slices grew; the largest eight account for only 37 percent and it takes 27 rows to reach 80. There is no short drill-down here, and saying otherwise would be the more flattering answer rather than the measured one.
Mix and rate effects per category on average order value
Average order value fell $0.99, and the two halves point opposite ways. Mix contributed +$0.83 and helped - volume moved toward Grocery, the most expensive category at $214 an order. Rate contributed -$1.82. Reading this as a basket story sends you to the wrong place.
The rate term is one category

Beauty lost $29.04 of its own average order value, down 20 percent, contributing -$3.88 against a net rate effect of -$1.82. Five of eight categories raised their own average and it did not matter, because the three that fell carry 39 percent of orders. The action is to go and look at Beauty - which is not what a mix-shift headline on its own would have told anyone to do. A threshold on average order value would have fired on -$0.99 and said nothing about any of this.

Mix plus rate equals the observed change exactly

Both decompositions reconcile exactly - segment effects sum to the observed total to floating point, asserted in code against the source totals rather than against the frame built from them - so no slice can be dropped to make an argument.

G. The artifact

A weekly digest whose every figure is computed, that shows the rows which actually crossed the threshold, and that states how often its own rule is wrong.

Open the weekly digest → Self-contained HTML. Opens from a file path, no server.
06
Step 6

Expert review - the panel reversed one of this page's conclusions

Five senior reviewer agents - 12 to 15 years each in detection theory, on-call observability, analytics leadership, data engineering and commercial P&L - were pointed at the code and the executed outputs. They did not describe defects; they ran them. One reviewer proved that a headline finding was an artifact of the build's own bug, and it was the finding the page had been built around.

The finding that was deleted

This page originally argued that a 3-sigma rule is badly miscalibrated because the null is heavy-tailed - fitted at about 3 degrees of freedom. It is not. The null was being fitted on the whole year including the planted changes, and because the shape anchor is the 99.5th percentile, two dozen incidents is exactly the amount of contamination that moves it. Fitted causally on trailing data with candidate events trimmed, the log-scale null comes back near-normal in shape, and the real residual problem turns out to be over-dispersion - z has a standard deviation of 1.13, not 1 - which the calibrated rung fixes by estimating the scale rather than assuming it. A null fitted on contaminated data widens to accommodate the incidents and then cannot see them - and it gets worse as the true incident rate rises.

Senior Statistician
15 yrs · detection theory, multiple testing

"Your entire precision column is the injection coverage fraction. 12 injections of mean length 32 over 4,740 scoreable series-days is 7.56 percent, and D1's published precision matches that to four decimals. You are reading the base rate and calling it a detector property. Also: the null is fitted on the whole panel, so a November incident sets the threshold applied to the previous March."

Fixed: score now returns base_rate and lift, computed from the panel width rather than the alert frame, and the page leads with them. The null is refitted every 28 days on a trailing 180-day window with candidates trimmed. Both changes are locked by regression tests.
Observability / On-call Lead
12 yrs · revenue-system rotations

"The digest prints one rung's measured error rates above a different rung's alerts. That is the single worst thing a page about honest error rates can do. And the table labelled 'largest deviations' contains no alerts at all - every row is sub-threshold, so an on-call opening this gets a leaderboard of non-events while the 13 real alerts appear nowhere."

Fixed: render_digest now takes the detector callable and its name, derives the alerts itself, and asserts its observed rate against the bench row. Alerts and sub-threshold moves are separate sections, and the alert count is stated even when it is zero.
Head of Analytics
15 yrs · marketplace insight

"Finding 25 is the page's conclusion and both halves of its causal story are the reverse of what the code computed. Mix is +$0.84 - it pushed AOV up - and it is written as a component of the drop. 'No category got worse at selling' is contradicted by three of eight, one of them by $29."

Fixed: the sentence is now generated from the signs of the two terms, with three branches, and a regression test asserts the rate-dominated case never blames the basket. The page now names Beauty.
Staff Data Engineer
12 yrs · analytics platforms

"The cadence sweep resamples with label='right', so the bucket stamped on the injection start covers the period before the change - 17 percent contamination at fortnightly, growing with exactly the variable you are comparing. And the fortnightly row is the mean of 9 replicates while every other row is 12; the only trace is three RuntimeWarnings on stderr."

Fixed: label='left' with an explicit span test, injections moved past the slowest cadence's warmup, and the sweep now raises rather than silently dropping a replicate. Reruns verified byte-identical from a frozen snapshot.
Commercial Director
15 yrs · SE Asia marketplace P&L

"Every dollar here is GMV and not one word says so. '$132,060 of phantom revenue' in a room where revenue means the P&L line is a misstatement claim - the real number is $14.5k. And nothing was 'booked'; a duplicate load in an analytics mart is not a ledger entry."

Fixed: a single TAKE_RATE constant, every headline figure quoted as GMV with the platform take beside it, and the March finding rewritten to name what actually needs reconciling downstream.
Senior Statistician
second pass · the ladder's logic

"No rung-to-rung comparison holds the alert budget fixed, so the ladder narrative is unidentified. And D6 does not correct for multiplicity, it relaxes the test - BH at q=0.05 over 15 tests is looser than alpha=0.0027 at every step, so the rung labelled 'multiplicity handled' doubles the false alarms."

Fixed: a whole new section retunes every rung to 35 false alarms a year, and the ordering reverses. The D5-to-D6 prose now says plainly that BH buys recall with budget and that the board is nested, so one incident lights up three series.
What the panel did not get fixed

Three findings are named on the page and in SKILL.md rather than resolved. There is no known-event calendar, so November still fires. There is no alert grouping, so every count here is an alert count and never an incident count. And per-alert precision still rewards a detector for staying on - an episode-level figure is computed alongside it, but the ladder table reports both rather than pretending the question is settled.

Run this on your own metrics

Point monitor.py at a CSV of daily series. It prints the suitability report first - which series are dense enough to monitor at all - then the fitted null, then the alerts. The suitability report is the part to read.

terminal
/plugin marketplace add phoebefu6/phoebe-data-skills
/plugin install how-to-biz-monitoring@phoebe-data-skills