← Back to list

Build 3 Reproducible Global Energy Charts in Python with Pandas and Matplotlib

A hands-on tutorial to create a global stackplot, a UK vs India coal crossover, and an oil price vs GDP index — all from OWID and World…

Aria Lucent in Data Science Collective · 2025-09-12 12:13 · 4 claps · 3.8 min read paywalled
#data-science #python #pandas #matplotlib #portfolio
Open on Medium ↗
Wiki topics: ML · Machine Learning MAC · Macroeconomics INV · Investing & Markets 🔬 · Science · General

Build 3 Reproducible Global Energy Charts in Python with Pandas and Matplotlib

A hands-on tutorial to create a global stackplot, a UK vs India coal crossover, and an oil price vs GDP index — all from OWID and World Bank datasets.

Introduction

Energy is not just fuel. It is power, shaping economies, nations, and crises.

Most articles on energy trends recycle the same charts. But what if you could reproduce them yourself, step by step, using live data? In this tutorial, you’ll walk through three reproducible Python visualizations that illustrate how coal, oil, gas, and renewables shaped history.

This isn’t just theory. You will code along, run real datasets from Our World in Data (OWID) and the World Bank, and leave with publication-ready figures that fit into a data science portfolio.

Why you should read this

Most energy posts stop at screenshots. When you try to reproduce them, the CSV links are broken or the units don’t line up.

In this tutorial, you’ll:

  • Load OWID and World Bank data directly from source URLs
  • Handle unit conversions and per-capita fields
  • Annotate crises like the 1973 oil shock, the 2008 financial crash, and the 2020 pandemic
  • Build three reproducible charts with clean, reusable code

What you’ll build

  1. A stackplot of global primary energy (1965–2024) with annotations for major crises
  2. A UK vs India per-capita coal chart (log scale) with automatic crossover labeling
  3. A 1970=100 indexed chart of real oil prices vs world GDP

Datasets at a glance

  • OWID Energy: consumption by source (coal, oil, gas, hydro, nuclear, renewables) in TWh, plus per-capita kWh/person fields. Coverage is strong from 1965 onward.
  • OWID Oil Prices (inflation-adjusted): real crude oil price series, suitable for historical comparisons.
  • World GDP (constant international $): long-run annual GDP series used for indexing.

All datasets are read from OWID’s open GitHub/CSV endpoints for full reproducibility.

Step 1. Load and sanity-check the data

import pandas as pd
import requests, io

def read_csv_from_url(url):
    r = requests.get(url, timeout=60)
    r.raise_for_status()
    return pd.read_csv(io.StringIO(r.text))
# Load OWID Energy
energy_url = "https://raw.githubusercontent.com/owid/energy-data/master/owid-energy-data.csv"
df_energy = read_csv_from_url(energy_url)
# Load World GDP
gdp_url = "https://ourworldindata.org/grapher/global-gdp-over-the-long-run.csv"
df_gdp = read_csv_from_url(gdp_url)
print(df_energy.shape, df_gdp.shape)

Gotchas:

  • OWID includes both country rows and “World” aggregates — filter carefully.
  • Some early years (<1965) have sparse coverage; stick to 1965+ for robustness.

Step 2. Build the global stackplot (1965–2024)

import matplotlib.pyplot as plt
import numpy as np

world = df_energy[df_energy["country"]=="World"].copy()
world = world[(world["year"]>=1965)&(world["year"]<=2024)]
fig, ax = plt.subplots(figsize=(11.5,6.8))
ax.stackplot(
    world["year"],
    world[["coal_consumption","oil_consumption","gas_consumption",
           "hydro_consumption","nuclear_consumption","renewables_consumption"]].fillna(0).T,
    labels=["Coal","Oil","Gas","Hydro","Nuclear","Renewables"], alpha=0.95
)
ax.set_title("Global Primary Energy by Source, 1965–2024")
ax.set_xlabel("Year"); ax.set_ylabel("TWh")
ax.legend(ncol=3, loc="upper left", frameon=False)
plt.show()

F1. Global Primary Energy by Source (1965–2024)

F1. Global Primary Energy by Source (1965–2024)

Interpretation:

  • Oil peaked in the 1970s just as crises hit.
  • Coal resurged in the 2000s with Asia’s growth.
  • Renewables accelerate after 2010, still modest in share.

Gotchas:

  • Units are in TWh; don’t confuse with per-capita fields.
  • Renewables include wind, solar, and bioenergy grouped together.

Step 3. UK vs India per-capita coal (log scale)

uk = df_energy[df_energy["country"]=="United Kingdom"].copy()
india = df_energy[df_energy["country"]=="India"].copy()

fig, ax = plt.subplots(figsize=(11.5,6.2))
ax.plot(uk["year"], uk["coal_consumption_per_capita"], label="United Kingdom", linewidth=2.0)
ax.plot(india["year"], india["coal_consumption_per_capita"], label="India", linewidth=2.0)
ax.set_yscale("log")
ax.set_title("Coal Consumption per Capita: UK vs India")
ax.set_xlabel("Year"); ax.set_ylabel("kWh per person")
ax.legend(loc="upper left", frameon=False)
plt.show()

Coal Consumption per Capita: United Kingdom vs India (1965–2024)

Coal Consumption per Capita: United Kingdom vs India (1965–2024)

Interpretation:

  • UK coal collapsed post-1970s.
  • India rose steadily and crossed the UK around 2016.

Gotchas:

  • Use log scale; India’s values are much smaller until recently.
  • Some per-capita fields may contain NaNs — fill or drop before plotting.

Step 4. Oil shocks vs World GDP (1970–1985)

oil_url = "https://ourworldindata.org/grapher/oil-prices-inflation-adjusted.csv"
df_oil = read_csv_from_url(oil_url)
df_oil.columns = ["entity","code","year","real_oil_price"]
df_oil = df_oil[df_oil["entity"]=="Crude oil prices"]

df_gdp_world = df_gdp[df_gdp["entity"]=="World"].copy()
df_merge = pd.merge(df_oil[["year","real_oil_price"]],
                    df_gdp_world[["year","gdp_const_intl_2021"]],
                    on="year").dropna()
base = df_merge[df_merge["year"]==1970].iloc[0]
df_merge["oil_idx"] = df_merge["real_oil_price"]/base["real_oil_price"]*100
df_merge["gdp_idx"] = df_merge["gdp_const_intl_2021"]/base["gdp_const_intl_2021"]*100
fig, ax = plt.subplots(figsize=(11,6))
ax.plot(df_merge["year"], df_merge["oil_idx"], marker="o", label="Real Oil Price (1970=100)")
ax.plot(df_merge["year"], df_merge["gdp_idx"], marker="o", label="World GDP (1970=100)")
ax.set_title("Oil Shock and the Global Economy, 1970–1985")
ax.set_xlabel("Year"); ax.set_ylabel("Index (1970=100)")
ax.legend(loc="upper left", frameon=False)
plt.show()

F3. Oil Shock and the Global Economy, 1970–1985

F3. Oil Shock and the Global Economy, 1970–1985

Interpretation:

  • Oil shocks hit hard in 1973 and 1979.
  • GDP growth bent but didn’t break.
  • Energy prices acted like the metronome of the global economy.

Gotchas:

  • Always check if base year exists (1970); otherwise pick the earliest available.
  • Convert Series to scalars with .iloc[0] to avoid FutureWarnings.

Conclusion

We built three charts from real OWID and World Bank data:

  • A stackplot of global energy by source
  • A UK vs India coal per-capita comparison
  • An indexed oil price vs GDP chart

Together, they show that energy inequality is not just about totals — it’s about who controlled resources and how crises reshaped the mix.

Follow for more reproducible data science tutorials.

Stay Connected with AriaData

If this project sparked your curiosity, there’s more to explore:

“Data is everywhere. But insight is rare. Let’s tell better stories.” — AriaData

AriaData

AriaData


메타데이터
post_id
b9bd91d16b8a
slug
build-3-reproducible-global-energy-charts-in-python-with-pandas-and-matplotlib-b9bd91d16b8a
url
https://medium.com/data-science-collective/build-3-reproducible-global-energy-charts-in-python-with-pandas-and-matplotlib-b9bd91d16b8a
canonical_url
https://medium.com/data-science-collective/build-3-reproducible-global-energy-charts-in-python-with-pandas-and-matplotlib-b9bd91d16b8a
author_url
https://medium.com/@ariadata
status
ok
fetched_at
2026-07-17 14:20:58