{"spec_id":"polar-bar","library":"altair","language":"python","code":"\"\"\" anyplot.ai\npolar-bar: Polar Bar Chart (Wind Rose)\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-13\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\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\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data - Wind direction frequency data\nnp.random.seed(42)\ndirections = [\"N\", \"NE\", \"E\", \"SE\", \"S\", \"SW\", \"W\", \"NW\"]\nangles = [0, 45, 90, 135, 180, 225, 270, 315]\n\n# Simulate realistic wind frequency with prevailing westerlies\nbase_freq = [12, 8, 10, 6, 9, 18, 22, 16]\nfrequencies = [f + np.random.randint(-2, 3) for f in base_freq]\n\n# Create DataFrame with angle in radians for Altair\ndf = pd.DataFrame({\"direction\": directions, \"angle\": angles, \"frequency\": frequencies})\n\n# Calculate start and end angles for each bar (in radians)\n# Altair uses radians, and we need to center each bar on its direction\nbar_width = 40  # degrees\ndf[\"theta_start\"] = np.radians(df[\"angle\"] - bar_width / 2)\ndf[\"theta_end\"] = np.radians(df[\"angle\"] + bar_width / 2)\n\n# Create the polar bar chart using mark_arc\nbase = alt.Chart(df)\n\n# Bars radiating from center using arc marks\nbars = base.mark_arc(stroke=INK_SOFT, strokeWidth=1.5).encode(\n    theta=alt.Theta(\"theta_start:Q\", scale=alt.Scale(domain=[0, 2 * np.pi])),\n    theta2=\"theta_end:Q\",\n    radius=alt.Radius(\"frequency:Q\", scale=alt.Scale(type=\"linear\", zero=True, rangeMin=0)),\n    color=alt.Color(\n        \"direction:N\",\n        scale=alt.Scale(range=IMPRINT),\n        legend=alt.Legend(\n            title=\"Direction\",\n            titleFontSize=18,\n            labelFontSize=16,\n            orient=\"right\",\n            fillColor=ELEVATED_BG,\n            strokeColor=INK_SOFT,\n        ),\n    ),\n    tooltip=[alt.Tooltip(\"direction:N\", title=\"Direction\"), alt.Tooltip(\"frequency:Q\", title=\"Frequency (days)\")],\n)\n\n# Add direction labels around the perimeter\nmax_freq = max(frequencies) * 1.25\nlabel_df = pd.DataFrame({\"direction\": directions, \"angle_rad\": np.radians(angles), \"radius\": [max_freq] * 8})\n\n# Calculate x, y positions for labels\nlabel_df[\"x\"] = label_df[\"radius\"] * np.sin(label_df[\"angle_rad\"])\nlabel_df[\"y\"] = label_df[\"radius\"] * np.cos(label_df[\"angle_rad\"])\n\nlabels = (\n    alt.Chart(label_df)\n    .mark_text(fontSize=20, fontWeight=\"bold\", color=INK_SOFT)\n    .encode(x=alt.X(\"x:Q\", axis=None), y=alt.Y(\"y:Q\", axis=None), text=\"direction:N\")\n)\n\n# Combine bars and labels\nchart = (\n    alt.layer(bars, labels)\n    .properties(\n        width=900,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(text=\"polar-bar · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", color=INK),\n    )\n    .configure_view(strokeWidth=0, fill=PAGE_BG)\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    .interactive()\n)\n\n# Save outputs\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}