{"spec_id":"rose-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nrose-basic: Basic Rose Chart\nLibrary: plotnine 0.15.7 | Python 3.13.14\nQuality: 90/100 | Updated: 2026-07-25\n\"\"\"\n\nimport math\nimport os\nimport sys\n\n\n# Remove this file's own directory from sys.path so that \"plotnine\" resolves\n# to the installed library rather than this file (Python 3.13 naming-collision fix).\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.realpath(p) != os.path.realpath(_script_dir)]\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_rect,\n    element_text,\n    geom_path,\n    geom_polygon,\n    geom_text,\n    ggplot,\n    labs,\n    scale_alpha_continuous,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n)\n\n\n# Theme tokens (Imprint palette)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nBRAND = \"#009E73\"  # Imprint palette position 1 — always first series\n\n# Data - Monthly rainfall (mm) for a temperate climate\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nrainfall = [78, 62, 55, 48, 52, 68, 82, 85, 72, 88, 95, 82]\n\nn = len(months)\n\n# Create wedge polygons for each month. Radius is proportional to rainfall\n# (not area), and fill alpha also scales with rainfall so a single Imprint\n# hue (rainfall -> water -> brand green) carries the value redundantly\n# without a second, competing colormap.\nn_arc_points = 30\nwedge_rows = []\nfor i, (month, value) in enumerate(zip(months, rainfall, strict=True)):\n    start_angle = math.pi / 2 - (i * 2 * math.pi / n)\n    end_angle = math.pi / 2 - ((i + 1) * 2 * math.pi / n)\n    gap = 0.02\n    start_angle += gap\n    end_angle -= gap\n    wedge_rows.append({\"x\": 0, \"y\": 0, \"month\": month, \"rainfall\": value, \"order\": 0})\n    for j, angle in enumerate(np.linspace(start_angle, end_angle, n_arc_points)):\n        wedge_rows.append(\n            {\n                \"x\": value * math.cos(angle),\n                \"y\": value * math.sin(angle),\n                \"month\": month,\n                \"rainfall\": value,\n                \"order\": j + 1,\n            }\n        )\n    wedge_rows.append({\"x\": 0, \"y\": 0, \"month\": month, \"rainfall\": value, \"order\": n_arc_points + 1})\n\ndf = pd.DataFrame(wedge_rows)\ndf[\"month\"] = pd.Categorical(df[\"month\"], categories=months, ordered=True)\n\n# Radial gridlines (circles)\ngrid_rows = []\ngrid_angles = np.linspace(0, 2 * math.pi, 101)\nfor radius in [20, 40, 60, 80, 100]:\n    for angle in grid_angles:\n        grid_rows.append({\"x\": radius * math.cos(angle), \"y\": radius * math.sin(angle), \"radius\": radius})\ngrid_df = pd.DataFrame(grid_rows)\n\n# Spoke lines from center to edge\nspoke_rows = []\nfor i in range(n):\n    angle = math.pi / 2 - (i * 2 * math.pi / n)\n    spoke_rows.append({\"x\": 0, \"y\": 0, \"spoke_id\": i})\n    spoke_rows.append({\"x\": 105 * math.cos(angle), \"y\": 105 * math.sin(angle), \"spoke_id\": i})\nspoke_df = pd.DataFrame(spoke_rows)\n\n# Month labels positioned outside the chart\nlabel_rows = []\nfor i, month in enumerate(months):\n    center_angle = math.pi / 2 - ((i + 0.5) * 2 * math.pi / n)\n    label_rows.append({\"label\": month, \"x\": 122 * math.cos(center_angle), \"y\": 122 * math.sin(center_angle)})\nlabel_df = pd.DataFrame(label_rows)\n\n# Value labels positioned along the top-right spoke\nvalue_label_rows = [{\"label\": str(r), \"x\": 6, \"y\": r + 4} for r in [20, 40, 60, 80, 100]]\nvalue_label_df = pd.DataFrame(value_label_rows)\n\n# Plot\nplot = (\n    ggplot()\n    + geom_path(\n        aes(x=\"x\", y=\"y\", group=\"radius\"), data=grid_df, color=INK_SOFT, size=0.4, alpha=0.15, linetype=\"dashed\"\n    )\n    + geom_path(aes(x=\"x\", y=\"y\", group=\"spoke_id\"), data=spoke_df, color=INK_SOFT, size=0.3, alpha=0.12)\n    + geom_polygon(aes(x=\"x\", y=\"y\", alpha=\"rainfall\", group=\"month\"), data=df, fill=BRAND, color=PAGE_BG, size=0.3)\n    + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=label_df, size=16, fontweight=\"bold\", color=INK)\n    + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=value_label_df, size=10, color=INK_MUTED)\n    + scale_alpha_continuous(range=(0.35, 1.0), guide=None)\n    + scale_x_continuous(limits=(-144, 144))\n    + scale_y_continuous(limits=(-144, 144))\n    + labs(title=\"Monthly Rainfall (mm) · rose-basic · python · plotnine · anyplot.ai\")\n    + theme(\n        figure_size=(6, 6),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        plot_title=element_text(size=10, ha=\"center\", color=INK),\n        axis_title=element_blank(),\n        axis_text=element_blank(),\n        axis_ticks=element_blank(),\n        axis_line=element_blank(),\n        panel_grid_major=element_blank(),\n        panel_grid_minor=element_blank(),\n        panel_border=element_blank(),\n        legend_position=\"none\",\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=6, height=6, units=\"in\")\n"}