{"spec_id":"venn-labeled-items","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nvenn-labeled-items: Chartgeist-Style Venn Diagram with Labeled Items\nLibrary: seaborn 0.13.2 | Python 3.13.14\nQuality: 86/100 | Updated: 2026-06-25\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib.patches import Circle\n\n\n# Theme tokens — Imprint palette chrome\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 positions 1-3 for the three Venn circles\nIMPRINT_GREEN = \"#009E73\"  # position 1 — Overhyped\nIMPRINT_LAVENDER = \"#C475FD\"  # position 2 — Actually Useful\nIMPRINT_BLUE = \"#4467A3\"  # position 3 — Secretly Loved\n\n# Seaborn scatter palette: Imprint positions 4-5 + semantic anchors for overlap-depth hue\n# Distinct from circle fill colors (positions 1-3) to avoid visual ambiguity\nDEPTH_PALETTE = {\n    \"outside\": INK_MUTED,  # semantic muted anchor: other/rest\n    \"1 circle\": INK_SOFT,  # secondary chrome: baseline items\n    \"2 circles\": \"#BD8233\",  # Imprint ochre (position 4): bridging items\n    \"3 circles\": \"#AE3030\",  # Imprint matte red (position 5): convergence hot-spot\n}\nOVERLAP_ORDER = [\"outside\", \"1 circle\", \"2 circles\", \"3 circles\"]\n\nsns.set_theme(\n    style=\"white\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": PAGE_BG,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"font.family\": \"serif\",\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data — tech & pop-culture taxonomy across three editorial lenses\ncircles = [\n    {\"name\": \"Overhyped\", \"color\": IMPRINT_GREEN, \"center\": (0.00, 0.55)},\n    {\"name\": \"Actually Useful\", \"color\": IMPRINT_LAVENDER, \"center\": (-0.476, -0.275)},\n    {\"name\": \"Secretly Loved\", \"color\": IMPRINT_BLUE, \"center\": (0.476, -0.275)},\n]\n\nzone_items = {\n    \"A\": [\"NFTs\", \"Metaverse\", \"Web3\"],\n    \"B\": [\"Spreadsheets\", \"Calculators\"],\n    \"C\": [\"Karaoke\", \"Bob Ross\"],\n    \"AB\": [\"Crypto\", \"ChatGPT\"],\n    \"AC\": [\"TikTok\", \"Pumpkin Spice\"],\n    \"BC\": [\"Google Maps\", \"Dolly Parton\", \"IKEA Meatballs\"],\n    \"ABC\": [\"Sourdough\"],\n    \"outside\": [\"Jury Duty\"],\n}\n\nzone_centroids = {\n    \"A\": (0.00, 1.05),\n    \"B\": (-1.00, -0.62),\n    \"C\": (1.00, -0.62),\n    \"AB\": (-0.55, 0.32),\n    \"AC\": (0.55, 0.32),\n    \"BC\": (0.00, -0.65),\n    \"ABC\": (0.00, 0.05),\n    \"outside\": (-1.95, 1.30),\n}\n\nzone_to_overlap = {\n    \"outside\": \"outside\",\n    \"A\": \"1 circle\",\n    \"B\": \"1 circle\",\n    \"C\": \"1 circle\",\n    \"AB\": \"2 circles\",\n    \"AC\": \"2 circles\",\n    \"BC\": \"2 circles\",\n    \"ABC\": \"3 circles\",\n}\n\n# Long-form DataFrame: each row = one labeled item with x, y, and overlap category\nrows = []\nspacing = 0.20\nfor zone, labels in zone_items.items():\n    cx, cy = zone_centroids[zone]\n    n = len(labels)\n    start_y = cy + (n - 1) * spacing / 2\n    for i, label in enumerate(labels):\n        rows.append({\"label\": label, \"x\": cx, \"y\": start_y - i * spacing, \"overlap\": zone_to_overlap[zone]})\nitems_df = pd.DataFrame(rows)\n\n# Plot — square canvas for the symmetric Venn layout (2400×2400 px)\nfig, ax = plt.subplots(figsize=(6, 6), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\nax.set_aspect(\"equal\")\n\nr = 0.85\nfor c in circles:\n    ax.add_patch(Circle(c[\"center\"], radius=r, facecolor=c[\"color\"], alpha=0.18, edgecolor=c[\"color\"], linewidth=1.0))\n\n# Subtle dashed highlight on the triple-overlap convergence zone\nax.add_patch(\n    Circle(\n        zone_centroids[\"ABC\"],\n        radius=0.13,\n        facecolor=INK,\n        alpha=0.05,\n        edgecolor=INK_SOFT,\n        linewidth=0.4,\n        linestyle=(0, (2, 2)),\n    )\n)\n\n# Seaborn hue+size scatter: overlap depth drives both marker color and size\n# This makes seaborn's categorical hue encoding the central visual layer\nsns.scatterplot(\n    data=items_df,\n    x=\"x\",\n    y=\"y\",\n    hue=\"overlap\",\n    size=\"overlap\",\n    sizes={\"outside\": 55, \"1 circle\": 75, \"2 circles\": 110, \"3 circles\": 160},\n    palette=DEPTH_PALETTE,\n    hue_order=OVERLAP_ORDER,\n    size_order=OVERLAP_ORDER,\n    alpha=0.80,\n    edgecolor=\"none\",\n    legend=\"full\",\n    ax=ax,\n)\n\n# Style the seaborn legend: deduplicate hue+size entries, position in upper-right whitespace\nhandles, labels = ax.get_legend_handles_labels()\nseen, h_dedup, l_dedup = set(), [], []\nfor h, lbl in zip(handles, labels, strict=False):\n    if lbl not in seen:\n        seen.add(lbl)\n        h_dedup.append(h)\n        l_dedup.append(lbl)\nax.legend(\n    h_dedup,\n    l_dedup,\n    title=\"Overlap depth\",\n    loc=\"upper right\",\n    fontsize=7,\n    title_fontsize=7,\n    framealpha=1.0,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n    borderpad=0.6,\n)\nax.get_legend().get_frame().set_linewidth(0.5)\n\n# Category labels positioned outside each circle on its far side\nax.text(\n    0,\n    0.55 + r + 0.06,\n    circles[0][\"name\"],\n    ha=\"center\",\n    va=\"bottom\",\n    fontsize=12,\n    fontweight=\"bold\",\n    color=circles[0][\"color\"],\n    clip_on=False,\n)\nax.text(\n    -1.40,\n    -0.275 - 0.80,\n    circles[1][\"name\"],\n    ha=\"right\",\n    va=\"top\",\n    fontsize=12,\n    fontweight=\"bold\",\n    color=circles[1][\"color\"],\n    clip_on=False,\n)\nax.text(\n    1.40,\n    -0.275 - 0.80,\n    circles[2][\"name\"],\n    ha=\"left\",\n    va=\"top\",\n    fontsize=12,\n    fontweight=\"bold\",\n    color=circles[2][\"color\"],\n    clip_on=False,\n)\n\n# Item labels placed slightly above their dot markers\nlabel_offset = 0.07\nfor _, row in items_df.iterrows():\n    is_triple = row[\"overlap\"] == \"3 circles\"\n    is_outside = row[\"overlap\"] == \"outside\"\n    ax.text(\n        row[\"x\"],\n        row[\"y\"] + label_offset,\n        row[\"label\"],\n        ha=\"center\",\n        va=\"bottom\",\n        fontsize=11 if is_triple else 10,\n        fontweight=\"bold\" if is_triple else \"normal\",\n        color=INK_MUTED if is_outside else INK,\n        style=\"italic\" if is_outside else \"normal\",\n        clip_on=False,\n    )\n\n# Parenthetical hint for the outside cluster\nax.text(\n    -1.95, 1.55, \"(outside all)\", ha=\"center\", va=\"bottom\", fontsize=8, color=INK_MUTED, style=\"italic\", clip_on=False\n)\n\n# Mandated title and editorial subtitle\ntitle = \"venn-labeled-items · python · seaborn · anyplot.ai\"\nfig.suptitle(title, fontsize=13, fontweight=\"medium\", color=INK, y=0.965)\nfig.text(\n    0.5,\n    0.918,\n    \"Tech & Trends — a Chartgeist taxonomy of what we love, use, and overrate\",\n    ha=\"center\",\n    va=\"top\",\n    fontsize=9,\n    color=INK_SOFT,\n    style=\"italic\",\n)\n\nax.set_xlim(-2.35, 2.35)\nax.set_ylim(-1.50, 1.85)\nax.axis(\"off\")\n\n# Save — bbox_inches omitted (defaults to None) to preserve the exact 2400×2400 canvas\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}