Created
April 6, 2026 02:03
-
-
Save ajdavis/73d7d5fb3fd39dd7c75e0ef59241c2d4 to your computer and use it in GitHub Desktop.
Living vs. cumulative dead through history, 10,000 BCE to 2100 CE
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
| """Living vs. cumulative dead through history, 10,000 BCE to 2100 CE. | |
| Cumulative births anchored to PRB (Haub & Kaneda 2022) era totals (~117B by 2022). | |
| Living interpolated from standard population benchmarks; post-2022 follows UN WPP 2024 | |
| medium variant. Dead = ever-born - living. | |
| """ | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| from matplotlib.ticker import FuncFormatter | |
| # PRB era cumulative births, scaled to hit 117B at 2022. | |
| prb_cum = [ | |
| (-10000, 0.9), | |
| (-8000, 1.2), | |
| (1, 51.1), | |
| (1200, 79.9), | |
| (1650, 93.8), | |
| (1750, 97.2), | |
| (1850, 101.6), | |
| (1900, 104.7), | |
| (1950, 108.4), | |
| (2000, 113.9), | |
| (2022, 117.0), | |
| (2050, 117.0 + 0.130 * 28), | |
| (2100, 117.0 + 0.130 * 28 + 0.115 * 50), | |
| ] | |
| # Living population benchmarks (millions) | |
| pop_knots = [ | |
| (-10000, 4), (-8000, 5), (-1000, 50), (1, 300), (1000, 310), | |
| (1500, 500), (1650, 500), (1750, 795), (1800, 980), (1850, 1260), | |
| (1900, 1650), (1950, 2520), (1970, 3700), (2000, 6070), (2022, 7960), | |
| (2050, 9700), (2086, 10300), (2100, 10200), | |
| ] | |
| years = np.arange(-10000, 2101) | |
| cum_years = np.array([c[0] for c in prb_cum]) | |
| cum_vals = np.array([c[1] for c in prb_cum]) * 1e9 | |
| cum_births = np.interp(years, cum_years, cum_vals) | |
| knot_years = np.array([k[0] for k in pop_knots]) | |
| knot_pops = np.array([k[1] for k in pop_knots]) * 1e6 | |
| pop = np.exp(np.interp(years, knot_years, np.log(knot_pops))) | |
| dead = cum_births - pop | |
| ratio = dead / pop | |
| # x = years before 2101, log-scaled then inverted so modern era gets spread | |
| # out on the right and ancient era gets compressed on the left. | |
| x = 2101 - years | |
| def year_fmt(val, _): | |
| y = 2101 - val | |
| if y <= 0: | |
| return f"{-y+1} BCE" | |
| if y < 1000: | |
| return f"{int(y)} CE" | |
| return f"{int(y)}" | |
| fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 7), sharex=True) | |
| ax1.plot(x, dead / 1e9, color="#444", lw=2, label="Cumulative dead") | |
| ax1.plot(x, pop / 1e9, color="#c44", lw=2, label="Living") | |
| ax1.set_xscale("log") | |
| ax1.set_yscale("log") | |
| ax1.invert_xaxis() | |
| ax1.set_ylabel("People (billions, log scale)") | |
| ax1.set_ylim(0.001, 200) | |
| ax1.grid(which="both", alpha=0.3) | |
| ax1.legend(loc="upper left", framealpha=0.9) | |
| ax1.set_title("The living, the dead, and their ratio (10,000 BCE – 2100 CE)") | |
| ax2.plot(x, ratio, color="#2a7", lw=2) | |
| ax2.set_xscale("log") | |
| ax2.set_yscale("log") | |
| ax2.set_ylabel("Dead per living (log scale)") | |
| ax2.set_xlabel("Year") | |
| ax2.set_ylim(1, 3000) | |
| ax2.grid(which="both", alpha=0.3) | |
| # Put ticks at recognizable years | |
| tick_years = [-10000, -3000, -1000, 1, 1000, 1500, 1800, 1900, 1950, 2000, 2050, 2100] | |
| tick_x = [2101 - y for y in tick_years] | |
| tick_labels = ["10000 BCE", "3000 BCE", "1000 BCE", "1 CE", | |
| "1000", "1500", "1800", "1900", "1950", "2000", "2050", "2100"] | |
| ax2.set_xticks(tick_x) | |
| ax2.set_xticklabels(tick_labels, rotation=30, ha="right") | |
| ax2.minorticks_off() | |
| plt.tight_layout() | |
| out = "/Users/emptysquare/co/emptysquare-hugo/emptysquare/content/the-dead-majority/dead-living.png" | |
| plt.savefig(out, dpi=150) | |
| print("saved", out) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment