Chris Parmer — home

Energy Sankey

where the UK's energy came from and went, restyled

Example from Plotly for highly customizable print-ready data visualization · shared helpers

Energy Sankey — where the UK's energy came from and went, restyled

Python Code

"""Energy Sankey — where the UK's energy came from and went, restyled.

The D3/plotly.js demo dataset in a print treatment: nodes coloured by the
kind of thing they are (source, carrier, sector, loss), links inheriting a
translucent version of their source's colour, and every label set in the
chart's own typeface with the totals typed in.
"""
from pathlib import Path

from _shared import fetch_json, save, base_layout, titles, INK, MUTED, FAINT, VIOLET, TEAL, GREEN, PINK, ORANGE, SLATE, GOLD, hex_to_rgba

import plotly.graph_objects as go

CHART_NUM = 12
URL = "https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json"
KIND = {
    "fossil": (["Coal", "Coal imports", "Coal reserves", "Oil", "Oil imports", "Oil reserves", "Gas", "Gas imports", "Gas reserves", "Ngas", "Petroleum", "Liquid", "Solid"], SLATE),
    "renewable": (["Wind", "Wave", "Tidal", "Solar", "Solar PV", "Solar Thermal", "Hydro", "Geothermal", "Biomass imports", "Bio-conversion", "Biofuel imports", "Agricultural 'waste'", "Marine algae", "UK land based bioenergy", "Other waste"], GREEN),
    "nuclear": (["Nuclear"], GOLD),
    "electricity": (["Electricity grid", "Thermal generation", "CHP", "District heating", "H2", "H2 conversion", "Pumped heat"], VIOLET),
    "end use": (["Industry", "Road transport", "Domestic aviation", "International aviation", "International shipping", "National navigation", "Rail", "Heating and cooling - homes", "Heating and cooling - commercial", "Lighting & appliances - homes", "Lighting & appliances - commercial", "Over generation / exports", "Agriculture"], TEAL),
    "losses": (["Losses"], PINK),
}


def generate():
    mock = fetch_json(URL)["data"][0]
    labels = mock["node"]["label"]
    colors = []
    for lab in labels:
        c = ORANGE
        for names, col in KIND.values():
            if lab in names:
                c = col
        colors.append(c)
    link = mock["link"]
    link_colors = [hex_to_rgba(colors[s], 0.28) for s in link["source"]]

    fig = go.Figure(go.Sankey(
        arrangement="snap", valueformat=",.0f", valuesuffix=" TWh",
        node=dict(label=labels, color=colors, pad=14, thickness=14, line=dict(width=0),
                  hovertemplate="%{label}: %{value:,.0f} TWh<extra></extra>"),
        link=dict(source=link["source"], target=link["target"], value=link["value"], color=link_colors,
                  hovertemplate="%{source.label} → %{target.label}: %{value:,.0f} TWh<extra></extra>"),
        textfont=dict(size=11, color=INK),
    ))
    fig.update_layout(**base_layout(margin=dict(l=30, r=30, t=110, b=70)))
    key = "   ".join(f"<span style='color:{col}'>■</span> {k}" for k, (_, col) in KIND.items())
    fig.add_annotation(x=1, y=1, xref="paper", yref="paper", xanchor="right", yanchor="bottom", yshift=36, showarrow=False,
                       text=key, font=dict(size=12, color=MUTED))
    titles(fig, "UK energy flows, from primary supply to end use",
           "Terawatt-hours per year. Bands are proportional to energy flow; colour follows the source of each flow.",
           "Source: UK Department of Energy & Climate Change 2050 pathways analysis, via the plotly.js sankey_energy example (Mike Bostock's D3 Sankey demo).")
    save(CHART_NUM, fig, width=1280, height=900)

Made with Plotly