Created
March 1, 2026 01:15
-
-
Save cavedave/319e760ece78b98c5ab1c3830d72cabc to your computer and use it in GitHub Desktop.
Australia coal for electricity Based on https://gist.github.com/cavedave/a5bc2d064a6273dffca6da7335a73ab1 based on guardian article https://www.theguardian.com/environment/ng-interactive/2019/may/25/the-power-switch-tracking-britains-record-coal-free-run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Extract coal electricity generation (TWh) by year from Ember data. | |
| China coal can use monthly or yearly data; World uses yearly. | |
| """ | |
| import argparse | |
| import pandas as pd | |
| from pathlib import Path | |
| DATA_FILE = Path(__file__).resolve().parent / "yearly_full_release_long_format.csv" | |
| MONTHLY_DATA_URL = ( | |
| "https://storage.googleapis.com/emb-prod-bkt-publicdata/public-downloads/" | |
| "monthly_full_release_long_format.csv" | |
| ) | |
| def load_data(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """Load Ember yearly long-format CSV.""" | |
| path = Path(csv_path) if csv_path else DATA_FILE | |
| return pd.read_csv(path, low_memory=False) | |
| def load_monthly_data(url_or_path: str | Path | None = None) -> pd.DataFrame: | |
| """Load Ember monthly long-format CSV from URL or local path.""" | |
| if url_or_path is None: | |
| url_or_path = MONTHLY_DATA_URL | |
| return pd.read_csv(url_or_path, low_memory=False) | |
| def get_china_coal_by_year( | |
| csv_path: str | Path | None = None, | |
| source: str = "monthly", | |
| ) -> pd.DataFrame: | |
| """ | |
| Extract China electricity generation from coal (TWh) for each year in the data. | |
| Returns a DataFrame with columns: Year, Coal_TWh. | |
| source: "monthly" (default) – load monthly CSV, sum by year (can include 2025); | |
| "yearly" – load yearly CSV. | |
| csv_path: For monthly, URL or path to monthly CSV (None = default URL). | |
| For yearly, path to yearly CSV (None = default DATA_FILE). | |
| """ | |
| if source == "yearly": | |
| df = load_data(csv_path) | |
| china_coal = df[ | |
| (df["Area"] == "China") | |
| & (df["Variable"] == "Coal") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| result = china_coal[["Year", "Value"]].sort_values("Year").reset_index(drop=True) | |
| result = result.rename(columns={"Value": "Coal_TWh"}) | |
| return result | |
| # monthly: load monthly data, derive Year from Date, sum by year | |
| df = load_monthly_data(csv_path) | |
| china_coal = df[ | |
| (df["Area"] == "China") | |
| & (df["Variable"] == "Coal") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| china_coal["Date"] = pd.to_datetime(china_coal["Date"]) | |
| china_coal["Year"] = china_coal["Date"].dt.year | |
| result = ( | |
| china_coal.groupby("Year")["Value"] | |
| .sum() | |
| .reset_index() | |
| .sort_values("Year") | |
| .reset_index(drop=True) | |
| ) | |
| result = result.rename(columns={"Value": "Coal_TWh"}) | |
| return result | |
| def get_china_coal_percentage_by_year( | |
| csv_path: str | Path | None = None, | |
| source: str = "monthly", | |
| ) -> pd.DataFrame: | |
| """ | |
| China coal and total generation by year, with coal as % of total. | |
| Returns DataFrame with columns: Year, Coal_TWh, Total_TWh, Percentage_Coal. | |
| Uses monthly data (source ignored for now; yearly file has no Total Generation). | |
| """ | |
| df = load_monthly_data(csv_path) | |
| df["Date"] = pd.to_datetime(df["Date"]) | |
| df["Year"] = df["Date"].dt.year | |
| china = df[ | |
| (df["Area"] == "China") | |
| & (df["Unit"] == "TWh") | |
| & (df["Variable"].isin(["Coal", "Total Generation"])) | |
| ].copy() | |
| by_year_var = china.groupby(["Year", "Variable"])["Value"].sum().reset_index() | |
| coal = by_year_var[by_year_var["Variable"] == "Coal"][["Year", "Value"]].rename(columns={"Value": "Coal_TWh"}) | |
| total = by_year_var[by_year_var["Variable"] == "Total Generation"][["Year", "Value"]].rename(columns={"Value": "Total_TWh"}) | |
| merged = pd.merge(coal, total, on="Year", how="inner") | |
| merged["Percentage_Coal"] = (merged["Coal_TWh"] / merged["Total_TWh"]).replace(0, pd.NA) * 100 | |
| return merged.sort_values("Year").reset_index(drop=True) | |
| def plot_china_coal_percentage( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| source: str = "monthly", | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot China's yearly coal percentage of total electricity generation. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| merged_df = get_china_coal_percentage_by_year(csv_path=csv_path, source=source) | |
| merged_df = merged_df[merged_df["Year"] >= start_year].copy() | |
| if merged_df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot(merged_df["Year"], merged_df["Percentage_Coal"], marker="o", linestyle="-", color="black") | |
| plt.title("China's Yearly Coal Percentage in Total Electricity Generation", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Percentage of Total Generation from Coal (%)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(merged_df["Year"].astype(int).unique(), rotation=45) | |
| plt.ylim(0, merged_df["Percentage_Coal"].max() * 1.05) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_china_solar_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| China solar electricity generation (TWh) by year from monthly data. | |
| Returns DataFrame with columns: Year, Solar_TWh. | |
| """ | |
| df = load_monthly_data(csv_path) | |
| solar = df[ | |
| (df["Area"] == "China") | |
| & (df["Variable"] == "Solar") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| solar["Date"] = pd.to_datetime(solar["Date"]) | |
| solar["Year"] = solar["Date"].dt.year | |
| result = ( | |
| solar.groupby("Year")["Value"] | |
| .sum() | |
| .reset_index() | |
| .sort_values("Year") | |
| .reset_index(drop=True) | |
| ) | |
| result = result.rename(columns={"Value": "Solar_TWh"}) | |
| return result | |
| def plot_china_solar( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot China solar electricity generation by year (TWh), same style as coal plot. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_china_solar_by_year(csv_path) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot( | |
| df["Year"], | |
| df["Solar_TWh"], | |
| marker="o", | |
| linestyle="-", | |
| color="#CC5500", | |
| ) | |
| plt.title("China Solar Electricity Generation Per Year", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Annual Generation (TWh)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.ylim(0, df["Solar_TWh"].max() * 1.05) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_india_solar_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| India solar electricity generation (TWh) by year from monthly data. | |
| Returns DataFrame with columns: Year, Solar_TWh. | |
| """ | |
| df = load_monthly_data(csv_path) | |
| solar = df[ | |
| (df["Area"] == "India") | |
| & (df["Variable"] == "Solar") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| solar["Date"] = pd.to_datetime(solar["Date"]) | |
| solar["Year"] = solar["Date"].dt.year | |
| result = ( | |
| solar.groupby("Year")["Value"] | |
| .sum() | |
| .reset_index() | |
| .sort_values("Year") | |
| .reset_index(drop=True) | |
| ) | |
| result = result.rename(columns={"Value": "Solar_TWh"}) | |
| return result | |
| def plot_india_solar( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot India solar electricity generation by year (TWh), same style as China solar. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_india_solar_by_year(csv_path) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot( | |
| df["Year"], | |
| df["Solar_TWh"], | |
| marker="o", | |
| linestyle="-", | |
| color="#CC5500", | |
| ) | |
| plt.title("India Solar Electricity Generation Per Year", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Annual Generation (TWh)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.ylim(0, df["Solar_TWh"].max() * 1.05) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_pakistan_solar_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| Pakistan solar electricity generation (TWh) by year from monthly data. | |
| Returns DataFrame with columns: Year, Solar_TWh. | |
| """ | |
| df = load_monthly_data(csv_path) | |
| solar = df[ | |
| (df["Area"] == "Pakistan") | |
| & (df["Variable"] == "Solar") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| solar["Date"] = pd.to_datetime(solar["Date"]) | |
| solar["Year"] = solar["Date"].dt.year | |
| result = ( | |
| solar.groupby("Year")["Value"] | |
| .sum() | |
| .reset_index() | |
| .sort_values("Year") | |
| .reset_index(drop=True) | |
| ) | |
| result = result.rename(columns={"Value": "Solar_TWh"}) | |
| return result | |
| def plot_pakistan_solar( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot Pakistan solar electricity generation by year (TWh), same style as China/India solar. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_pakistan_solar_by_year(csv_path) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot( | |
| df["Year"], | |
| df["Solar_TWh"], | |
| marker="o", | |
| linestyle="-", | |
| color="#CC5500", | |
| ) | |
| plt.title("Pakistan Solar Electricity Generation Per Year", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Annual Generation (TWh)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.ylim(0, df["Solar_TWh"].max() * 1.05) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def plot_china_coal( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| source: str = "monthly", | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot China coal electricity generation by year (TWh). | |
| source: "monthly" (default) or "yearly". csv_path: monthly URL/path or yearly path. | |
| If save_path is set, saves the figure to that file instead of showing interactively. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_china_coal_by_year(csv_path=csv_path, source=source) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot( | |
| df["Year"], | |
| df["Coal_TWh"], | |
| marker="o", | |
| linestyle="-", | |
| color="black", | |
| ) | |
| plt.title("China Coal Electricity Generation Per Year", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Annual Generation (TWh)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_india_coal_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| India electricity generation from coal (TWh) by year from monthly data. | |
| Returns a DataFrame with columns: Year, Coal_TWh. | |
| """ | |
| df = load_monthly_data(csv_path) | |
| india_coal = df[ | |
| (df["Area"] == "India") | |
| & (df["Variable"] == "Coal") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| india_coal["Date"] = pd.to_datetime(india_coal["Date"]) | |
| india_coal["Year"] = india_coal["Date"].dt.year | |
| result = ( | |
| india_coal.groupby("Year")["Value"] | |
| .sum() | |
| .reset_index() | |
| .sort_values("Year") | |
| .reset_index(drop=True) | |
| ) | |
| result = result.rename(columns={"Value": "Coal_TWh"}) | |
| return result | |
| def plot_india_coal( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot India coal electricity generation by year (TWh), same style as China coal plot. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_india_coal_by_year(csv_path) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot( | |
| df["Year"], | |
| df["Coal_TWh"], | |
| marker="o", | |
| linestyle="-", | |
| color="black", | |
| ) | |
| plt.title("India Coal Electricity Generation Per Year", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Annual Generation (TWh)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_australia_coal_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| Australia electricity generation from coal (TWh) by year from monthly data. | |
| Only includes years with 12 months of data (excludes partial years like 2026). | |
| Returns a DataFrame with columns: Year, Coal_TWh. | |
| """ | |
| df = load_monthly_data(csv_path) | |
| aus_coal = df[ | |
| (df["Area"] == "Australia") | |
| & (df["Variable"] == "Coal") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| aus_coal["Date"] = pd.to_datetime(aus_coal["Date"]) | |
| aus_coal["Year"] = aus_coal["Date"].dt.year | |
| months_per_year = aus_coal.groupby("Year")["Date"].apply(lambda x: x.dt.month.nunique()) | |
| full_years = months_per_year[months_per_year == 12].index | |
| aus_coal_full = aus_coal[aus_coal["Year"].isin(full_years)] | |
| result = ( | |
| aus_coal_full.groupby("Year")["Value"] | |
| .sum() | |
| .reset_index() | |
| .sort_values("Year") | |
| .reset_index(drop=True) | |
| ) | |
| result = result.rename(columns={"Value": "Coal_TWh"}) | |
| return result | |
| def plot_australia_coal( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot Australia coal electricity generation by year (TWh), same style with blue line. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_australia_coal_by_year(csv_path) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot( | |
| df["Year"], | |
| df["Coal_TWh"], | |
| marker="o", | |
| linestyle="-", | |
| color="#2563EB", | |
| ) | |
| plt.title("Australia Coal Electricity Generation Per Year", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Annual Generation (TWh)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_china_fossil_vs_renewables_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| China electricity by year: Coal+Gas (fossil) and Solar+Wind+Hydro (renewables), TWh. | |
| Returns DataFrame with columns: Year, Fossil_TWh, Renewables_TWh. | |
| """ | |
| df = load_monthly_data(csv_path) | |
| df["Date"] = pd.to_datetime(df["Date"]) | |
| df["Year"] = df["Date"].dt.year | |
| china = df[ | |
| (df["Area"] == "China") | |
| & (df["Unit"] == "TWh") | |
| & (df["Variable"].isin(["Coal", "Gas", "Solar", "Wind", "Hydro"])) | |
| ].copy() | |
| by_year_var = china.groupby(["Year", "Variable"])["Value"].sum().reset_index() | |
| fossil_vars = ["Coal", "Gas"] | |
| renew_vars = ["Solar", "Wind", "Hydro"] | |
| fossil = by_year_var[by_year_var["Variable"].isin(fossil_vars)].groupby("Year")["Value"].sum().reset_index().rename(columns={"Value": "Fossil_TWh"}) | |
| renew = by_year_var[by_year_var["Variable"].isin(renew_vars)].groupby("Year")["Value"].sum().reset_index().rename(columns={"Value": "Renewables_TWh"}) | |
| merged = pd.merge(fossil, renew, on="Year", how="outer").fillna(0) | |
| return merged.sort_values("Year").reset_index(drop=True) | |
| def plot_china_fossil_vs_renewables( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot China: Coal+Gas vs Solar+Wind+Hydro (TWh per year), same style as other graphs. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_china_fossil_vs_renewables_by_year(csv_path) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot(df["Year"], df["Fossil_TWh"], marker="o", linestyle="-", color="black", label="Coal + Gas") | |
| plt.plot(df["Year"], df["Renewables_TWh"], marker="o", linestyle="-", color="#CC5500", label="Solar + Wind + Hydro") | |
| plt.title("China Electricity: Fossil (Coal+Gas) vs Renewables (Solar+Wind+Hydro)", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Annual Generation (TWh)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.legend() | |
| ymax = max(df["Fossil_TWh"].max(), df["Renewables_TWh"].max()) * 1.05 | |
| plt.ylim(0, ymax) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_china_fossil_vs_renewables_pct_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| China electricity by year: Coal+Gas and Solar+Wind+Hydro as % of total generation. | |
| Returns DataFrame with columns: Year, Fossil_Pct, Renewables_Pct. | |
| """ | |
| df = load_monthly_data(csv_path) | |
| df["Date"] = pd.to_datetime(df["Date"]) | |
| df["Year"] = df["Date"].dt.year | |
| china = df[ | |
| (df["Area"] == "China") | |
| & (df["Unit"] == "TWh") | |
| & (df["Variable"].isin(["Coal", "Gas", "Solar", "Wind", "Hydro", "Total Generation"])) | |
| ].copy() | |
| by_year_var = china.groupby(["Year", "Variable"])["Value"].sum().reset_index() | |
| fossil_vars = ["Coal", "Gas"] | |
| renew_vars = ["Solar", "Wind", "Hydro"] | |
| fossil = by_year_var[by_year_var["Variable"].isin(fossil_vars)].groupby("Year")["Value"].sum().reset_index().rename(columns={"Value": "Fossil_TWh"}) | |
| renew = by_year_var[by_year_var["Variable"].isin(renew_vars)].groupby("Year")["Value"].sum().reset_index().rename(columns={"Value": "Renewables_TWh"}) | |
| total = by_year_var[by_year_var["Variable"] == "Total Generation"][["Year", "Value"]].rename(columns={"Value": "Total_TWh"}) | |
| merged = pd.merge(fossil, renew, on="Year", how="outer").fillna(0) | |
| merged = pd.merge(merged, total, on="Year", how="inner") | |
| merged["Fossil_Pct"] = (merged["Fossil_TWh"] / merged["Total_TWh"]) * 100 | |
| merged["Renewables_Pct"] = (merged["Renewables_TWh"] / merged["Total_TWh"]) * 100 | |
| return merged[["Year", "Fossil_Pct", "Renewables_Pct"]].sort_values("Year").reset_index(drop=True) | |
| def plot_china_fossil_vs_renewables_pct( | |
| start_year: int = 2015, | |
| csv_path: str | Path | None = None, | |
| save_path: str | Path | None = None, | |
| ) -> None: | |
| """ | |
| Plot China: Coal+Gas and Solar+Wind+Hydro as % of total electricity, same style. | |
| """ | |
| import matplotlib | |
| if save_path is not None: | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| df = get_china_fossil_vs_renewables_pct_by_year(csv_path) | |
| df = df[df["Year"] >= start_year].copy() | |
| if df.empty: | |
| return | |
| plt.figure(figsize=(12, 6)) | |
| plt.plot(df["Year"], df["Fossil_Pct"], marker="o", linestyle="-", color="black", label="Coal + Gas") | |
| plt.plot(df["Year"], df["Renewables_Pct"], marker="o", linestyle="-", color="#CC5500", label="Solar + Wind + Hydro") | |
| plt.title("China Electricity: Fossil vs Renewables (% of Total Generation)", fontsize=16) | |
| plt.xlabel("Year") | |
| plt.ylabel("Percentage of Total Generation (%)") | |
| plt.grid(True, linestyle="--", alpha=0.6) | |
| plt.xticks(df["Year"].astype(int).unique(), rotation=45) | |
| plt.legend() | |
| ymax = max(df["Fossil_Pct"].max(), df["Renewables_Pct"].max()) * 1.05 | |
| plt.ylim(0, ymax) | |
| plt.tight_layout(rect=[0, 0.06, 1, 1]) | |
| plt.gcf().text(0.99, 0.02, "data: ember-energy by @iamredave", fontsize=9, ha="right", va="bottom") | |
| if save_path is not None: | |
| plt.savefig(save_path, dpi=150) | |
| plt.close() | |
| print("Saved plot to", save_path) | |
| else: | |
| plt.show() | |
| def get_world_coal_by_year(csv_path: str | Path | None = None) -> pd.DataFrame: | |
| """ | |
| Extract World electricity generation from coal (TWh) for each year in the data. | |
| Returns a DataFrame with columns: Year, Coal_TWh. | |
| If 2025 is not present in the data, prints a message to that effect. | |
| """ | |
| df = load_data(csv_path) | |
| world_coal = df[ | |
| (df["Area"] == "World") | |
| & (df["Variable"] == "Coal") | |
| & (df["Unit"] == "TWh") | |
| ].copy() | |
| result = world_coal[["Year", "Value"]].sort_values("Year").reset_index(drop=True) | |
| result = result.rename(columns={"Value": "Coal_TWh"}) | |
| years = result["Year"].astype(int).tolist() | |
| if 2025 not in years: | |
| print("2025 is not present in the World coal data. Latest year available:", max(years) if years else "N/A") | |
| return result | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="Extract coal electricity by year (China / World)") | |
| parser.add_argument( | |
| "--plot", | |
| action="store_true", | |
| help="Plot China coal generation by year (from 2015 by default)", | |
| ) | |
| parser.add_argument( | |
| "--start-year", | |
| type=int, | |
| default=2015, | |
| help="Start year for China coal plot (default: 2015)", | |
| ) | |
| parser.add_argument( | |
| "--save", | |
| metavar="FILE", | |
| help="Save China coal plot to FILE (e.g. china_coal.png) instead of showing", | |
| ) | |
| parser.add_argument( | |
| "--yearly", | |
| action="store_true", | |
| help="Use yearly file for China coal (default: use monthly data)", | |
| ) | |
| parser.add_argument( | |
| "--monthly-file", | |
| metavar="PATH", | |
| help="Path to local monthly CSV for China (default: fetch from Ember URL)", | |
| ) | |
| parser.add_argument( | |
| "--plot-pct", | |
| action="store_true", | |
| help="Plot China coal percentage of total generation", | |
| ) | |
| parser.add_argument( | |
| "--save-pct", | |
| metavar="FILE", | |
| help="Save China coal percentage plot to FILE (e.g. china_coal_pct.png)", | |
| ) | |
| parser.add_argument( | |
| "--plot-solar", | |
| action="store_true", | |
| help="Plot China solar generation by year", | |
| ) | |
| parser.add_argument( | |
| "--save-solar", | |
| metavar="FILE", | |
| help="Save China solar plot to FILE (e.g. china_solar.png)", | |
| ) | |
| parser.add_argument( | |
| "--plot-india-coal", | |
| action="store_true", | |
| help="Plot India coal generation by year", | |
| ) | |
| parser.add_argument( | |
| "--save-india-coal", | |
| metavar="FILE", | |
| help="Save India coal plot to FILE (e.g. india_coal.png)", | |
| ) | |
| parser.add_argument( | |
| "--plot-india-solar", | |
| action="store_true", | |
| help="Plot India solar generation by year", | |
| ) | |
| parser.add_argument( | |
| "--save-india-solar", | |
| metavar="FILE", | |
| help="Save India solar plot to FILE (e.g. india_solar.png)", | |
| ) | |
| parser.add_argument( | |
| "--plot-pakistan-solar", | |
| action="store_true", | |
| help="Plot Pakistan solar generation by year", | |
| ) | |
| parser.add_argument( | |
| "--save-pakistan-solar", | |
| metavar="FILE", | |
| help="Save Pakistan solar plot to FILE (e.g. pakistan_solar.png)", | |
| ) | |
| parser.add_argument( | |
| "--plot-australia-coal", | |
| action="store_true", | |
| help="Plot Australia coal generation by year", | |
| ) | |
| parser.add_argument( | |
| "--save-australia-coal", | |
| metavar="FILE", | |
| help="Save Australia coal plot to FILE (e.g. australia_coal.png)", | |
| ) | |
| parser.add_argument( | |
| "--save-australia-coal-heat", | |
| metavar="FILE", | |
| help="Save Australia coal heatmap to FILE (e.g. coal_aus_heat.png)", | |
| ) | |
| parser.add_argument( | |
| "--plot-fossil-renewables", | |
| action="store_true", | |
| help="Plot China Coal+Gas vs Solar+Wind+Hydro", | |
| ) | |
| parser.add_argument( | |
| "--save-fossil-renewables", | |
| metavar="FILE", | |
| help="Save China fossil vs renewables plot to FILE", | |
| ) | |
| parser.add_argument( | |
| "--plot-fossil-renewables-pct", | |
| action="store_true", | |
| help="Plot China fossil vs renewables as %% of total generation", | |
| ) | |
| parser.add_argument( | |
| "--save-fossil-renewables-pct", | |
| metavar="FILE", | |
| help="Save China fossil vs renewables %% plot to FILE", | |
| ) | |
| args = parser.parse_args() | |
| china_source = "yearly" if args.yearly else "monthly" | |
| china_path = None if args.yearly else args.monthly_file | |
| print("=" * 60) | |
| print("China – coal electricity generation by year (TWh)") | |
| if china_source == "monthly": | |
| print("(from monthly data, summed by year)") | |
| print("=" * 60) | |
| china = get_china_coal_by_year(csv_path=china_path, source=china_source) | |
| print(china.to_string(index=False)) | |
| print() | |
| print("=" * 60) | |
| print("World – coal electricity generation by year (TWh)") | |
| print("=" * 60) | |
| world = get_world_coal_by_year() | |
| print(world.to_string(index=False)) | |
| if 2025 not in world["Year"].astype(int).tolist(): | |
| print("\nNote: 2025 is not present in the World coal data.") | |
| print() | |
| print("Done.") | |
| if args.plot or args.save: | |
| save_path = Path(args.save) if args.save else None | |
| plot_china_coal( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| source=china_source, | |
| save_path=save_path, | |
| ) | |
| if args.plot_pct or args.save_pct: | |
| pct_save = Path(args.save_pct) if args.save_pct else None | |
| plot_china_coal_percentage( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| source=china_source, | |
| save_path=pct_save, | |
| ) | |
| if args.plot_solar or args.save_solar: | |
| solar_save = Path(args.save_solar) if args.save_solar else None | |
| plot_china_solar( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| save_path=solar_save, | |
| ) | |
| if args.plot_india_coal or args.save_india_coal: | |
| india_coal_save = Path(args.save_india_coal) if args.save_india_coal else None | |
| plot_india_coal( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| save_path=india_coal_save, | |
| ) | |
| if args.plot_india_solar or args.save_india_solar: | |
| india_solar_save = Path(args.save_india_solar) if args.save_india_solar else None | |
| plot_india_solar( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| save_path=india_solar_save, | |
| ) | |
| if args.plot_pakistan_solar or args.save_pakistan_solar: | |
| pakistan_solar_save = Path(args.save_pakistan_solar) if args.save_pakistan_solar else None | |
| plot_pakistan_solar( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| save_path=pakistan_solar_save, | |
| ) | |
| if args.plot_australia_coal or args.save_australia_coal: | |
| australia_coal_save = Path(args.save_australia_coal) if args.save_australia_coal else None | |
| plot_australia_coal( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| save_path=australia_coal_save, | |
| ) | |
| if args.save_australia_coal_heat: | |
| from coal_aus_heat import plot_australia_coal_heatmap | |
| plot_australia_coal_heatmap(csv_path=china_path, save_path=Path(args.save_australia_coal_heat)) | |
| if args.plot_fossil_renewables or args.save_fossil_renewables: | |
| fr_save = Path(args.save_fossil_renewables) if args.save_fossil_renewables else None | |
| plot_china_fossil_vs_renewables( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| save_path=fr_save, | |
| ) | |
| if args.plot_fossil_renewables_pct or args.save_fossil_renewables_pct: | |
| fr_pct_save = Path(args.save_fossil_renewables_pct) if args.save_fossil_renewables_pct else None | |
| plot_china_fossil_vs_renewables_pct( | |
| start_year=args.start_year, | |
| csv_path=china_path, | |
| save_path=fr_pct_save, | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
