{"spec_id":"marimekko-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nmarimekko-basic: Basic Marimekko Chart\nLibrary: altair 6.2.2 | Python 3.13.14\nQuality: 95/100 | Updated: 2026-07-24\n\"\"\"\n\nimport os\nimport sys\n\n\n# The file is named altair.py; remove its own directory from sys.path so\n# `import altair` resolves to the library, not this script.\n_HERE = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if not p or os.path.abspath(p) != _HERE]\n\nimport altair as alt\nimport pandas as pd\nfrom PIL import Image\n\n\n# Theme-adaptive chrome 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# Imprint categorical palette, canonical order\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\n# Data - Revenue by region (bar widths) and product line (segment heights)\ndata = {\n    \"Region\": [\"North America\"] * 4 + [\"Europe\"] * 4 + [\"Asia Pacific\"] * 4 + [\"Latin America\"] * 4,\n    \"Product\": [\"Electronics\", \"Clothing\", \"Food\", \"Home\"] * 4,\n    \"Revenue\": [\n        120,\n        80,\n        60,\n        40,  # North America (total: 300)\n        90,\n        70,\n        50,\n        30,  # Europe (total: 240)\n        100,\n        60,\n        80,\n        60,  # Asia Pacific (total: 300)\n        40,\n        30,\n        35,\n        25,  # Latin America (total: 130)\n    ],\n}\ndf = pd.DataFrame(data)\n\n# Region totals determine bar widths\nregion_totals = df.groupby(\"Region\")[\"Revenue\"].sum().reset_index()\nregion_totals.columns = [\"Region\", \"RegionTotal\"]\ngrand_total = region_totals[\"RegionTotal\"].sum()\nregion_totals[\"WidthPct\"] = region_totals[\"RegionTotal\"] / grand_total * 100\nregion_totals = region_totals.sort_values(\"RegionTotal\", ascending=False)\nregion_totals[\"x_start\"] = region_totals[\"WidthPct\"].cumsum() - region_totals[\"WidthPct\"]\nregion_totals[\"x_end\"] = region_totals[\"WidthPct\"].cumsum()\nregion_totals[\"x_mid\"] = (region_totals[\"x_start\"] + region_totals[\"x_end\"]) / 2\nregion_totals[\"Label\"] = region_totals[\"Region\"] + \"\\n($\" + region_totals[\"RegionTotal\"].astype(int).astype(str) + \"M)\"\n\ndf = df.merge(region_totals, on=\"Region\")\n\n# Product share within each region determines segment heights\ndf[\"PctWithinRegion\"] = df[\"Revenue\"] / df[\"RegionTotal\"] * 100\nproduct_order = [\"Electronics\", \"Clothing\", \"Food\", \"Home\"]\ndf[\"ProductOrder\"] = df[\"Product\"].map({p: i for i, p in enumerate(product_order)})\ndf = df.sort_values([\"Region\", \"ProductOrder\"])\ndf[\"y_end\"] = df.groupby(\"Region\")[\"PctWithinRegion\"].cumsum()\ndf[\"y_start\"] = df[\"y_end\"] - df[\"PctWithinRegion\"]\ndf[\"y_mid\"] = (df[\"y_start\"] + df[\"y_end\"]) / 2\ndf[\"RevenueLabel\"] = \"$\" + df[\"Revenue\"].astype(str) + \"M\"\n\n# Purple (Clothing) is light enough that white text loses contrast; use ink instead.\nTEXT_ON_COLOR = {\"Electronics\": \"#FFFFFF\", \"Clothing\": \"#1A1A17\", \"Food\": \"#FFFFFF\", \"Home\": \"#FFFFFF\"}\ndf[\"LabelColor\"] = df[\"Product\"].map(TEXT_ON_COLOR)\n\n# Legend-bound selection: clicking a legend swatch isolates that product line\n# across all regions (altair-native interactivity, visible in the HTML export).\nhighlight = alt.selection_point(fields=[\"Product\"], bind=\"legend\")\n\n# Largest single segment gets a subtle dashed outline to draw the eye.\ntop = df.loc[df[\"Revenue\"].idxmax()]\ntop_outline = (\n    alt.Chart(\n        pd.DataFrame(\n            [{\"x_start\": top[\"x_start\"], \"x_end\": top[\"x_end\"], \"y_start\": top[\"y_start\"], \"y_end\": top[\"y_end\"]}]\n        )\n    )\n    .mark_rect(fill=None, stroke=INK, strokeWidth=2.5, strokeDash=[5, 3])\n    .encode(x=\"x_start:Q\", x2=\"x_end:Q\", y=\"y_start:Q\", y2=\"y_end:Q\")\n)\n\nsegments = (\n    alt.Chart(df)\n    .mark_rect(stroke=PAGE_BG, strokeWidth=2, cornerRadius=2)\n    .encode(\n        x=alt.X(\"x_start:Q\", axis=None),\n        x2=\"x_end:Q\",\n        y=alt.Y(\n            \"y_start:Q\",\n            axis=alt.Axis(title=\"Product Mix (%)\", labelFontSize=11, titleFontSize=13),\n            scale=alt.Scale(domain=[0, 100]),\n        ),\n        y2=\"y_end:Q\",\n        color=alt.Color(\n            \"Product:N\",\n            scale=alt.Scale(domain=product_order, range=IMPRINT_PALETTE),\n            legend=alt.Legend(\n                title=\"Product Line\",\n                titleFontSize=13,\n                labelFontSize=11,\n                symbolSize=130,\n                symbolType=\"circle\",\n                cornerRadius=6,\n                padding=8,\n            ),\n        ),\n        opacity=alt.condition(highlight, alt.value(1.0), alt.value(0.3)),\n        tooltip=[\n            alt.Tooltip(\"Region:N\", title=\"Region\"),\n            alt.Tooltip(\"Product:N\", title=\"Product\"),\n            alt.Tooltip(\"Revenue:Q\", title=\"Revenue ($M)\", format=\",.0f\"),\n            alt.Tooltip(\"PctWithinRegion:Q\", title=\"% of Region\", format=\".1f\"),\n        ],\n    )\n    .add_params(highlight)\n)\n\nrevenue_labels = (\n    alt.Chart(df)\n    .mark_text(align=\"center\", baseline=\"middle\", fontSize=11, fontWeight=\"bold\")\n    .encode(\n        x=alt.X(\"x_mid:Q\", scale=alt.Scale(domain=[0, 100])),\n        y=alt.Y(\"y_mid:Q\", scale=alt.Scale(domain=[0, 100])),\n        text=\"RevenueLabel:N\",\n        color=alt.Color(\"LabelColor:N\", scale=None, legend=None),\n        opacity=alt.condition(highlight, alt.value(1.0), alt.value(0.3)),\n    )\n)\n\nregion_labels = (\n    alt.Chart(region_totals)\n    .mark_text(\n        align=\"center\", baseline=\"top\", dy=10, lineHeight=15, lineBreak=\"\\n\", fontSize=12, fontWeight=\"bold\", color=INK\n    )\n    .encode(x=alt.X(\"x_mid:Q\", scale=alt.Scale(domain=[0, 100])), y=alt.value(320), text=\"Label:N\")\n)\n\nchart = (\n    alt.layer(segments, top_outline, revenue_labels, region_labels)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(\"marimekko-basic · python · altair · anyplot.ai\", fontSize=16, anchor=\"middle\", color=INK),\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\n# PAD-only to canonical target (do NOT crop — cropping clips title/axis labels)\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}x{_h}, exceeds target {TW}x{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}