{"spec_id":"upset-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nupset-basic: UpSet Plot for Multi-Set Intersection Analysis\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 89/100 | Created: 2026-05-13\n\"\"\"\n\nimport os\nimport sys\nfrom collections import defaultdict\n\n\nsys.path.pop(0)  # prevent shadowing the matplotlib library with this file\n\nimport matplotlib.gridspec as gridspec\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n\n# Theme tokens\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\"\nBRAND = \"#009E73\"\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\"]\n\n# Data: bug reports spanning software system modules\nnp.random.seed(42)\nset_names = [\"Frontend\", \"Backend\", \"Database\", \"API\", \"Security\"]\nn_sets = len(set_names)\nn_bugs = 800\n\n# Probability of each bug affecting each module\nprobs = [0.42, 0.36, 0.28, 0.24, 0.16]\nmembership = np.column_stack([np.random.rand(n_bugs) < p for p in probs])\nmembership = membership[membership.any(axis=1)]\n\n# Intersection signatures and counts\nintersection_counts = defaultdict(int)\nfor row in membership:\n    intersection_counts[tuple(bool(v) for v in row)] += 1\n\nset_sizes = [int(membership[:, i].sum()) for i in range(n_sets)]\n\n# Sort intersections by count (descending), take top 12\nsorted_ints = sorted(intersection_counts.items(), key=lambda x: -x[1])[:12]\nint_sigs = [s for s, _ in sorted_ints]\nint_counts = [c for _, c in sorted_ints]\nn_ints = len(int_sigs)\ndegrees = [sum(sig) for sig in int_sigs]\n\n# Figure layout: 2×2 grid (top-left empty, top-right = bars, bottom-left = set sizes, bottom-right = matrix)\nfig = plt.figure(figsize=(16, 9), facecolor=PAGE_BG)\ngs = gridspec.GridSpec(\n    2,\n    2,\n    width_ratios=[1, 3.5],\n    height_ratios=[2.5, 1.5],\n    hspace=0.04,\n    wspace=0.06,\n    left=0.09,\n    right=0.97,\n    top=0.90,\n    bottom=0.05,\n)\nax_bars = fig.add_subplot(gs[0, 1])\nax_matrix = fig.add_subplot(gs[1, 1])\nax_sets = fig.add_subplot(gs[1, 0])\n\n# ── Intersection bars (top right) ──────────────────────────────────────────────\nbar_colors = [IMPRINT[min(d - 1, len(IMPRINT) - 1)] for d in degrees]\nax_bars.bar(range(n_ints), int_counts, color=bar_colors, width=0.65, zorder=2)\nfor i, (cnt, _col) in enumerate(zip(int_counts, bar_colors, strict=False)):\n    ax_bars.text(\n        i,\n        cnt + max(int_counts) * 0.018,\n        str(cnt),\n        ha=\"center\",\n        va=\"bottom\",\n        fontsize=13,\n        color=INK_SOFT,\n        fontweight=\"medium\",\n    )\n\nax_bars.set_xlim(-0.5, n_ints - 0.5)\nax_bars.set_ylim(0, max(int_counts) * 1.15)\nax_bars.set_ylabel(\"Intersection Size\", fontsize=18, color=INK, labelpad=8)\nax_bars.set_facecolor(PAGE_BG)\nax_bars.tick_params(axis=\"y\", labelsize=14, labelcolor=INK_SOFT, colors=INK_SOFT)\nax_bars.tick_params(axis=\"x\", bottom=False, labelbottom=False)\nax_bars.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\nax_bars.set_axisbelow(True)\nax_bars.spines[\"top\"].set_visible(False)\nax_bars.spines[\"right\"].set_visible(False)\nax_bars.spines[\"bottom\"].set_visible(False)\nax_bars.spines[\"left\"].set_color(INK_SOFT)\n\n# Degree legend inside intersection bars area\npatches = [mpatches.Patch(color=IMPRINT[min(d - 1, 5)], label=f\"Degree {d}\") for d in sorted(set(degrees))]\nleg = ax_bars.legend(\n    handles=patches,\n    fontsize=13,\n    loc=\"upper right\",\n    title=\"Intersection\\nDegree\",\n    title_fontsize=13,\n    frameon=True,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n)\nplt.setp(leg.get_texts(), color=INK_SOFT)\nplt.setp(leg.get_title(), color=INK_SOFT)\n\n# ── Dot matrix (bottom right) ──────────────────────────────────────────────────\nax_matrix.set_facecolor(PAGE_BG)\nax_matrix.set_xlim(-0.5, n_ints - 0.5)\nax_matrix.set_ylim(-0.5, n_sets - 0.5)\nax_matrix.invert_yaxis()\nax_matrix.set_yticks(range(n_sets))\nax_matrix.set_yticklabels([])\nax_matrix.tick_params(axis=\"x\", bottom=False, labelbottom=False)\nax_matrix.tick_params(axis=\"y\", left=False, length=0)\n\n# Alternating row shading\nfor i in range(n_sets):\n    if i % 2 == 0:\n        ax_matrix.axhspan(i - 0.5, i + 0.5, color=INK, alpha=0.04, zorder=0)\n\n# Inactive dots\nfor col_j, sig in enumerate(int_sigs):\n    for row_i, active in enumerate(sig):\n        if not active:\n            ax_matrix.scatter(col_j, row_i, s=190, color=INK_MUTED, alpha=0.22, zorder=1, linewidths=0)\n\n# Active dots + connecting lines\nfor col_j, (sig, deg) in enumerate(zip(int_sigs, degrees, strict=False)):\n    color = IMPRINT[min(deg - 1, len(IMPRINT) - 1)]\n    active_rows = [i for i, v in enumerate(sig) if v]\n    if len(active_rows) > 1:\n        ax_matrix.plot(\n            [col_j, col_j],\n            [min(active_rows), max(active_rows)],\n            color=color,\n            linewidth=4.5,\n            solid_capstyle=\"round\",\n            zorder=2,\n        )\n    for row_i in active_rows:\n        ax_matrix.scatter(col_j, row_i, s=210, color=color, zorder=3, linewidths=0)\n\nfor spine in ax_matrix.spines.values():\n    spine.set_visible(False)\n\n# ── Set size bars (bottom left) ────────────────────────────────────────────────\nax_sets.set_facecolor(PAGE_BG)\nax_sets.barh(range(n_sets), set_sizes, color=BRAND, height=0.55)\nax_sets.set_xlim(0, max(set_sizes) * 1.08)\nax_sets.invert_xaxis()\nax_sets.set_ylim(-0.5, n_sets - 0.5)\nax_sets.invert_yaxis()\nax_sets.set_yticks(range(n_sets))\nax_sets.set_yticklabels(set_names, fontsize=15, color=INK_SOFT)\nax_sets.set_xlabel(\"Set Size\", fontsize=18, color=INK, labelpad=8)\nax_sets.tick_params(axis=\"x\", labelsize=14, labelcolor=INK_SOFT, colors=INK_SOFT)\nax_sets.tick_params(axis=\"y\", left=False, length=0, labelright=False, labelleft=True)\nax_sets.spines[\"top\"].set_visible(False)\nax_sets.spines[\"right\"].set_visible(False)\nax_sets.spines[\"left\"].set_visible(False)\nax_sets.spines[\"bottom\"].set_color(INK_SOFT)\nax_sets.xaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\nax_sets.set_axisbelow(True)\n\n# ── Title ──────────────────────────────────────────────────────────────────────\nfig.text(\n    0.5,\n    0.95,\n    \"upset-basic · matplotlib · anyplot.ai\",\n    ha=\"center\",\n    va=\"center\",\n    fontsize=22,\n    fontweight=\"medium\",\n    color=INK,\n)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}