{"spec_id":"climograph-walter-lieth","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nclimograph-walter-lieth: Walter-Lieth Climate Diagram\nLibrary: plotnine 0.15.7 | Python 3.13.13\nQuality: 86/100 | Created: 2026-06-15\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove this script's own directory from sys.path so that\n# `from plotnine import ...` resolves to the installed package, not this file.\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [\n    p for p in sys.path if os.path.normpath(os.path.abspath(p) if p else os.getcwd()) != os.path.normpath(_script_dir)\n]\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    annotate,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_line,\n    geom_ribbon,\n    geom_segment,\n    geom_text,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n)\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — semantic override: temperature=red (heat), precipitation=blue (water)\nTEMP_COLOR = \"#AE3030\"\nPRECIP_COLOR = \"#4467A3\"\n\n# Fill alpha — higher in dark mode to keep fills visible on near-black background\nFILL_ALPHA = 0.35 if THEME == \"light\" else 0.60\n\n# Station: Athens, Greece — Mediterranean 1991–2020 climate normals\nmonths_labels = [\"J\", \"F\", \"M\", \"A\", \"M\", \"J\", \"J\", \"A\", \"S\", \"O\", \"N\", \"D\"]\nmonth_num = np.arange(1, 13)\ntemperature = np.array([9.9, 10.9, 13.2, 17.2, 22.3, 27.0, 29.5, 29.4, 24.7, 19.7, 15.0, 11.6])\nprecipitation = np.array([57, 37, 38, 24, 17, 8, 6, 7, 15, 52, 58, 71])\n\ntemp_annual = round(float(np.mean(temperature)), 1)\nprecip_annual = int(np.sum(precipitation))\n\n# Walter-Lieth scaling: 10 °C ≡ 20 mm → divide precip by 2 to plot on temperature axis\nprecip_scaled = precipitation / 2.0\n\n# Humid ribbon (blue): regions where precip_scaled ≥ temperature\nhumid_ymin = temperature.astype(float).copy()\nhumid_ymax = np.maximum(temperature, precip_scaled)\nmask_arid = temperature > precip_scaled\nhumid_ymin[mask_arid] = temperature[mask_arid]\nhumid_ymax[mask_arid] = temperature[mask_arid]\n\n# Arid ribbon (red): regions where temperature > precip_scaled\narid_ymin = np.minimum(temperature, precip_scaled).astype(float)\narid_ymax = temperature.astype(float).copy()\nmask_humid = precip_scaled >= temperature\narid_ymin[mask_humid] = temperature[mask_humid]\narid_ymax[mask_humid] = temperature[mask_humid]\n\ndf = pd.DataFrame(\n    {\n        \"month\": month_num,\n        \"temperature\": temperature,\n        \"precip_sc\": precip_scaled,\n        \"hum_lo\": humid_ymin,\n        \"hum_hi\": humid_ymax,\n        \"ari_lo\": arid_ymin,\n        \"ari_hi\": arid_ymax,\n    }\n)\n\n# Long-format dataframe for the two curves — enables a proper legend via color aesthetic\nTEMP_LABEL = \"Temperature (°C)\"\nPRECIP_LABEL = \"Precipitation (mm)\"\ndf_lines = pd.DataFrame(\n    {\n        \"month\": np.tile(month_num, 2),\n        \"value\": np.concatenate([temperature, precip_scaled]),\n        \"variable\": [TEMP_LABEL] * 12 + [PRECIP_LABEL] * 12,\n    }\n)\n\n# Y and X axis limits — extend Y_MAX to 50 to accommodate the 100 mm right-axis tick\n# (Walter-Lieth convention: 100 mm ≡ 50 °C on the temperature scale)\nY_MIN, Y_MAX = -5, 50\nX_MAX_EXTENDED = 14.5\n\n# Right-side precipitation axis: spec-mandated ticks 0/20/40/60/100 mm\n# In temperature units: 0/10/20/30/50 °C\np_y_ticks = [0, 10, 20, 30, 50]\np_mm_labels = [\"0\", \"20\", \"40\", \"60\", \"100\"]\n\nprec_ticks_df = pd.DataFrame(\n    {\"x\": 12.65, \"xend\": 12.95, \"y\": p_y_ticks, \"yend\": p_y_ticks, \"lx\": 13.15, \"label\": p_mm_labels}\n)\nprec_axis_line_df = pd.DataFrame({\"x\": [12.65], \"xend\": [12.65], \"y\": [0], \"yend\": [50]})\n\n# Title — 67 chars → default size 12\nplot_title = \"Athens · climograph-walter-lieth · python · plotnine · anyplot.ai\"\ntitle_len = len(plot_title)\ntitle_size = max(8, round(12 * 67 / title_len))\n\nsubtitle = f\"Athens, Greece  ·  107 m a.s.l.  ·  T̅ = {temp_annual} °C  ·  ΣP = {precip_annual} mm  ·  1991–2020\"\n\nanyplot_theme = theme(\n    figure_size=(8, 4.5),\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major_y=element_line(color=INK_SOFT, size=0.3, alpha=0.18),\n    panel_grid_major_x=element_blank(),\n    panel_grid_minor=element_blank(),\n    panel_border=element_blank(),\n    axis_title=element_text(size=10, color=INK),\n    axis_text=element_text(size=8, color=INK_SOFT),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    axis_ticks=element_line(color=INK_SOFT, size=0.3),\n    plot_title=element_text(size=title_size, color=INK, face=\"bold\"),\n    plot_subtitle=element_text(size=8, color=INK_SOFT),\n    legend_position=\"bottom\",\n    legend_background=element_rect(fill=PAGE_BG, color=\"none\"),\n    legend_key=element_rect(fill=PAGE_BG),\n    legend_text=element_text(size=8, color=INK_SOFT),\n    legend_title=element_blank(),\n)\n\nplot = (\n    ggplot(df, aes(x=\"month\"))\n    # Humid fill (blue — wet periods where precip > temp curve)\n    + geom_ribbon(aes(ymin=\"hum_lo\", ymax=\"hum_hi\"), fill=PRECIP_COLOR, alpha=FILL_ALPHA)\n    # Arid fill (red — dry periods where temp > precip curve)\n    + geom_ribbon(aes(ymin=\"ari_lo\", ymax=\"ari_hi\"), fill=TEMP_COLOR, alpha=FILL_ALPHA)\n    # 0 °C frost reference line\n    + geom_hline(yintercept=0, color=INK_SOFT, size=0.5, linetype=\"dashed\", alpha=0.55)\n    # Temperature and precipitation curves — color mapped to variable for legend\n    + geom_line(data=df_lines, mapping=aes(x=\"month\", y=\"value\", group=\"variable\", color=\"variable\"), size=1.1)\n    + scale_color_manual(values={TEMP_LABEL: TEMP_COLOR, PRECIP_LABEL: PRECIP_COLOR}, name=\"\")\n    # Right-side precipitation axis: axis line\n    + geom_segment(\n        data=prec_axis_line_df, mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), color=INK_SOFT, size=0.5\n    )\n    # Right-side precipitation axis: tick marks\n    + geom_segment(data=prec_ticks_df, mapping=aes(x=\"x\", xend=\"xend\", y=\"y\", yend=\"yend\"), color=INK_SOFT, size=0.3)\n    # Right-side precipitation axis: tick labels (mm)\n    + geom_text(\n        data=prec_ticks_df, mapping=aes(x=\"lx\", y=\"y\", label=\"label\"), size=3.2, color=INK_SOFT, ha=\"left\", va=\"center\"\n    )\n    # Right-side precipitation axis: axis title (rotated)\n    + annotate(\"text\", x=14.1, y=25, label=\"Precipitation (mm)\", angle=90, size=4, color=INK, ha=\"center\", va=\"bottom\")\n    + scale_x_continuous(breaks=month_num.tolist(), labels=months_labels, limits=(0.5, X_MAX_EXTENDED), expand=(0, 0))\n    + scale_y_continuous(name=\"Temperature (°C)\", breaks=[0, 10, 20, 30, 40], limits=(Y_MIN, Y_MAX))\n    + labs(title=plot_title, subtitle=subtitle, x=\"Month\")\n    + anyplot_theme\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\")\n"}