{"spec_id":"upset-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nupset-basic: UpSet Plot for Multi-Set Intersection Analysis\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 82/100 | Created: 2026-05-13\n\"\"\"\n\nimport os\nfrom collections import Counter\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\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\"\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data: customer segments across marketing channels\nnp.random.seed(42)\nn_customers = 1200\nchannel_names = [\"Email\", \"Mobile\", \"Social\", \"Premium\", \"Loyalty\"]\nchannel_probs = [0.62, 0.51, 0.39, 0.27, 0.21]\n\nmembership = np.column_stack([np.random.random(n_customers) < p for p in channel_probs])\nmembership = membership[membership.any(axis=1)]\nn_sets = len(channel_names)\n\nintersection_counts = Counter(tuple(row) for row in membership)\nsorted_ints = sorted(intersection_counts.items(), key=lambda x: -x[1])\ntop_n = min(14, len(sorted_ints))\ntop_keys = [k for k, _ in sorted_ints[:top_n]]\ntop_counts = [v for _, v in sorted_ints[:top_n]]\nn_ints = len(top_keys)\nset_sizes = membership.sum(axis=0)\n\n# Figure layout\nfig = plt.figure(figsize=(16, 9), facecolor=PAGE_BG)\ngs = fig.add_gridspec(\n    2,\n    2,\n    width_ratios=[1.4, 5],\n    height_ratios=[3, 2],\n    hspace=0.0,\n    wspace=0.0,\n    left=0.07,\n    right=0.97,\n    top=0.88,\n    bottom=0.05,\n)\n\nax_corner = fig.add_subplot(gs[0, 0])\nax_intbar = fig.add_subplot(gs[0, 1])\nax_setbar = fig.add_subplot(gs[1, 0])\nax_matrix = fig.add_subplot(gs[1, 1])\n\nx_cols = np.arange(n_ints)\ny_rows = np.arange(n_sets)\n\n# Intersection bar chart (top right)\nax_intbar.set_facecolor(PAGE_BG)\nax_intbar.set_axisbelow(True)\nax_intbar.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\nax_intbar.bar(x_cols, top_counts, color=BRAND, width=0.55, zorder=2)\nfor i, cnt in enumerate(top_counts):\n    ax_intbar.text(i, cnt + max(top_counts) * 0.015, str(cnt), ha=\"center\", va=\"bottom\", fontsize=10, color=INK_SOFT)\nax_intbar.set_xlim(-0.5, n_ints - 0.5)\nax_intbar.set_ylabel(\"Intersection Size\", fontsize=17, color=INK, labelpad=8)\nax_intbar.tick_params(axis=\"y\", labelsize=13, colors=INK_SOFT)\nax_intbar.tick_params(axis=\"x\", bottom=False, labelbottom=False)\nax_intbar.spines[\"top\"].set_visible(False)\nax_intbar.spines[\"right\"].set_visible(False)\nax_intbar.spines[\"bottom\"].set_visible(False)\nax_intbar.spines[\"left\"].set_color(INK_SOFT)\n\n# Dot matrix (bottom right)\nax_matrix.set_facecolor(PAGE_BG)\nfor row_idx in range(n_sets):\n    ax_matrix.axhline(row_idx, color=INK, alpha=0.06, linewidth=0.8, zorder=1)\nfor col_idx, key in enumerate(top_keys):\n    active_rows = [r for r, active in enumerate(key) if active]\n    for row_idx, is_active in enumerate(key):\n        if is_active:\n            ax_matrix.scatter(col_idx, row_idx, s=200, color=BRAND, zorder=4, linewidths=0)\n        else:\n            ax_matrix.scatter(col_idx, row_idx, s=55, color=INK_MUTED, alpha=0.28, zorder=3, linewidths=0)\n    if len(active_rows) > 1:\n        ax_matrix.plot(\n            [col_idx, col_idx],\n            [min(active_rows), max(active_rows)],\n            color=BRAND,\n            linewidth=3.5,\n            zorder=3,\n            solid_capstyle=\"round\",\n        )\nax_matrix.set_xlim(-0.5, n_ints - 0.5)\nax_matrix.set_ylim(-0.5, n_sets - 0.5)\nax_matrix.set_yticks(y_rows)\nax_matrix.set_yticklabels(channel_names, fontsize=14, color=INK)\nax_matrix.tick_params(axis=\"x\", bottom=False, labelbottom=False)\nax_matrix.tick_params(axis=\"y\", left=False)\nfor spine in ax_matrix.spines.values():\n    spine.set_visible(False)\n\n# Set size bars (bottom left, bars extend left toward margin)\nax_setbar.set_facecolor(PAGE_BG)\nax_setbar.set_axisbelow(True)\nax_setbar.xaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\nfor row_idx in range(n_sets):\n    ax_setbar.axhline(row_idx, color=INK, alpha=0.06, linewidth=0.8, zorder=1)\nax_setbar.barh(y_rows, set_sizes, color=BRAND, height=0.45, zorder=2)\nax_setbar.set_ylim(-0.5, n_sets - 0.5)\nax_setbar.invert_xaxis()\nax_setbar.set_xlabel(\"Set Size\", fontsize=15, color=INK, labelpad=6)\nax_setbar.tick_params(axis=\"x\", labelsize=12, colors=INK_SOFT)\nax_setbar.tick_params(axis=\"y\", left=False, labelleft=False)\nax_setbar.spines[\"top\"].set_visible(False)\nax_setbar.spines[\"left\"].set_visible(False)\nax_setbar.spines[\"right\"].set_visible(False)\nax_setbar.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Empty corner\nax_corner.set_facecolor(PAGE_BG)\nax_corner.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)\nfor spine in ax_corner.spines.values():\n    spine.set_visible(False)\n\n# Title\nfig.text(\n    0.5,\n    0.93,\n    \"Customer Segments · upset-basic · seaborn · anyplot.ai\",\n    ha=\"center\",\n    va=\"center\",\n    fontsize=21,\n    fontweight=\"medium\",\n    color=INK,\n)\n\n# Save\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}