{"spec_id":"dot-matrix-proportional","library":"altair","language":"python","code":"\"\"\" anyplot.ai\ndot-matrix-proportional: Dot Matrix Chart for Proportional Counts\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 89/100 | Created: 2026-05-08\n\"\"\"\n\nimport importlib\nimport os\nimport sys\n\n\n# Remove the script's own directory from sys.path so \"import altair\" resolves\n# the library, not this file (which shares the library's name).\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if not (p and os.path.abspath(p) == _this_dir)]\n\nalt = importlib.import_module(\"altair\")\npd = importlib.import_module(\"pandas\")\n\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\nCOLORS = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data: conference session survey — 100 attendees, 10×10 grid\n# Categories ordered alphabetically to match Altair's nominal-sort color assignment.\ncategories = [\"Agree\", \"Neutral\", \"Oppose\"]\ncounts = [58, 27, 15]\ntotal = 100\ngrid_cols = 10\ngrid_rows = 10\n\n# Build dot grid: fill left-to-right, top-to-bottom\ndots = []\nfor cat, cnt in zip(categories, counts, strict=True):\n    dots.extend([cat] * cnt)\n\nrecords = []\nfor idx, cat in enumerate(dots):\n    records.append({\"col\": idx % grid_cols, \"row\": idx // grid_cols, \"category\": cat})\n\ndf = pd.DataFrame(records)\n\ncat_counts = dict(zip(categories, counts, strict=True))\n\n# Vega expression to append count to each legend label\nlabel_expr = \" : \".join(f\"datum.label === '{c}' ? '{c} — {n}'\" for c, n in cat_counts.items()) + \" : datum.label\"\n\nchart = (\n    alt.Chart(df)\n    .mark_circle(size=5500, opacity=1.0)\n    .encode(\n        x=alt.X(\"col:O\", axis=None),\n        y=alt.Y(\"row:O\", sort=\"ascending\", axis=None),\n        color=alt.Color(\n            \"category:N\",\n            scale=alt.Scale(domain=categories, range=COLORS),\n            legend=alt.Legend(\n                title=\"Response\",\n                titleFontSize=20,\n                labelFontSize=18,\n                symbolSize=200,\n                symbolType=\"circle\",\n                labelExpr=label_expr,\n                titleLimit=300,\n                labelLimit=300,\n            ),\n        ),\n        tooltip=[alt.Tooltip(\"category:N\", title=\"Response\")],\n    )\n    .properties(\n        width=1200,\n        height=1200,\n        background=PAGE_BG,\n        title=alt.TitleParams(\n            text=\"dot-matrix-proportional · altair · anyplot.ai\",\n            subtitle=\"Conference session survey — Would you recommend this talk?\",\n            fontSize=28,\n            subtitleFontSize=20,\n            color=INK,\n            subtitleColor=INK_SOFT,\n            anchor=\"start\",\n            offset=20,\n        ),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_legend(\n        fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK, padding=12, cornerRadius=4\n    )\n    .configure_title(color=INK)\n)\n\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}