{"spec_id":"mosaic-categorical","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nmosaic-categorical: Mosaic Plot for Categorical Association Analysis\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-19\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as mpatches\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\"\n\n# Okabe-Ito palette — first series always #009E73\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\ncolor_survived = IMPRINT[0]  # bluish green\ncolor_not_survived = IMPRINT[1]  # vermillion\n\nsns.set_theme(\n    style=\"white\",\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\ndf = sns.load_dataset(\"titanic\")\n\n# Contingency table: rows = passenger class, columns = [Survived, Did Not Survive]\ncontingency_df = df.groupby([\"class\", \"survived\"], observed=True).size().unstack(fill_value=0)\ncontingency = contingency_df[[1, 0]].values  # 1=Survived first\ncategories_1 = [\"First\", \"Second\", \"Third\"]\ncategories_2 = [\"Survived\", \"Did Not Survive\"]\n\n# Calculate proportions\nrow_totals = contingency.sum(axis=1)\ntotal = contingency.sum()\ncol_widths = row_totals / total\ncol_heights = contingency / row_totals[:, np.newaxis]\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\ngap = 0.015\nplot_left = 0.15\nplot_bottom = 0.12\nplot_width = 0.75\nplot_height = 0.75\n\n# Draw mosaic rectangles\nx_start = plot_left\nfor i, cat1 in enumerate(categories_1):\n    width = col_widths[i] * plot_width - gap\n    y_start = plot_bottom\n\n    for j in range(len(categories_2)):\n        height = col_heights[i, j] * plot_height - gap / 2\n        color = color_survived if j == 0 else color_not_survived\n\n        rect = mpatches.FancyBboxPatch(\n            (x_start + gap / 2, y_start + gap / 4),\n            width,\n            height,\n            boxstyle=\"round,pad=0,rounding_size=0.008\",\n            facecolor=color,\n            edgecolor=PAGE_BG,\n            linewidth=3,\n        )\n        ax.add_patch(rect)\n\n        freq = contingency[i, j]\n        cx = x_start + gap / 2 + width / 2\n        cy = y_start + gap / 4 + height / 2\n        if height > 0.05:\n            ax.text(cx, cy, f\"{freq}\", ha=\"center\", va=\"center\", fontsize=22, fontweight=\"bold\", color=\"white\")\n\n        y_start += height + gap / 2\n\n    cx = x_start + gap / 2 + width / 2\n    ax.text(cx, plot_bottom - 0.04, cat1, ha=\"center\", va=\"top\", fontsize=18, fontweight=\"bold\", color=INK)\n\n    x_start += col_widths[i] * plot_width\n\n# Row labels on the left\navg_survived_h = col_heights[:, 0].mean() * plot_height\navg_not_survived_h = col_heights[:, 1].mean() * plot_height\nsurvived_y = plot_bottom + avg_survived_h / 2\nnot_survived_y = plot_bottom + avg_survived_h + avg_not_survived_h / 2\n\nax.text(plot_left - 0.02, survived_y, \"Survived\", ha=\"right\", va=\"center\", fontsize=18, fontweight=\"bold\", color=INK)\nax.text(\n    plot_left - 0.02,\n    not_survived_y,\n    \"Did Not\\nSurvive\",\n    ha=\"right\",\n    va=\"center\",\n    fontsize=18,\n    fontweight=\"bold\",\n    color=INK,\n)\n\nax.set_xlim(0, 1)\nax.set_ylim(0, 1)\nax.set_aspect(\"auto\")\nax.axis(\"off\")\n\n# Title\nax.set_title(\n    \"Titanic Passenger Survival by Class · mosaic-categorical · python · seaborn · anyplot.ai\",\n    fontsize=24,\n    fontweight=\"bold\",\n    pad=15,\n    loc=\"center\",\n    color=INK,\n)\n\n# Legend — anchored inside the right margin of the plot area\nlegend_elements = [\n    mpatches.Patch(facecolor=color_survived, edgecolor=PAGE_BG, linewidth=2, label=\"Survived\"),\n    mpatches.Patch(facecolor=color_not_survived, edgecolor=PAGE_BG, linewidth=2, label=\"Did Not Survive\"),\n]\nax.legend(\n    handles=legend_elements,\n    loc=\"center left\",\n    fontsize=16,\n    framealpha=0.95,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n    bbox_to_anchor=(0.91, 0.50),\n    labelcolor=INK,\n)\n\n# Axis labels\nax.text(\n    plot_left + plot_width / 2,\n    plot_bottom - 0.09,\n    \"Passenger Class\",\n    ha=\"center\",\n    va=\"top\",\n    fontsize=20,\n    fontweight=\"bold\",\n    color=INK,\n)\nax.text(\n    0.02,\n    plot_bottom + plot_height / 2,\n    \"Survival Status\",\n    ha=\"left\",\n    va=\"center\",\n    fontsize=20,\n    fontweight=\"bold\",\n    rotation=90,\n    color=INK,\n)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}