{"spec_id":"pyramid-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\npyramid-basic: Basic Pyramid Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-04-29\n\"\"\"\n\nimport importlib\nimport os\nimport sys\n\n\n# Drop script directory from sys.path so the `altair` package resolves, not this file\nsys.path[:] = [p for p in sys.path if os.path.abspath(p or \".\") != os.path.dirname(os.path.abspath(__file__))]\nalt = importlib.import_module(\"altair\")\npd = importlib.import_module(\"pandas\")\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\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data — population pyramid with pronounced gender gap in older age groups\nage_groups = [\"0-9\", \"10-19\", \"20-29\", \"30-39\", \"40-49\", \"50-59\", \"60-69\", \"70-79\", \"80+\"]\nmale = [4.8, 5.2, 6.1, 7.3, 8.5, 7.8, 5.9, 3.2, 1.2]\nfemale = [4.5, 5.0, 6.3, 7.5, 8.7, 8.2, 6.8, 4.8, 2.5]  # more females in older groups\n\ndf = pd.DataFrame(\n    {\n        \"Age Group\": age_groups * 2,\n        \"Population\": [-v for v in male] + female,\n        \"Gender\": [\"Male\"] * len(age_groups) + [\"Female\"] * len(age_groups),\n        \"Absolute\": male + female,\n    }\n)\n\nage_order = list(reversed(age_groups))\n\n# Interactive highlight — click legend to isolate a gender\nselection = alt.selection_point(fields=[\"Gender\"], bind=\"legend\")\n\n# Plot\nchart = (\n    alt.Chart(df)\n    .mark_bar(cornerRadiusEnd=3)\n    .encode(\n        y=alt.Y(\"Age Group:N\", sort=age_order, axis=alt.Axis(title=\"Age Group\", titleFontSize=22, labelFontSize=18)),\n        x=alt.X(\n            \"Population:Q\",\n            axis=alt.Axis(\n                title=\"Population (millions)\",\n                titleFontSize=22,\n                labelFontSize=18,\n                values=[-8, -6, -4, -2, 0, 2, 4, 6, 8],\n                labelExpr=\"abs(datum.value)\",\n            ),\n            scale=alt.Scale(domain=[-10, 10]),\n        ),\n        color=alt.Color(\n            \"Gender:N\",\n            scale=alt.Scale(domain=[\"Male\", \"Female\"], range=[IMPRINT[0], IMPRINT[1]]),\n            legend=alt.Legend(title=\"Gender\", titleFontSize=20, labelFontSize=18, orient=\"bottom\"),\n        ),\n        opacity=alt.condition(selection, alt.value(0.9), alt.value(0.25)),\n        tooltip=[\n            alt.Tooltip(\"Gender:N\"),\n            alt.Tooltip(\"Age Group:N\"),\n            alt.Tooltip(\"Absolute:Q\", title=\"Population (M)\", format=\".1f\"),\n        ],\n    )\n    .add_params(selection)\n    .properties(\n        width=1600,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(text=\"pyramid-basic · altair · anyplot.ai\", fontSize=28, anchor=\"middle\"),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_title(color=INK)\n    .configure_legend(\n        fillColor=ELEVATED_BG,\n        strokeColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        titleFontSize=20,\n        labelFontSize=18,\n    )\n)\n\n# Save\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}