Created
September 7, 2025 04:44
-
-
Save Siddhant-K-code/d6e0a4f9f818f5af87a15b8c96b32a7f to your computer and use it in GitHub Desktop.
Beautiful MatplotLib's xkcd graph
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
| #!/usr/bin/env python3 | |
| # make_chalk_plots.py | |
| # Hand-drawn (xkcd) style charts; saves stacked + per-panel images. | |
| from pathlib import Path | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| import matplotlib.patheffects as pe | |
| # --------- Output ---------- | |
| OUTDIR = Path("images/xkcd") | |
| DPI = 300 | |
| # --------- Theme ---------- | |
| BG = "#0c0c0c" # chalkboard | |
| FG = "white" # chalk | |
| LINE = "#ff3b3b" # red chalk | |
| def apply_global_rc(): | |
| plt.rcParams.update({ | |
| "figure.facecolor": BG, | |
| "axes.facecolor": BG, | |
| "axes.edgecolor": FG, | |
| "axes.labelcolor": FG, | |
| "xtick.color": FG, | |
| "ytick.color": FG, | |
| "grid.color": FG, | |
| "grid.linewidth": 2.2, | |
| "axes.linewidth": 3.0, | |
| "font.size": 14, | |
| "svg.fonttype": "none", # keep text as text in SVG | |
| }) | |
| def style_axes(ax): | |
| ax.grid(True) | |
| for s in ax.spines.values(): | |
| s.set_linewidth(3.2) | |
| s.set_color(FG) | |
| ax.margins(x=0.02, y=0.15) | |
| def plot_line(ax, x, y): | |
| ax.plot( | |
| x, y, lw=3.2, color=LINE, | |
| marker="o", ms=7, | |
| markerfacecolor=BG, markeredgecolor=FG, markeredgewidth=2.5, | |
| path_effects=[pe.Stroke(linewidth=6, foreground=FG), pe.Normal()], | |
| ) | |
| def savefig(fig, name): | |
| OUTDIR.mkdir(parents=True, exist_ok=True) | |
| png = OUTDIR / f"{name}.png" | |
| svg = OUTDIR / f"{name}.svg" | |
| fig.savefig(png, dpi=DPI, bbox_inches="tight", facecolor=fig.get_facecolor()) | |
| fig.savefig(svg, bbox_inches="tight", facecolor=fig.get_facecolor()) | |
| return png, svg | |
| def slugify(s: str) -> str: | |
| return "".join(c.lower() if c.isalnum() else "-" for c in s).strip("-").replace("--", "-") | |
| # --------- Data --------- | |
| # Replace values with your real measurements if needed. | |
| years = np.array([1980, 1990, 2000, 2005, 2010, 2015, 2020, 2025]) | |
| series = [ | |
| # --- original five --- | |
| {"title": "Transistor Count / Socket", "ylabel": "Transistors (Billions)", | |
| "values": [0, 0.1, 0.3, 0.6, 2, 30, 70, 90]}, | |
| {"title": "Core Count / Socket", "ylabel": "Cores", | |
| "values": [1, 1, 1, 2, 8, 32, 64, 96]}, | |
| {"title": "Vector GFLOPS", "ylabel": "GFLOPS", | |
| "values": [0, 0.5, 1, 5, 100, 2000, 3000, 3800]}, | |
| {"title": "Memory BW", "ylabel": "GB/s", | |
| "values": [0, 0.5, 1, 4, 50, 140, 320, 480]}, | |
| {"title": "Total Cache Size", "ylabel": "MB", | |
| "values": [0.1, 0.5, 1, 2, 24, 192, 256, 320]}, | |
| # --- new two (from your image) --- | |
| {"title": "Storage BW / drive", "ylabel": "GB/s", | |
| "values": [0.02, 0.04, 0.08, 0.12, 0.6, 3.5, 5.5, 7.2], | |
| "ylim": (0, 7.5), "yticks": [0, 2, 4, 6]}, | |
| {"title": "Network BW", "ylabel": "GB/s", | |
| "values": [0.02, 0.05, 0.2, 0.6, 5, 12, 30, 50], | |
| "ylim": (0, 52), "yticks": [0, 10, 20, 30, 40, 50]}, | |
| ] | |
| # --------- Main ---------- | |
| def make_all(): | |
| apply_global_rc() | |
| with plt.xkcd(scale=1.0, length=200, randomness=2.5): | |
| # Stacked (all panels) | |
| fig, axes = plt.subplots(len(series), 1, figsize=(8, 18), sharex=True) | |
| for ax, s in zip(axes, series): | |
| y = np.array(s["values"], dtype=float) | |
| plot_line(ax, years, y) | |
| ax.set_title(s["title"]) | |
| ax.set_ylabel(s["ylabel"]) | |
| if "ylim" in s: ax.set_ylim(*s["ylim"]) | |
| if "yticks" in s: ax.set_yticks(s["yticks"]) | |
| style_axes(ax) | |
| axes[-1].set_xlabel("Year") | |
| savefig(fig, "stacked_all") | |
| plt.close(fig) | |
| # Individual panel figures | |
| for idx, s in enumerate(series, start=1): | |
| fig, ax = plt.subplots(figsize=(7.2, 4.5)) | |
| y = np.array(s["values"], dtype=float) | |
| plot_line(ax, years, y) | |
| ax.set_title(s["title"]) | |
| ax.set_xlabel("Year") | |
| ax.set_ylabel(s["ylabel"]) | |
| if "ylim" in s: ax.set_ylim(*s["ylim"]) | |
| if "yticks" in s: ax.set_yticks(s["yticks"]) | |
| style_axes(ax) | |
| savefig(fig, f"panel_{idx:02d}_{slugify(s['title'])}") | |
| plt.close(fig) | |
| if __name__ == "__main__": | |
| make_all() | |
| print(f"Saved images to: {OUTDIR.resolve()}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment