{"spec_id":"sparkline-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nsparkline-basic: Basic Sparkline\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-06-16\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_rect,\n    element_text,\n    facet_wrap,\n    geom_line,\n    geom_point,\n    geom_ribbon,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    scale_color_manual,\n    theme,\n    theme_void,\n)\n\n\nLetsPlot.setup_html()\n\n# Theme-adaptive chrome (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\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\"\n\n# Imprint palette — brand green is the sparkline line; red/blue mark the extremes\nBRAND = \"#009E73\"  # Imprint position 1 — the trend line (always first series)\nLOW = \"#AE3030\"  # Imprint matte red — minimum (semantic low)\nHIGH = \"#4467A3\"  # Imprint blue — maximum\n\n# Data — a small-multiples KPI dashboard: six product metrics over 45 days.\n# Each metric gets its own trend shape so the sparklines tell distinct stories.\nnp.random.seed(42)\nn_days = 45\ndays = np.arange(n_days)\n\nseries = {\n    \"Monthly Revenue ($K)\": 120 + np.cumsum(np.random.randn(n_days) * 2.0 + 0.7),\n    \"Active Users (K)\": 48 + np.cumsum(np.random.randn(n_days) * 1.4 + 0.35),\n    \"Conversion Rate (%)\": 3.1 + np.cumsum(np.random.randn(n_days) * 0.12),\n    \"Avg Session (min)\": 9.0 + np.cumsum(np.random.randn(n_days) * 0.18 - 0.02),\n    \"Churn Rate (%)\": 5.4 - np.cumsum(np.random.randn(n_days) * 0.05 + 0.018),\n    \"NPS Score\": 31 + np.cumsum(np.random.randn(n_days) * 0.7 + 0.45),\n}\norder = list(series.keys())\n\n# Long-format frame plus per-metric extreme/endpoint frames for the highlight dots\nframes, mins, maxs, lasts = [], [], [], []\nfor name, vals in series.items():\n    i_min, i_max = int(np.argmin(vals)), int(np.argmax(vals))\n    frames.append(pd.DataFrame({\"metric\": name, \"day\": days, \"value\": vals, \"floor\": vals.min()}))\n    mins.append({\"metric\": name, \"day\": i_min, \"value\": vals[i_min]})\n    maxs.append({\"metric\": name, \"day\": i_max, \"value\": vals[i_max]})\n    lasts.append({\"metric\": name, \"day\": n_days - 1, \"value\": vals[-1]})\n\ndf = pd.concat(frames, ignore_index=True)\ndf[\"metric\"] = pd.Categorical(df[\"metric\"], categories=order, ordered=True)\n\n# One tidy frame of highlight dots, with a \"kind\" column that drives the legend key.\nkinds = [\"minimum\", \"maximum\", \"latest\"]\ndots = pd.concat(\n    [\n        pd.DataFrame(mins).assign(kind=\"minimum\"),\n        pd.DataFrame(maxs).assign(kind=\"maximum\"),\n        pd.DataFrame(lasts).assign(kind=\"latest\"),\n    ],\n    ignore_index=True,\n)\ndots[\"metric\"] = pd.Categorical(dots[\"metric\"], categories=order, ordered=True)\ndots[\"kind\"] = pd.Categorical(dots[\"kind\"], categories=kinds, ordered=True)\n\n# Plot — pure sparklines: no axes, ticks, or gridlines; each panel free on y.\n# Subtle area anchored to each panel's floor, thin line, and red/blue/green dots.\nplot = (\n    ggplot(df, aes(\"day\", \"value\"))\n    + geom_ribbon(aes(ymin=\"floor\", ymax=\"value\"), fill=BRAND, alpha=0.10, size=0)\n    + geom_line(color=BRAND, size=1.3)\n    # A single mapped point layer so min/max/latest get a real legend key.\n    + geom_point(data=dots, mapping=aes(color=\"kind\"), size=4.2)\n    + scale_color_manual(name=\"\", values={\"minimum\": LOW, \"maximum\": HIGH, \"latest\": BRAND})\n    + facet_wrap(\"metric\", ncol=3, scales=\"free_y\")\n    + labs(title=\"sparkline-basic · python · letsplot · anyplot.ai\")\n    + ggsize(800, 450)  # scale=4 on export -> 3200 x 1800 px (landscape)\n    + theme_void()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        plot_title=element_text(size=16, color=INK, hjust=0.5),\n        strip_text=element_text(size=13, color=INK_SOFT, hjust=0),\n        strip_background=element_blank(),  # drop the bordered strip frame (cleaner sparkline chrome)\n        legend_position=\"bottom\",  # compact key: red=min, blue=max, green=latest\n        legend_text=element_text(size=12, color=INK_SOFT),\n        legend_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        legend_key=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        plot_margin=[24, 28, 24, 28],\n    )\n)\n\n# Save PNG (scale 4x -> 3200 x 1800) and interactive HTML\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}