{"spec_id":"line-timeseries-rolling","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nline-timeseries-rolling: Time Series with Rolling Average Overlay\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-13\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nimport plotnine as pn\nfrom mizani.breaks import breaks_date\nfrom mizani.labels import label_date\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# Okabe-Ito palette\nIMPRINT = [\"#009E73\", \"#C475FD\"]  # brand green, vermillion\n\n# Data - Daily temperature readings with 7-day rolling average\nnp.random.seed(42)\n\n# Generate 180 days of temperature data (6 months)\ndates = pd.date_range(\"2024-01-01\", periods=180, freq=\"D\")\n\n# Create seasonal temperature pattern with noise\n# Base seasonal pattern: winter -> spring -> summer\nday_of_year = np.arange(180)\nseasonal = 5 + 15 * np.sin(2 * np.pi * (day_of_year - 30) / 365)\nnoise = np.random.normal(0, 3, 180)\ntemperature = seasonal + noise\n\n# Create DataFrame and calculate rolling average\ndf = pd.DataFrame({\"date\": dates, \"temperature\": temperature})\ndf[\"rolling_avg\"] = df[\"temperature\"].rolling(window=7, center=True).mean()\n\n# Reshape data for plotnine - need long format for multiple series\ndf_raw = df[[\"date\", \"temperature\"]].copy()\ndf_raw[\"series\"] = \"Daily Temperature\"\ndf_raw = df_raw.rename(columns={\"temperature\": \"value\"})\n\ndf_roll = df[[\"date\", \"rolling_avg\"]].dropna().copy()\ndf_roll[\"series\"] = \"7-Day Rolling Average\"\ndf_roll = df_roll.rename(columns={\"rolling_avg\": \"value\"})\n\ndf_long = pd.concat([df_raw, df_roll], ignore_index=True)\n\n# Make series categorical for consistent ordering\ndf_long[\"series\"] = pd.Categorical(\n    df_long[\"series\"], categories=[\"Daily Temperature\", \"7-Day Rolling Average\"], ordered=True\n)\n\n# Plot\nplot = (\n    pn.ggplot(df_long, pn.aes(x=\"date\", y=\"value\", color=\"series\", alpha=\"series\", size=\"series\"))\n    + pn.geom_line()\n    + pn.scale_color_manual(values={\"Daily Temperature\": IMPRINT[0], \"7-Day Rolling Average\": IMPRINT[1]})\n    + pn.scale_alpha_manual(values={\"Daily Temperature\": 0.5, \"7-Day Rolling Average\": 1.0})\n    + pn.scale_size_manual(values={\"Daily Temperature\": 0.8, \"7-Day Rolling Average\": 2.0})\n    + pn.guides(alpha=\"none\", size=\"none\")\n    + pn.scale_x_datetime(breaks=breaks_date(14), labels=label_date(\"%b %Y\"))\n    + pn.labs(x=\"Date\", y=\"Temperature (°C)\", title=\"line-timeseries-rolling · plotnine · anyplot.ai\", color=\"\")\n    + pn.theme_minimal()\n    + pn.theme(\n        figure_size=(16, 9),\n        plot_background=pn.element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=pn.element_rect(fill=PAGE_BG),\n        panel_grid_major=pn.element_line(color=INK, size=0.3, alpha=0.10),\n        panel_grid_minor=pn.element_line(color=INK, size=0.2, alpha=0.05),\n        panel_border=pn.element_rect(color=INK_SOFT, fill=None),\n        axis_title=pn.element_text(size=20, color=INK),\n        axis_text=pn.element_text(size=16, color=INK_SOFT),\n        axis_text_x=pn.element_text(angle=30, hjust=1),\n        axis_line=pn.element_line(color=INK_SOFT),\n        plot_title=pn.element_text(size=24, color=INK),\n        legend_background=pn.element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=pn.element_text(size=16, color=INK_SOFT),\n        legend_title=pn.element_text(size=0),\n        legend_position=\"right\",\n        text=pn.element_text(size=14),\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}