{"spec_id":"climograph-walter-lieth","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nclimograph-walter-lieth: Walter-Lieth Climate Diagram\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 88/100 | Created: 2026-06-15\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent the script directory from shadowing the seaborn package on sys.path\n_this_dir = os.path.abspath(os.path.dirname(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _this_dir]\n\nimport matplotlib.lines as mlines\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\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# Imprint palette — semantic assignments\n# Temperature → matte red (Imprint slot 5, semantic: heat)\n# Precipitation → blue (Imprint slot 3, semantic: water/sky)\nTEMP_COLOR = \"#AE3030\"\nPRECIP_COLOR = \"#4467A3\"\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Station: Pyrenean highland — demonstrates all Walter-Lieth features:\n# frost months (Jan/Feb/Dec), summer arid period (Jun/Jul/Aug), perhumid autumn (Oct)\nSTATION = \"Montsec Ridge\"\nELEVATION = 1240\nPERIOD = \"1991–2020\"\n\nMONTHS = [\"J\", \"F\", \"M\", \"A\", \"M\", \"J\", \"J\", \"A\", \"S\", \"O\", \"N\", \"D\"]\nTEMPERATURE = np.array([-4.0, -2.0, 4.0, 10.0, 15.0, 22.0, 27.0, 26.0, 20.0, 12.0, 4.0, -2.0])\nPRECIP = np.array([30, 25, 55, 75, 65, 20, 8, 12, 85, 120, 90, 40])\n\nT_MEAN = round(TEMPERATURE.mean(), 1)\nP_TOTAL = int(PRECIP.sum())\n\n# Walter-Lieth dual-scale: 10°C = 20 mm below 100 mm (2:1); above 100 mm compressed 10:1\n# P ≤ 100 mm → precip_t = P / 2\n# P > 100 mm → precip_t = 50 + (P − 100) / 10\nPERHUMID_THRESHOLD = 100.0\nPERHUMID_Y = PERHUMID_THRESHOLD / 2.0  # = 50.0 on temp axis\n\nprecip_t = np.where(PRECIP <= PERHUMID_THRESHOLD, PRECIP / 2.0, PERHUMID_Y + (PRECIP - PERHUMID_THRESHOLD) / 10.0)\nx = np.arange(12)\n\nY_MIN = -10\nY_MAX = max(TEMPERATURE.max(), precip_t.max()) + 6  # ~58\n\n# Figure (landscape 3200×1800)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Theme-adaptive fill alphas: arid fill is nearly invisible on dark near-black surface\nARID_ALPHA = 0.45 if THEME == \"dark\" else 0.22\nHUMID_ALPHA = 0.40 if THEME == \"dark\" else 0.26\n\n# Humid fill (blue): where precip_t >= temperature, capped at perhumid threshold\nax.fill_between(\n    x,\n    TEMPERATURE,\n    np.minimum(precip_t, PERHUMID_Y),\n    where=precip_t >= TEMPERATURE,\n    color=PRECIP_COLOR,\n    alpha=HUMID_ALPHA,\n    interpolate=True,\n    zorder=1,\n)\n\n# Perhumid fill (solid blue): P > 100 mm — solid to mark \"very wet\" zone above 100 mm\nperhumid_mask = PRECIP > PERHUMID_THRESHOLD\nax.fill_between(\n    x, PERHUMID_Y, precip_t, where=perhumid_mask, color=PRECIP_COLOR, alpha=0.90, interpolate=True, zorder=1\n)\n\n# Arid fill (red): where temperature exceeds precipitation curve\nax.fill_between(\n    x,\n    TEMPERATURE,\n    precip_t,\n    where=TEMPERATURE >= precip_t,\n    color=TEMP_COLOR,\n    alpha=ARID_ALPHA,\n    interpolate=True,\n    zorder=1,\n)\n\n# Frost month indicators: solid blue bar from Y_MIN to 0 for months with mean T < 0°C\nfor i, t in enumerate(TEMPERATURE):\n    if t < 0:\n        ax.fill_between([i - 0.45, i + 0.45], [Y_MIN, Y_MIN], [0, 0], color=PRECIP_COLOR, alpha=0.50, zorder=2)\n\n# Temperature and precipitation lines via seaborn\nsns.lineplot(x=x, y=TEMPERATURE, ax=ax, color=TEMP_COLOR, linewidth=2.5, zorder=3)\nsns.lineplot(x=x, y=precip_t, ax=ax, color=PRECIP_COLOR, linewidth=2.5, zorder=3)\n\n# Perhumid threshold line — marks scale change at 100 mm\nax.axhline(PERHUMID_Y, color=PRECIP_COLOR, linewidth=0.9, linestyle=\"--\", alpha=0.55, zorder=2)\n\n# Zero-degree reference line\nax.axhline(0, color=INK_SOFT, linewidth=0.8, linestyle=\"--\", alpha=0.5, zorder=1)\n\n# Left (temperature) axis styling\nax.set_ylim(Y_MIN, Y_MAX)\nax.set_xlim(-0.5, 11.5)\nax.set_xticks(x)\nax.set_xticklabels(MONTHS, fontsize=8)\nax.set_yticks([0, 10, 20, 30])\nax.tick_params(axis=\"y\", colors=TEMP_COLOR, labelsize=8)\nax.set_ylabel(\"Temperature (°C)\", fontsize=10, color=TEMP_COLOR, labelpad=6)\nax.spines[\"left\"].set_color(TEMP_COLOR)\nax.spines[\"bottom\"].set_color(INK_SOFT)\nax.yaxis.grid(True, alpha=0.12, linewidth=0.8, color=INK, zorder=0)\n\n# Remove top and right spines — seaborn-idiomatic\nsns.despine(ax=ax, right=True, top=True)\n\n# Right precipitation axis with dual-scale tick labels (0, 20, 40, 60, 100, 200 mm)\nax2 = ax.twinx()\nax2.set_ylim(Y_MIN, Y_MAX)\n\ntick_mm = np.array([0.0, 20.0, 40.0, 60.0, 100.0, 200.0])\ntick_pos = np.where(tick_mm <= PERHUMID_THRESHOLD, tick_mm / 2.0, PERHUMID_Y + (tick_mm - PERHUMID_THRESHOLD) / 10.0)\nax2.set_yticks(tick_pos)\nax2.set_yticklabels([str(int(p)) for p in tick_mm])\nax2.tick_params(axis=\"y\", labelsize=8, colors=PRECIP_COLOR)\nax2.set_ylabel(\"Precipitation (mm)\", fontsize=10, color=PRECIP_COLOR, labelpad=8)\nax2.spines[\"right\"].set_color(PRECIP_COLOR)\nax2.spines[\"top\"].set_visible(False)\nax2.spines[\"bottom\"].set_visible(False)\nax2.spines[\"left\"].set_visible(False)\n\n# Legend with proxy artists\nleg = ax.legend(\n    handles=[\n        mlines.Line2D([], [], color=TEMP_COLOR, linewidth=2.5, label=\"Temperature\"),\n        mlines.Line2D([], [], color=PRECIP_COLOR, linewidth=2.5, label=\"Precipitation\"),\n        mpatches.Patch(facecolor=PRECIP_COLOR, alpha=0.40, label=\"Humid period\"),\n        mpatches.Patch(facecolor=PRECIP_COLOR, alpha=0.90, label=\"Perhumid >100 mm\"),\n        mpatches.Patch(facecolor=TEMP_COLOR, alpha=0.40, label=\"Arid period\"),\n    ],\n    fontsize=7.5,\n    loc=\"upper left\",\n    bbox_to_anchor=(0.01, 0.83),\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n    framealpha=0.9,\n)\nfor text in leg.get_texts():\n    text.set_color(INK)\n\n# Station header\nheader = f\"{STATION}  ·  {ELEVATION} m  ·  {PERIOD}    T = {T_MEAN} °C  ·  P = {P_TOTAL} mm\"\nax.text(\n    0.5,\n    0.97,\n    header,\n    transform=ax.transAxes,\n    ha=\"center\",\n    va=\"top\",\n    fontsize=8.5,\n    color=INK,\n    bbox={\"boxstyle\": \"round,pad=0.35\", \"facecolor\": ELEVATED_BG, \"edgecolor\": INK_SOFT, \"alpha\": 0.9},\n)\n\n# Title\ntitle = \"climograph-walter-lieth · python · seaborn · anyplot.ai\"\nn = len(title)\nratio = 67 / n if n > 67 else 1.0\ntitle_fontsize = max(8, round(12 * ratio))\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK, pad=10)\n\n# Layout and save\nfig.subplots_adjust(left=0.09, right=0.88, bottom=0.09, top=0.84)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\nplt.close()\n"}