{"spec_id":"dot-matrix-proportional","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\ndot-matrix-proportional: Dot Matrix Chart for Proportional Counts\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 86/100 | Created: 2026-05-08\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib.lines import Line2D\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\"\n\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\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 — renewable energy source preference survey (100 respondents)\ncategories = [\"Solar Power\", \"Wind Energy\", \"Hydropower\", \"Other Sources\"]\ncounts = [38, 29, 21, 12]\ntotal = sum(counts)  # 100\n\n# Build dot grid: 10 columns × 10 rows, filled left-to-right, top-to-bottom\ncols = 10\nrows = total // cols\n\ndot_labels = []\nfor cat, n in zip(categories, counts, strict=True):\n    dot_labels.extend([cat] * n)\n\nxs = [i % cols for i in range(total)]\nys = [rows - 1 - (i // cols) for i in range(total)]\n\ndf = pd.DataFrame({\"x\": xs, \"y\": ys, \"category\": dot_labels})\n\n# Plot\nfig, ax = plt.subplots(figsize=(12, 12), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\npalette = dict(zip(categories, IMPRINT, strict=True))\n\nsns.scatterplot(\n    data=df,\n    x=\"x\",\n    y=\"y\",\n    hue=\"category\",\n    palette=palette,\n    s=1200,\n    linewidth=0.8,\n    edgecolors=PAGE_BG,\n    ax=ax,\n    legend=False,\n)\n\nax.set_xlim(-0.8, 9.8)\nax.set_ylim(-0.8, 9.8)\nax.set_aspect(\"equal\")\n\n# Legend with counts and percentages, placed to the right of the grid\nlegend_handles = [\n    Line2D(\n        [0],\n        [0],\n        marker=\"o\",\n        color=\"none\",\n        markerfacecolor=IMPRINT[i],\n        markeredgecolor=PAGE_BG,\n        markeredgewidth=0.5,\n        markersize=16,\n        label=f\"{categories[i]}  ·  {counts[i]} / {total}  ({counts[i]}%)\",\n    )\n    for i in range(len(categories))\n]\n\nleg = ax.legend(\n    handles=legend_handles,\n    fontsize=16,\n    title=\"Energy Source  (n = 100)\",\n    title_fontsize=17,\n    loc=\"center left\",\n    bbox_to_anchor=(1.03, 0.5),\n    frameon=True,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n    handletextpad=1.0,\n    borderpad=1.2,\n    labelspacing=1.2,\n)\nleg.get_title().set_color(INK)\nfor text in leg.get_texts():\n    text.set_color(INK_SOFT)\n\n# Style\nax.set_title(\n    \"Energy Source Survey · dot-matrix-proportional · seaborn · anyplot.ai\",\n    fontsize=22,\n    fontweight=\"medium\",\n    color=INK,\n    pad=24,\n)\nax.set_xlabel(\"\")\nax.set_ylabel(\"\")\nax.set_xticks([])\nax.set_yticks([])\nfor spine in ax.spines.values():\n    spine.set_visible(False)\n\n# Caption\nax.text(4.5, -0.62, \"1 dot = 1 respondent\", ha=\"center\", va=\"center\", fontsize=14, color=INK_MUTED, style=\"italic\")\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}