Skip to content

Instantly share code, notes, and snippets.

@cavedave
Last active September 8, 2025 08:10
Show Gist options
  • Select an option

  • Save cavedave/a11fa410a471b4fb50b656e76e3edbe0 to your computer and use it in GitHub Desktop.

Select an option

Save cavedave/a11fa410a471b4fb50b656e76e3edbe0 to your computer and use it in GitHub Desktop.
staircase.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyNv3w0Sagj1PRZ2d9ZxKnZa",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/cavedave/a11fa410a471b4fb50b656e76e3edbe0/staircase.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"lets make a staircase of denial gif and share the code\n",
"\n",
"the idea is that between every el nino hot year people go 'look it hasnt gotten warmer in X years global warming is disproven. Checkmate now, king me'\n",
"\n",
"And i want to make a way to easily show that warming continues inside the el nino cyscle and new record year is coming."
],
"metadata": {
"id": "QpovhOROUzWI"
}
},
{
"cell_type": "markdown",
"source": [
"## staircase"
],
"metadata": {
"id": "a1l5rz8vKgQW"
}
},
{
"cell_type": "markdown",
"source": [
"I heard about the escalator of denial here and wanted to update it and make the code public https://skepticalscience.com/graphics.php?g=465\n",
"\n",
"and this tweet also shows it https://x.com/RARohde/status/1614991656583041024\n",
"\n"
],
"metadata": {
"id": "15WdK15XKh4T"
}
},
{
"cell_type": "markdown",
"source": [
"hadcrut data https://www.metoffice.gov.uk/hadobs/hadcrut5/data/HadCRUT.5.0.2.0/download.html"
],
"metadata": {
"id": "QG8tIYGLKnfG"
}
},
{
"cell_type": "code",
"metadata": {
"id": "d69c314c"
},
"source": [
"import pandas as pd\n",
"\n",
"url = \"https://www.metoffice.gov.uk/hadobs/hadcrut5/data/HadCRUT.5.0.2.0/analysis/diagnostics/HadCRUT.5.0.2.0.analysis.summary_series.global.annual.csv\"\n",
"df = pd.read_csv(url)\n",
"display(df.head())"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import imageio\n",
"from scipy.signal import argrelextrema\n",
"\n",
"\n",
"# rename columns\n",
"df = df.rename(columns={'Time':'Year', 'Anomaly (deg C)':'Anomaly'})\n",
"df.set_index('Year', inplace=True)\n",
"ts = df['Anomaly']"
],
"metadata": {
"id": "017GpJsNLoBY"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Make the years"
],
"metadata": {
"id": "srGGdU8cwj1Y"
}
},
{
"cell_type": "code",
"metadata": {
"id": "d69c314c"
},
"source": [
"# 1) find every record‐breaking year ≥ 1980",
"post80 = ts.where(ts.index >= 1980)",
"current_max = -np.inf",
"record_years = []",
"record_values = []",
"for yr, val in post80.items():",
"if np.isnan(val):",
"continue",
"if val >= current_max:",
"current_max = val",
"record_years.append(yr)",
"record_values.append(val)",
"# 2) build ALL steps",
"all_steps = [(s, e, y) for s, e, y in zip(record_years, record_years[1:], record_values)]",
"# 3) filter to only multi‑year “flat” periods (>=3 yr)",
"steps = [(s, e, y) for (s, e, y) in all_steps if (e - s) >= 3]",
"# 4) pre‑compute limits & frame list",
"xmin, xmax = ts.index.min(), ts.index.max()",
"ymin, ymax = ts.min() - 0.1, ts.max() + 0.1",
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import imageio\n",
"\n",
"# …after loading df and computing ts, steps, xmin,xmax,ymin,ymax…\n",
"\n",
"frame_files = []\n",
"\n",
"for year in ts.index:\n",
" if year < 1980:\n",
" continue\n",
" fig, ax = plt.subplots(figsize=(8,5))\n",
" ax.set_position([0.12, 0.15, 0.80, 0.75])\n",
"\n",
" # 1) Zero line\n",
" ax.axhline(0, color='black', linestyle='--', linewidth=1, alpha=0.1)\n",
"\n",
" # 2) full series\n",
" ax.plot(ts.index, ts.values, color='lightgray', lw=1)\n",
"\n",
" # 3) staircase steps\n",
" for s, e, y in steps:\n",
" if e <= year:\n",
" ax.hlines(y, s, e, lw=3, color='C0')\n",
" elif s <= year < e:\n",
" ax.hlines(y, s, year, lw=4, color='tomato')\n",
" ax.text(\n",
" year, y + 0.02,\n",
" f\"No warming in {year - s} yr\",\n",
" ha='right', va='bottom',\n",
" color='tomato', fontsize=11, fontweight='bold'\n",
" )\n",
"\n",
" # 4) lock axes\n",
" ax.set_xlim(xmin, xmax)\n",
" ax.set_ylim(ymin, ymax)\n",
"\n",
" # 5) titles & labels\n",
" ax.set_title(\n",
" \"Staircase of Denial — “No warming in years”\\n\",\n",
" loc='center', fontsize=16, fontweight='bold',\n",
" y=0.94, pad=5\n",
" )\n",
" ax.set_xlabel('Year', fontsize=12)\n",
" ax.set_ylabel('Temperature anomaly (°C)', fontsize=12)\n",
"\n",
" # 6) source & notes\n",
" ax.text(0.99, -0.1,\n",
" \"Source: HadCRUT5 by @iamreddave\",\n",
" ha='right', va='top',\n",
" transform=ax.transAxes,\n",
" fontsize=9, color='gray')\n",
" ax.text(0.20, -0.1,\n",
" \"Anomalies relative to 1961-1990\",\n",
" ha='right', va='top',\n",
" transform=ax.transAxes,\n",
" fontsize=9, color='gray')\n",
"\n",
" # 7) save\n",
" fname = f'frame_{year}.png'\n",
" fig.savefig(fname, dpi=120)\n",
" plt.close(fig)\n",
" frame_files.append(fname)"
],
"metadata": {
"id": "l76R5v5GujNI"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"output_path = '/content/staircase_hadcrut5.gif'\n",
"with imageio.get_writer(\n",
" '/content/staircase_hadcrut5.gif',\n",
" mode='I',\n",
" fps=7\n",
") as writer:\n",
" for fn in frame_files:\n",
" writer.append_data(imageio.imread(fn))"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Jaq21m9jungW",
"outputId": "45d77e7d-be05-4be7-d5db-58f0f846418b"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stderr",
"text": [
"/tmp/ipython-input-53-3463716466.py:8: DeprecationWarning: Starting with ImageIO v3 the behavior of this function will switch to that of iio.v3.imread. To keep the current behavior (and make this warning disappear) use `import imageio.v2 as imageio` or call `imageio.v2.imread` directly.\n",
" writer.append_data(imageio.imread(fn))\n"
]
}
]
}
]
}
@cavedave
Copy link
Author

staircase_hadcrut5

@cavedave
Copy link
Author

staircase_static (3)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment