Meet the Dataset

A fourteen-day hourly snapshot of a mid-size grid: two renewable generators and one demand curve.

Column Meaning
timestamp hourly index, 14 days
solar_mw solar generation MW
wind_mw wind generation MW
demand_mw grid demand MW
df.columns.tolist()
# ['solar_mw', 'wind_mw', 'demand_mw']
# 336 hourly rows, one per hour from 2026-09-01 to 2026-09-14

Note

All data is generated inline in the next cell — no downloads.

(336, 3)
solar_mw wind_mw demand_mw
timestamp
2026-09-01 00:00:00 0.0 22.00 55.75
2026-09-01 01:00:00 0.0 17.97 53.04
2026-09-01 02:00:00 0.0 20.09 51.38
2026-09-01 03:00:00 0.0 27.13 57.32
2026-09-01 04:00:00 0.0 30.00 56.01
2026-09-01 05:00:00 0.0 16.22 61.67

First Look

We build the chart in two animated steps.

import plotly.express as px

fig = px.line(
    df,
)
fig

First Look

import plotly.express as px

fig = px.line(
    df[["solar_mw", "wind_mw", "demand_mw"]],
    labels={"index": "Time"},
    title="Hourly Generation vs Demand (MW)",
)
fig.update_layout(height=380, margin=dict(t=40, b=10, l=10, r=10))
fig

The Renewable Share

Now we compute renewable generation as a share of demand — the single number executives ask for first.

import plotly.express as px

df["renewable_mw"] = (df["solar_mw"] + df["wind_mw"]).round(2)
df["renewable_share_pct"] = (100.0 * df["renewable_mw"] / df["demand_mw"]).round(1)

daily = df.resample("D")["renewable_share_pct"].mean().round(1)
fig = px.bar(
    daily,
    labels={"index": "Day", "value": "Avg renewable share (% of demand)"},
    title="Daily Renewable Share of Demand",
)
fig.update_layout(height=340, margin=dict(t=40, b=10, l=10, r=10))  # noqa: C408
fig

Deficit Hours

Deficit hours are the times when renewables fall short of demand and the grid leans on storage or backup gas — let’s count them.

deficit = df[df["renewable_mw"] < df["demand_mw"]]
print(
    f"Deficit hours: {len(deficit)} of {len(df)} ({100.0 * len(deficit) / len(df):.1f}%)"
)

worst = (
    deficit.groupby(deficit.index.date)["demand_mw"]
    .count()
    .sort_values(ascending=False)
)
print("Worst day:", worst.index[0], "with", int(worst.iloc[0]), "deficit hours")
Deficit hours: 323 of 336 (96.1%)
Worst day: 2026-09-01 with 24 deficit hours

What the Numbers Mean

  • Renewables dominate midday but collapse to zero overnight.
  • Deficit hours concentrate in the evening ramp and late night.
  • The worst day is driven by a wind lull, not a solar shortfall.

Tip

Operational takeaway: schedule storage discharge for the 18:00–24:00 window — that is where the renewable share drops fastest.

The Companion Dashboard

Everything you just computed — renewable share, deficit hours, worst-day rollups — runs on the same inline dataset inside an interactive companion dashboard embedded directly on this training page. Hover, filter, and scrub through the fourteen days yourself; the numbers match this deck exactly because they are produced by the same seeded code.

Viage digital training platform dashboard

Recap & Key Takeaways

  • You can read a generation-vs-demand chart and see exactly where renewables carry the load — and where they don’t.
  • Deficit hours are a pattern, not a mystery: they cluster in the evening ramp and late night, and the worst days trace back to wind lulls.
  • Interactive material sticks: every chart in this module was produced live from the same seeded dataset you can re-run.
  • The analysis continues in the companion dashboard — the same numbers, now fully interactive.
  • Slides are a launchpad, not the destination: the same dataset powers the hands-on lab.

Tip

Enroll your team: book a live session at viagellc.com/contact and we will run this module on your own grid data.