Data AnalyticsPortfolio projects with Indian public data

Delhi's daily peak load by discom from SLDC's five-minute data

PK
Pankit Kumar
Sr. Data Scientist at Parexel (a Goldman Sachs–backed company) · 27 September 2026 · 5 min read
In this article (7 sections)

Related programme: the Data Analytics with Generative AI course (3–4 months; ₹90,000 (inclusive of GST) · EMI from ₹4,000/month).

The short answer

When does Delhi's electricity demand peak, how high does it go, and how is the load split among the city's distribution companies? The Delhi State Load Despatch Centre (SLDC) publishes a five-minute load table for each day, so you can answer this with public data. In our September 2026 pull, the highest reading was 7,244.29 at 23:00 on 16 September, and most daily peaks fell between 22:40 and midnight rather than in the afternoon. This project collects those tables, builds a daily peak series with its timing, splits load by discom, and documents the missing days, partial days and unlabelled units you will meet.

The source

The SLDC's load data page takes a date as ?mode=dd/mm/yyyy and shows a table headed "SCADA Load for" that date. The columns are:

ColumnWhat it is
TIMESLOTFive-minute slot, 00:00 to 23:55
DELHITotal Delhi load
BRPL, BYPLBSES Rajdhani and BSES Yamuna
NDPLNorth Delhi Power Limited, the former name of today's Tata Power-DDL, still used as the column label
NDMCNew Delhi Municipal Council
MESMilitary Engineer Services

The page does not print a unit. Confirm it from SLDC's other published reports before labelling a chart axis, and say in your README where the confirmation came from.

What our pull showed

We fetched the pages for 6 to 26 September 2026 on 27 September. The quality issues are the project:

  • A missing day. The page for 8 September returned no table on two attempts.
  • A partial day. 7 September ended at 17:05, with 207 rows including the header.
  • Missing slots. Full days had 278 to 288 rows against 288 expected, and 20 September was missing its 23:55 slot.
  • Components do not add up. Across all rows, the five listed utilities summed to slightly less than the DELHI column, with a median gap of 0.95% of DELHI and never a negative one. The table does not say what the remainder is, so report it as "not attributed on this page".
  • Boundary peaks. On six days the maximum was the 00:00 slot, which belongs to the previous night's peak.

Over the days we collected, the average shares of the DELHI total were BRPL 44.5%, NDPL 27.0%, BYPL 22.4%, NDMC 4.6% and MES 0.6%. These are three weeks of data, not an annual pattern.

Where this shows up at work

Power utilities, energy traders, rooftop-solar and battery companies, and data-centre operators planning in NCR all work with load curves. The skills transfer to any high-frequency operational feed, such as call-centre volumes, app traffic or factory sensors. You need to collect politely, detect gaps, choose the right daily summary and avoid over-reading a short window.

Code: daily peaks with gap flags

The five-minute data below is synthetic. It is shaped like the SLDC table, with a planted missing day, a partial day and missing slots. The values are invented and are not Delhi's load.

python
import numpy as np
import pandas as pd

rng = np.random.default_rng(11)
slots = pd.timedelta_range("0h", "23h55m", freq="5min")
frames = []
for day in pd.date_range("2026-01-05", periods=6, freq="D"):
    if day.day == 7:
        continue                                  # missing day
    hrs = slots.total_seconds() / 3600
    base = 3000 + 900 * np.cos((hrs - 22.5) / 24 * 2 * np.pi) + rng.normal(0, 40, len(hrs))
    df = pd.DataFrame({"ts": day + slots, "TOTAL": base.round(2)})
    for name, share in [("U1", 0.45), ("U2", 0.27), ("U3", 0.22), ("U4", 0.05)]:
        df[name] = (df["TOTAL"] * share).round(2)
    if day.day == 6:
        df = df[df["ts"].dt.hour < 17]            # partial day
    frames.append(df.drop(index=df.sample(3, random_state=day.day).index))
load = pd.concat(frames, ignore_index=True)

load["date"] = load["ts"].dt.normalize()
daily = load.groupby("date").agg(rows=("TOTAL", "size"), peak=("TOTAL", "max"),
                                 peak_at=("TOTAL", lambda s: load.loc[s.idxmax(), "ts"].strftime("%H:%M")))
daily = daily.reindex(pd.date_range(load["date"].min(), load["date"].max(), freq="D"))
daily["status"] = np.select(
    [daily["rows"].isna(), daily["rows"] < 250, daily["peak_at"] == "00:00"],
    ["missing", "partial", "boundary peak"], default="ok")
daily["completeness_pct"] = (100 * daily["rows"] / 288).round(1)

parts = load[["U1", "U2", "U3", "U4"]].sum(axis=1)
load["unattributed_pct"] = 100 * (load["TOTAL"] - parts) / load["TOTAL"]
print(daily.to_string())
print("mean unattributed share:", round(load["unattributed_pct"].mean(), 2), "%")

The status column matters more than the peak itself. A partial day that stops at 17:00 misses the late-evening peak entirely. In the synthetic output its "peak" is a reading from just after midnight, the highest value before the data ran out. Averaging peaks without dropping it would bias the series downwards. The missing day stays in the table as a blank row, so gaps are visible on the chart.

Analysis plan

  1. 1Fetch one date per request with a pause between requests, save each raw page and log failures. Retry once, then record the day as missing.
  2. 2Parse the table, validate the column names and slot format, and store a tidy file of date, slot and utility load.
  3. 3Build the daily table: rows, peak, peak time, status and completeness.
  4. 4Chart the daily peak and the time of peak for complete days only, and plot the average load curve by hour for weekdays and weekends.
  5. 5Compute discom shares at the peak slot and on average, and the unattributed remainder.
  6. 6Extend the pull back through summer and compare the timing of the peak with September's.

Deliverables

  • Line chart: daily peak for complete days, with missing and partial days marked.
  • Heatmap: day against hour, coloured by load.
  • Share table: utility, average share, share at peak, and the unattributed remainder.
  • README: URL pattern, fetch dates, pause between requests, raw pages kept, gaps found, the unit and where you confirmed it, the old NDPL label.

Interview questions

  • A day's peak is at 00:00. What would you report instead?
  • Why must a partial day be excluded from an average of daily peaks?
  • The utilities do not add up to the total. What would you do before publishing shares?
  • How would you tell a real gap in the data from a failed fetch on your side?

The companion load-forecast baseline project uses the same feed for forecasting, and the CEA capacity project covers the supply side. Time-series cleaning is taught in our Data Analyst programme.

Frequently asked questions

Why do some Delhi daily peaks fall at 00:00?

When load is still rising late at night, the highest reading on a day's table can be its first slot, which belongs to the tail of the previous evening. Report a night-peak window that spans midnight, or flag peaks that fall at the boundary, rather than treating 00:00 as a midday-style peak.

PK
Pankit Kumar
Lead Instructor, NeuraPath Academy

Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.

This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.

Explore Data Analytics with Generative AI
Counselling is free · no obligation

Not sure which programme fits?

Tell us your background and we will map it to the right entry point — including saying so when a cheaper programme is the better fit. A counsellor replies within one working day.