{"spec_id":"span-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nspan-basic: Basic Span Plot (Highlighted Region)\nLibrary: letsplot 4.11.0 | Python 3.13.14\nQuality: 93/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    geom_rect,\n    geom_text,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    layer_tooltips,\n    scale_color_manual,\n    scale_fill_manual,\n    scale_x_continuous,\n    theme,\n    theme_minimal,\n)\n\n\nLetsPlot.setup_html()\n\n# Theme tokens (see prompts/default-style-guide.md \"Background\" + \"Theme-adaptive Chrome\")\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\"\nBRAND = \"#009E73\"  # Imprint palette position 1 — ALWAYS first series\nANYPLOT_AMBER = \"#DDCC77\"  # warning / caution semantic anchor — recession (vertical span)\nSTIMULUS_BLUE = \"#4467A3\"  # Imprint palette position 3 — policy response (vertical span)\nTARGET_TEAL = \"#2ABCCD\"  # Imprint palette position 6 — acceptable-value threshold (horizontal span)\n\n# Data - simulated economic indicator over time (2006-2011)\nnp.random.seed(42)\nmonths = pd.date_range(\"2006-01\", periods=72, freq=\"ME\")\n# Economic cycle: growth -> recession dip -> recovery\nbase = np.linspace(105, 90, 24).tolist() + np.linspace(90, 75, 18).tolist() + np.linspace(75, 115, 30).tolist()\nnoise = np.random.randn(72) * 2.5\nvalues = np.array(base) + noise\n\ndf = pd.DataFrame({\"date\": months, \"index\": values})\ndf[\"date_num\"] = np.arange(len(df))\n\n# Year labels for x-axis\nyear_positions = [0, 12, 24, 36, 48, 60]\nyear_labels = [\"2006\", \"2007\", \"2008\", \"2009\", \"2010\", \"2011\"]\n\nx_start, x_end = df[\"date_num\"].min(), df[\"date_num\"].max()\ny_min, y_max = df[\"index\"].min() - 8, df[\"index\"].max() + 8\n\n# Vertical spans - time periods of interest (recession, then policy response)\nvertical_spans = pd.DataFrame(\n    {\n        \"xmin\": [24, 42],\n        \"xmax\": [42, 54],\n        \"ymin\": [y_min, y_min],\n        \"ymax\": [y_max, y_max],\n        \"label\": [\"Recession Period\", \"Stimulus Response\"],\n        \"range_desc\": [\"Jan 2008 - Jun 2009\", \"Jul 2009 - Jun 2010\"],\n    }\n)\n\n# Horizontal span - acceptable value-range threshold across the full period\nhorizontal_span = pd.DataFrame(\n    {\n        \"xmin\": [x_start],\n        \"xmax\": [x_end],\n        \"ymin\": [95],\n        \"ymax\": [105],\n        \"label\": [\"Target Range\"],\n        \"range_desc\": [\"95 - 105 index pts\"],\n    }\n)\n\nspans = pd.concat([horizontal_span, vertical_spans], ignore_index=True)\nspan_colors = {\"Recession Period\": ANYPLOT_AMBER, \"Stimulus Response\": STIMULUS_BLUE, \"Target Range\": TARGET_TEAL}\nspan_order = list(span_colors.keys())\n\n# Label positions for span annotations\nspan_label = pd.DataFrame(\n    {\n        \"x\": [33, 48, x_end - 1],\n        \"y\": [df[\"index\"].max() + 4, df[\"index\"].max() + 4, 106.5],\n        \"text\": [\"Recession\\n2008-2009\", \"Stimulus\\nResponse\", \"Target Range\"],\n        \"hjust\": [0.5, 0.5, 1.0],\n    }\n)\n\n# Theme-adaptive chrome (see prompts/library/letsplot.md \"Theme-adaptive Chrome\")\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_border=element_blank(),\n    panel_grid_major_x=element_blank(),\n    panel_grid_minor_x=element_blank(),\n    panel_grid_major_y=element_line(color=INK, size=0.3),\n    panel_grid_minor_y=element_blank(),\n    axis_title=element_text(color=INK, size=12),\n    axis_text=element_text(color=INK_SOFT, size=10),\n    axis_line=element_line(color=INK_SOFT),\n    plot_title=element_text(color=INK, size=16, face=\"bold\"),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=10),\n    legend_title=element_text(color=INK, size=10, face=\"bold\"),\n)\n\n# Plot\nplot = (\n    ggplot()\n    # Highlighted spans: one horizontal (value threshold) + two vertical (time periods),\n    # each with its own tooltip surfaced in the interactive HTML export\n    + geom_rect(\n        data=spans,\n        mapping=aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\", fill=\"label\", color=\"label\"),\n        alpha=0.22,\n        tooltips=layer_tooltips().title(\"@label\").line(\"@range_desc\"),\n    )\n    # Economic indicator line\n    + geom_line(\n        data=df,\n        mapping=aes(x=\"date_num\", y=\"index\"),\n        color=BRAND,\n        size=1.8,\n        tooltips=layer_tooltips().line(\"Index|@index\"),\n    )\n    # Span label annotations\n    + geom_text(\n        data=span_label, mapping=aes(x=\"x\", y=\"y\", label=\"text\", hjust=\"hjust\"), size=4, color=INK, tooltips=\"none\"\n    )\n    + labs(x=\"Year\", y=\"Economic Index\", title=\"span-basic · python · letsplot · anyplot.ai\")\n    + scale_fill_manual(values=list(span_colors.values()), limits=span_order, name=\"Highlighted Region\")\n    + scale_color_manual(values=list(span_colors.values()), limits=span_order, guide=\"none\")\n    + scale_x_continuous(breaks=year_positions, labels=year_labels)\n    + theme_minimal()\n    + anyplot_theme\n    + ggsize(800, 450)\n)\n\n# Save PNG (scale 4x gives 3200 x 1800 px) + HTML\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}