{"spec_id":"heatmap-polar","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nheatmap-polar: Polar Heatmap for Cyclic Two-Dimensional Data\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 90/100 | Created: 2026-05-13\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent the local altair.py from shadowing the installed library\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nif _script_dir in sys.path:\n    sys.path.remove(_script_dir)\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\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# Data — monthly energy consumption (kWh) across five years\nnp.random.seed(42)\n\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nyears = [2019, 2020, 2021, 2022, 2023]\nn_months = len(months)\nn_years = len(years)\n\nrows = []\nfor yi, year in enumerate(years):\n    for mi, month in enumerate(months):\n        winter = 75 * np.exp(-min(mi**2, (12 - mi) ** 2) / 3.5)\n        summer = 45 * np.exp(-((mi - 6.5) ** 2) / 5.0)\n        trend = yi * 9\n        noise = np.random.normal(0, 10)\n        value = round(max(80, 155 + winter + summer + trend + noise), 1)\n\n        rows.append(\n            {\n                \"month\": month,\n                \"year\": str(year),\n                \"value\": value,\n                \"theta\": mi * 2 * np.pi / n_months,\n                \"theta2\": (mi + 1) * 2 * np.pi / n_months,\n                \"r_inner\": 100 + yi * 85,\n                \"r_outer\": 100 + (yi + 1) * 85,\n            }\n        )\n\ndf = pd.DataFrame(rows)\n\n# Month label positions (just outside outermost ring at r=525)\nmonth_label_df = pd.DataFrame(\n    [\n        {\"label\": month, \"theta_mid\": (mi + 0.5) * 2 * np.pi / n_months, \"r_label\": 555}\n        for mi, month in enumerate(months)\n    ]\n)\n\n# Year label positions (near 11 o'clock, in the gap between rings)\nyear_label_df = pd.DataFrame(\n    [\n        {\"label\": str(year), \"theta_mid\": 2 * np.pi - np.pi / 6, \"r_label\": 100 + (yi + 0.5) * 85}\n        for yi, year in enumerate(years)\n    ]\n)\n\n# Arc heatmap cells\narc = (\n    alt.Chart(df)\n    .mark_arc(stroke=PAGE_BG, strokeWidth=0.8)\n    .encode(\n        theta=alt.Theta(\"theta:Q\", scale=alt.Scale(type=\"identity\")),\n        theta2=alt.Theta2(\"theta2:Q\"),\n        radius=alt.Radius(\"r_outer:Q\", scale=alt.Scale(type=\"identity\")),\n        radius2=alt.Radius2(\"r_inner:Q\"),\n        color=alt.Color(\n            \"value:Q\",\n            scale=alt.Scale(scheme=\"viridis\"),\n            legend=alt.Legend(\n                title=\"kWh / month\",\n                titleFontSize=18,\n                labelFontSize=16,\n                fillColor=ELEVATED_BG,\n                labelColor=INK_SOFT,\n                titleColor=INK,\n                strokeColor=INK_SOFT,\n                orient=\"right\",\n                offset=20,\n            ),\n        ),\n        tooltip=[\n            alt.Tooltip(\"month:N\", title=\"Month\"),\n            alt.Tooltip(\"year:N\", title=\"Year\"),\n            alt.Tooltip(\"value:Q\", title=\"kWh\", format=\".0f\"),\n        ],\n    )\n)\n\n# Month labels around the perimeter\nmonth_labels = (\n    alt.Chart(month_label_df)\n    .mark_text(fontSize=18, fontWeight=\"bold\", align=\"center\", baseline=\"middle\", color=INK_SOFT)\n    .encode(\n        theta=alt.Theta(\"theta_mid:Q\", scale=alt.Scale(type=\"identity\")),\n        radius=alt.Radius(\"r_label:Q\", scale=alt.Scale(type=\"identity\")),\n        text=\"label:N\",\n    )\n)\n\n# Year labels near 11 o'clock, one per ring\nyear_labels = (\n    alt.Chart(year_label_df)\n    .mark_text(fontSize=16, align=\"center\", baseline=\"middle\", color=INK_SOFT)\n    .encode(\n        theta=alt.Theta(\"theta_mid:Q\", scale=alt.Scale(type=\"identity\")),\n        radius=alt.Radius(\"r_label:Q\", scale=alt.Scale(type=\"identity\")),\n        text=\"label:N\",\n    )\n)\n\nchart = (\n    (arc + month_labels + year_labels)\n    .properties(\n        width=1200,\n        height=1200,\n        background=PAGE_BG,\n        title=alt.Title(\"heatmap-polar · altair · anyplot.ai\", fontSize=28, color=INK, anchor=\"middle\"),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n)\n\n# Save\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}