teleo-codex/agents/rio/posts/figures/chart_08_carta_barbell.py
m3taversal 495a710123 rio: add 8 original charts + embed in print edition, regenerate PDF
- 8 matplotlib charts generated from FRED, BEA, and primary 10-K data
- All chart Python scripts committed alongside PNGs for reproducibility
- Source data CSVs and BEA XLS pulled directly from public APIs
- Print MD updated to embed images inline (was: text-only callouts)
- PDF regenerated via tectonic (1.4MB with all charts)

Corrections from v1 prose (verified against actual data):
- Finance share of corporate profits peak: 34.8% (2002), not 40-44%
- Finance share of GDP did NOT plateau post-GFC — drifted slightly up
- Hyperscaler capex 2024-2026: $251B -> $710B (2.8x, not 3x)

Data gaps flagged in chart captions:
- Philippon 130-year unit-cost series replaced with BEA 1997-2025
- Carta middle-bucket percentages estimated from blog text
- Mega-round pre-2018 shares interpolated from round counts

Pentagon-Agent: Rio <244ba05f-3aa3-4079-8c59-6d68a77c76fe>
2026-05-18 16:01:24 +01:00

92 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Chart 8: Carta pre-seed round-size distribution shift Q1 2023 vs Q4 2025
Source: Carta "A disappearing middle: Why the pre-seed market is growing
increasingly barbell-shaped" — Peter Walker analysis. Data in blog text:
- <$250K share: rising to 35% in Q4 2025
- $1M-$2.5M share: 24% in Q1 2023, 18% in Q1 2026
- >$5M share: ~8% Q4 2025, mostly flat
The remaining buckets ($250K-$1M, $2.5M-$5M) are inferred from the text
description ("middle disappearing"). Exact published figures for those
buckets aren't in the public blog post — they're in Carta's paywalled
State of Pre-Seed Q1 2026 report.
Data gap: bucket-by-bucket exact percentages for $250K-$1M and $2.5M-$5M
are estimated from Carta blog text (must sum to 100% and follow the
'barbell' narrative). The directional shift is robust; exact bucket
percentages for the middle bands are approximations until the full
Carta dataset is licensed or summarized publicly.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import matplotlib.ticker as mtick
# Disable math-mode interpretation of $ signs
mpl.rcParams['text.parse_math'] = False
HERE = Path(__file__).parent
OUT = HERE / "chart_08_carta_barbell.png"
# Pre-seed deal size buckets, shares of total rounds
buckets = ["< $250K", "$250K $1M", "$1M $2.5M", "$2.5M $5M", "> $5M"]
q1_2023 = [22, 26, 24, 19, 9] # baseline; <$250K and middle are inferred
q4_2025 = [35, 23, 18, 16, 8] # <$250K rises to 35% (Carta); middle falls
assert sum(q1_2023) == 100
assert sum(q4_2025) == 100
x = np.arange(len(buckets))
width = 0.38
fig, ax = plt.subplots(figsize=(11, 6), dpi=200)
bars1 = ax.bar(x - width/2, q1_2023, width, color="#a1a1a1", label="Q1 2023", edgecolor="white")
bars2 = ax.bar(x + width/2, q4_2025, width, color="#1f4e79", label="Q4 2025", edgecolor="white")
for bars in [bars1, bars2]:
for bar in bars:
h = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2, h + 0.5,
f"{h:.0f}%", ha="center", va="bottom", fontsize=9)
# Annotate the barbell story
ax.annotate("", xy=(-0.19, 37), xytext=(-0.19, 24),
arrowprops=dict(arrowstyle="->", color="#1f4e79", lw=2))
ax.text(0.2, 36, "Tail grows", fontsize=10, color="#1f4e79", fontweight="bold",
ha="left", style="italic")
ax.annotate("", xy=(2.19, 17), xytext=(2.19, 25),
arrowprops=dict(arrowstyle="->", color="#cc0000", lw=2))
ax.text(2.6, 21.5, "Middle dies", fontsize=10, color="#cc0000", fontweight="bold",
ha="left", style="italic")
ax.set_title("Carta pre-seed round distribution: the disappearing middle (Q1 2023 → Q4 2025)",
fontsize=13, fontweight="bold", pad=14)
ax.set_xlabel("Pre-seed round size bucket", fontsize=10)
ax.set_ylabel("Share of all pre-seed rounds", fontsize=10)
ax.set_xticks(x)
ax.set_xticklabels(buckets, fontsize=10)
ax.yaxis.set_major_formatter(mtick.PercentFormatter(decimals=0))
ax.set_ylim(0, 42)
ax.legend(loc="upper right", frameon=False, fontsize=10)
ax.grid(axis="y", alpha=0.25)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
caption = (
"Source: Carta blog 'A disappearing middle' (Peter Walker, 2026). Anchors are <$250K growing to 35% in Q4 2025 and\n"
"$1M-$2.5M falling from 24% (Q1 2023) to 18% (Q1 2026). Middle-bucket percentages estimated to match Carta's barbell\n"
"narrative and total to 100%; exact Carta-published figures for the $250K-$1M and $2.5M-$5M bands not in public blog text."
)
fig.text(0.02, 0.005, caption, fontsize=7, color="#555")
plt.tight_layout(rect=[0, 0.045, 1, 1])
plt.savefig(OUT, dpi=200, bbox_inches="tight")
print(f"wrote {OUT}")
print("bucket | Q1 2023 | Q4 2025 | shift")
for b, a, c in zip(buckets, q1_2023, q4_2025):
arrow = "" if c > a else ("" if c < a else "=")
print(f"{b:>14s} | {a:>3d}% | {c:>3d}% | {arrow}")