Portfolio Anomaly Detection on Building Energy Telemetry

PYTHON · ML · CLOUD · Commercial property portfolio, 62 buildings

GridWatch

Anomaly Detection on Building Energy Telemetry

2026 13 weeks 7 min read

Every one of 1,480 half-hourly meters across a 62-building commercial portfolio was recording, and nothing was reading them. A fault surfaced when a quarterly bill looked high — typically 60 to 90 days after a chiller had started running through the night. I built unsupervised anomaly detection on the telemetry that raises a ranked, costed alert to the facilities team within hours.

  • €318k annualised energy waste identified across the portfolio
  • 4.2 h median detection time, from 60+ days
  • 1,480 half-hourly meters monitored continuously
Screenshot of the Anomaly Detection on Building Energy Telemetry dashboard: three headline metric cards above a bar chart of median time to detect an energy anomaly, 1512 hours at Baseline down to 4.2 by Wk 14.
Unsupervised detection across 1,480 meters that finds a failing plant item in hours instead of at the quarterly bill.

01 — Context

The problem.

Half-hourly data existed for every meter and was effectively unread. With 1,480 series there was no realistic way for a facilities team to eyeball them, and the few thresholds someone had configured were static — so they fired constantly in January and never in June. Most alerts were ignored, which is the normal end state for an alert nobody trusts.

The failures being missed were mundane and expensive: schedules left on after a bank holiday, a stuck valve, simultaneous heating and cooling, a chiller cycling all night in an empty building.

02 — Method

The approach.

The build, in the order it happened.

  1. A baseline per meter, not per portfolio

    Each meter gets its own expected profile from a seasonal-trend decomposition conditioned on day type, occupancy schedule and heating and cooling degree days. Anomaly means departure from that meter’s own expectation, which is the only definition that survives a portfolio this varied.

  2. Two detectors, deliberately different

    A residual-based statistical detector catches sustained level shifts; an isolation forest over shape features catches profile changes that leave the daily total unchanged — the overnight-running case that a total-consumption check misses entirely.

  3. Ranked by euros, not by z-score

    Every alert carries an estimated cost per day using the site’s actual tariff and standing charges. The facilities team works a list ordered by money, which is what made them work the list at all.

  4. Closed the loop on outcomes

    Each alert is closed with a cause code by the engineer who attended. Those codes tune the detectors and, after ten weeks, let false positives be suppressed by pattern rather than by raising a global threshold.

03 — Outcome

What changed.

  • Median detection time fell from over 60 days to 4.2 hours from the onset of an anomaly.
  • €318k of annualised waste identified in the first two quarters, of which €241k was remediated within the period.
  • Alert precision improved from 21% at launch to 68% by week twelve as cause codes fed back into the detectors.
  • The single largest find was a chiller running unoccupied overnight in one building for an estimated 11 weeks, at roughly €640 a week.

04 — The numbers

Median time to detect an energy anomaly.

Tracked as hours, from Baseline through Wk 14 — low 4.2, high 1512.

GridWatch — Median time to detect an energy anomaly, hours, 2026.

06 — Stack & role

Built with.

  • Python (Pandas, scikit-learn, statsmodels)
  • BigQuery
  • Cloud Functions
  • SQL
  • Looker Studio
Role
Consulting data scientist — detection design, deployment, FM handover
Duration
13 weeks
Client
Commercial property portfolio, 62 buildings
Period
2026

07 — Questions

The questions I get asked about this one.

There were no labelled faults to train on — that was the whole problem. Unsupervised detection plus engineer-supplied cause codes bootstraps the labels, and a supervised layer becomes viable once enough have accumulated.

Heating and cooling degree days are covariates in each meter’s baseline, so a cold snap raises the expectation instead of raising 1,480 alerts.

Ranking by cost and closing every alert with a cause code. An alert with a euro figure attached gets attended; a z-score does not.

Got a report that takes two days to assemble?

That is usually a one-week fix. Tell me what you are reconciling by hand and I will tell you what I would automate first.

Ask the AI Advisor