- Python 100%
| figures | ||
| scripts | ||
| tables | ||
| .DS_Store | ||
| README.md | ||
ChatGPT Citation Shift — Graphics
Every graphic for the ChatGPT citation-shift piece, plus the code and data that produce them. Ten figures for the article body and eight appendix tables, all as true-vector SVG.
Built to the conventions of the matplotlib-graphs skill at
https://git.dread.technology/aman/research-grade-figures/: vector masters only, bundled
DejaVu Sans, Okabe-Ito palette, finding-led titles, zero-based scales, no raster content.
Layout
citation-shift-graphics/
figures/
figures.py generator for the ten article figures
data/*.csv one file per figure, transcribed from the draft
output/*.svg
tables/
tables.py generator for the eight appendix tables
data/*.csv one file per table
output/*.svg
scripts/
verify_artifacts.py rejects any file that is secretly a raster
rasterize_pdf.py PDF → PNG, for eyeballing at final size
Reproducing
uv is the only prerequisite (https://docs.astral.sh/uv/getting-started/installation/).
Dependencies are declared inline in each script's PEP 723 header, so there is no virtualenv
to create and nothing to pip install.
cd figures && uv run figures.py --formats svg
cd tables && uv run tables.py --formats svg
Both accept --output-dir and --formats svg,pdf. figures.py takes --figures 00,03
and tables.py takes repeatable --table <slug> to rebuild a subset.
Verify, and inspect at final size:
uv run scripts/verify_artifacts.py figures/output/*.svg tables/output/*.svg
uv run scripts/rasterize_pdf.py figures/output/figure-00-tshirt-abstract.pdf --dpi 150
rasterize_pdf.py needs a PDF, so rebuild with --formats svg,pdf first if you want to
look at one.
The figures
| File | What it shows |
|---|---|
figure-00-tshirt-abstract |
The abstract: what stopped being cited, and what replaced it |
figure-01-reddit-collapse |
Reddit 2.93% → 0.12% |
figure-02-authority-shift-all-categories |
All twelve categories, before and after |
figure-03-august-8-to-august-14-timeline |
The two dated stages, on a calendar axis |
figure-04-authority-domains-gained |
The thirteen domains that multiplied |
figure-05-cochrane-arxiv-pubmed-swap |
The academic swap |
figure-06-media-citations-by-ai-engine |
News and trade press across five engines |
figure-07-reddit-citations-by-ai-engine |
Reddit share across six engines |
figure-08-robustness-replication |
Per-prompt and per-project replication |
figure-09-stat-callouts |
Professional associations, corporate/SMB, Google long-run |
Tables table-01 through table-08 mirror the appendix in the same order.
Reading the numbers
The draft moves between three baselines, which produce different percentages for the same finding. Every figure therefore states its comparison basis and exact date windows in the subtitle, and no figure is left ambiguous.
- Mix-controlled — identical prompts present in both windows. Before = Jul 18 – Aug 7, after = Aug 14 – 23, 2026. This is the headline basis.
- Raw — every citation in the window, including prompts that ran in only one of them.
- Per 10k — citations per 10,000 ChatGPT citations, which removes the change in total citation volume between windows.
Two inconsistencies in the source drafts were resolved before the graphics were produced:
- Reddit −94% vs −96%. Both are correct on different bases: mix-controlled is 2.93% → 0.12% (−96%), raw over the same windows is 2.95% → 0.17% (−94%). The figures lead with −96% and state the raw pair in the footnote. Note that the draft's Methodology section reads "2.95% → 0.12%", which pairs a raw before with a mix-controlled after; it should read 2.95% → 0.17%.
- Cochrane 0 → 46 vs 0 → 70.
Rewrite.mdcarries both pairs two paragraphs apart. V3 and the appendix carry only the second, so these graphics use 0 → 70 and 35 → 11 (−67%). The "0 to 46 / fell 77%" sentence should be deleted from the rewrite.
Data provenance
Values are transcribed by hand from the appendix and body tables of the draft, not recomputed. Where the published multipliers and percentage-point changes differ slightly from what the rounded inputs would give — osha.gov at 37×, standards at +1.69 pp — the published figure is kept and the discrepancy is disclosed in that figure's footnote.
Source: OpenLens citation database; 7,416,403 citations across seven AI platforms, Feb 26 – Aug 23, 2026. ChatGPT subset used here: 957,946 citations from 87,681 cited responses, Jun 1 – Aug 23, 2026.
Layout notes for anyone editing the code
Two rules keep text from colliding, and both are worth preserving:
frame()measures the title, subtitle and footnote blocks and returns the top and bottom margins the axes must respect. Never hardcode a bottom margin — a footnote that wraps to a second line will grow straight into the x-axis label.- Before/after values live in fixed columns to the right of the plot (
side_columns), not beside their marks. Dumbbells whose dots nearly touch — Google AI at 1.72 → 1.71 — would otherwise print their labels on top of each other.
Free-floating labels (slope-chart ends, line-chart ends) are pushed apart by
spread_labels. frame() still supports a blue "FIGURE NN / KICKER" line; every call site
currently passes an empty kicker, so restoring them is a one-argument change.