{"spec_id":"bar-pareto","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nbar-pareto: Pareto Chart with Cumulative Line\nLibrary: matplotlib 3.11.0 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-06-20\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as mticker\nimport numpy as np\nfrom matplotlib.patches import Patch\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — position 1 is always first series\nBAR_VITAL = \"#009E73\"  # Imprint pos 1 (brand green) — vital few bars\nLINE_COLOR = \"#4467A3\"  # Imprint pos 3 (blue) — cumulative % line\n\n# Data — manufacturing defect analysis\ndefect_types = [\"Scratches\", \"Dents\", \"Misalignment\", \"Cracks\", \"Discoloration\", \"Burrs\", \"Warping\", \"Contamination\"]\ndefect_counts = np.array([142, 98, 71, 45, 32, 18, 12, 7])\n\nsort_idx = np.argsort(-defect_counts)\ndefect_types = [defect_types[i] for i in sort_idx]\ndefect_counts = defect_counts[sort_idx]\ncumulative_pct = np.cumsum(defect_counts) / defect_counts.sum() * 100\n\n# Vital few: bars that collectively account for >= 80% of defects\ncross_idx = np.searchsorted(cumulative_pct, 80)  # first bar index at or past 80%\nvital_count = cross_idx + 1\nbar_colors = [BAR_VITAL if i < vital_count else INK_MUTED for i in range(len(defect_types))]\n\n# Plot — landscape 3200×1800 px\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\nx = np.arange(len(defect_types))\nbars = ax.bar(x, defect_counts, color=bar_colors, width=0.65, zorder=2, edgecolor=PAGE_BG, linewidth=0.5)\nax.bar_label(bars, fontsize=8, fontweight=\"bold\", padding=4, color=INK)\n\nax2 = ax.twinx()\nax2.patch.set_visible(False)\nax2.plot(\n    x,\n    cumulative_pct,\n    color=LINE_COLOR,\n    marker=\"o\",\n    markersize=6,\n    linewidth=2.5,\n    markeredgecolor=PAGE_BG,\n    markeredgewidth=1.5,\n    zorder=3,\n)\nax2.axhline(y=80, color=LINE_COLOR, linestyle=\"--\", linewidth=1.2, alpha=0.6, zorder=1)\n\n# Annotate 80% crossing point with interpolated x position\ncross_x = np.interp(80, cumulative_pct[max(0, cross_idx - 1) : cross_idx + 1], x[max(0, cross_idx - 1) : cross_idx + 1])\nax2.annotate(\n    f\"80% reached\\n({vital_count} of {len(defect_types)} types)\",\n    xy=(cross_x, 80),\n    xytext=(cross_x + 1.8, 62),\n    fontsize=8,\n    color=INK,\n    fontweight=\"bold\",\n    arrowprops={\"arrowstyle\": \"->\", \"color\": LINE_COLOR, \"lw\": 1.5},\n    bbox={\"boxstyle\": \"round,pad=0.4\", \"fc\": ELEVATED_BG, \"ec\": LINE_COLOR, \"alpha\": 0.9},\n    zorder=4,\n)\n\n# Labels & title\nax.set_title(\"bar-pareto · python · matplotlib · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK, pad=8)\nax.set_xlabel(\"Defect Type\", fontsize=10, color=INK)\nax.set_ylabel(\"Frequency\", fontsize=10, color=INK)\nax2.set_ylabel(\"Cumulative %\", fontsize=10, color=LINE_COLOR)\n\nax.set_xticks(x)\nax.set_xticklabels(defect_types, fontsize=8, rotation=20, ha=\"right\", color=INK_SOFT)\nax.tick_params(axis=\"y\", labelsize=8, labelcolor=INK_SOFT, length=0)\nax.tick_params(axis=\"x\", length=0)\nax2.tick_params(axis=\"y\", labelsize=8, labelcolor=LINE_COLOR, length=0)\nax2.yaxis.set_major_formatter(mticker.FuncFormatter(lambda v, _: f\"{v:.0f}%\"))\n\nax2.set_ylim(0, 110)\nax.set_ylim(0, max(defect_counts) * 1.18)\nax.set_xlim(-0.5, len(defect_types) - 0.5)\n\n# Spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax2.spines[\"top\"].set_visible(False)\nax2.spines[\"right\"].set_color(LINE_COLOR)\nfor spine in (\"left\", \"bottom\"):\n    ax.spines[spine].set_color(INK_SOFT)\n\n# Grid\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\nax.set_axisbelow(True)\n\n# Legend distinguishing vital few from trivial many\nlegend_elements = [\n    Patch(facecolor=BAR_VITAL, edgecolor=PAGE_BG, label=\"Vital few\"),\n    Patch(facecolor=INK_MUTED, edgecolor=PAGE_BG, label=\"Trivial many\"),\n]\nleg = ax.legend(handles=legend_elements, fontsize=8, loc=\"upper left\", frameon=True)\nleg.get_frame().set_facecolor(ELEVATED_BG)\nleg.get_frame().set_edgecolor(INK_SOFT)\nplt.setp(leg.get_texts(), color=INK_SOFT)\n\nfig.subplots_adjust(left=0.08, right=0.88, top=0.93, bottom=0.16)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\nplt.close()\n"}