Temperature anomaly¶
Shows the global temperature anomaly from 1995 to 2024 using the HadCRUT5 dataset. The video pauses at notable dates.
Highlights:
- Uses
wrap_matplotlib
to automatically handle saving and closing the figure. - Uses a custom
renderer
function to create each frame of the animation. - Uses
Paused
to pause the animation at notable dates.
import pandas as pd
import matplotlib.pyplot as plt
from streamjoy import stream, wrap_matplotlib, Paused
URL = "https://climexp.knmi.nl/data/ihadcrut5_global.dat"
NOTABLE_DATES = {
"1997-12": "Kyoto Protocol adopted",
"2005-01": "Exceeded 380 ppm",
"2010-01": "Exceeded 390 ppm",
"2013-05": "Exceeded 400 ppm",
"2015-12": "Paris Agreement signed",
"2016-01": "CO2 permanently over 400 ppm",
}
@wrap_matplotlib()
def renderer(df):
plt.style.use("dark_background") # Setting the style for dark mode
fig, ax = plt.subplots()
fig.patch.set_facecolor("#1b1e23")
ax.set_facecolor("#1b1e23")
ax.set_frame_on(False)
ax.axis("off")
# Set title
year = df["year"].iloc[-1]
ax.set_title(
f"Global Temperature Anomaly {year} [HadCRUT5]",
fontsize=15,
loc="left",
fontname="Courier New",
color="lightgrey",
)
# draw line
df.groupby("year")["anom"].plot(
y="anom", color="lightgrey", legend=False, ax=ax, lw=0.5
)
# add source text at bottom right
ax.text(
0.01,
0.05,
f"Source: {URL}",
va="bottom",
ha="left",
transform=ax.transAxes,
fontsize=8,
color="lightgrey",
fontname="Courier New",
)
# draw end point
jday = df.index.values[-1]
anom = df["anom"].values[-1]
ax.scatter(jday, anom, color="red", zorder=999)
anom_label = f"+{anom:.1f} K" if anom > 0 else f"{anom:.1f} K"
ax.annotate(
anom_label,
(jday, anom),
textcoords="offset points",
xytext=(-10, 5),
fontsize=12,
ha="right",
va="bottom",
color="lightgrey",
)
# draw yearly labels
for year, df_year in df.reset_index().groupby("year").last().iloc[-5:].iterrows():
if df_year["month"] != 12:
continue
ax.annotate(
year,
(df_year["jday"], df_year["anom"]),
fontsize=12,
ha="left",
va="center",
color="lightgrey",
fontname="Courier New",
)
plt.subplots_adjust(bottom=0, top=0.9, left=0.05)
month = df["date"].iloc[-1].strftime("%b")
ax.annotate(
month,
(jday, anom),
textcoords="offset points",
xytext=(-10, 3),
fontsize=12,
ha="right",
va="top",
color="lightgrey",
fontname="Courier New",
)
date_string = df["date"].iloc[-1].strftime("%Y-%m")
if date_string in NOTABLE_DATES:
ax.annotate(
f"{NOTABLE_DATES[date_string]}",
xy=(0, 1),
xycoords="axes fraction",
xytext=(0, -5),
textcoords="offset points",
fontsize=12,
ha="left",
va="top",
color="lightgrey",
fontname="Courier New",
)
return Paused(fig, 3)
return fig
df = (
pd.read_csv(
URL,
comment="#",
header=None,
sep="\s+",
na_values=[-999.9],
)
.rename(columns={0: "year"})
.melt(id_vars="year", var_name="month", value_name="anom")
)
df.index = pd.to_datetime(
df["year"].astype(str) + df["month"].astype(str), format="%Y%m"
)
df = df.sort_index()["1995":"2024"]
df["jday"] = df.index.dayofyear
df = df.rename_axis("date").reset_index().set_index("jday")
df_list = [df[:i] for i in range(1, len(df) + 1)]
stream(df_list, renderer=renderer, threads_per_worker=1).write(
"temperature_anomaly.mp4"
)