Skip to content

Commit f1968b3

Browse files
feat(letsplot): implement line-timeseries-rolling (#2827)
## Implementation: `line-timeseries-rolling` - letsplot Implements the **letsplot** version of `line-timeseries-rolling`. **File:** `plots/line-timeseries-rolling/implementations/letsplot.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20602454238)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 345b4fd commit f1968b3

2 files changed

Lines changed: 96 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
""" pyplots.ai
2+
line-timeseries-rolling: Time Series with Rolling Average Overlay
3+
Library: letsplot 4.8.2 | Python 3.13.11
4+
Quality: 92/100 | Created: 2025-12-30
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from lets_plot import *
10+
11+
12+
LetsPlot.setup_html()
13+
14+
# Data: Simulated daily sensor temperature readings
15+
np.random.seed(42)
16+
n_days = 180
17+
dates = pd.date_range("2024-01-01", periods=n_days, freq="D")
18+
19+
# Generate realistic temperature data with seasonal trend and noise
20+
base_temp = 15 # Base temperature in Celsius
21+
seasonal = 10 * np.sin(2 * np.pi * np.arange(n_days) / 365) # Seasonal variation
22+
noise = np.random.normal(0, 3, n_days) # Daily fluctuations
23+
trend = np.linspace(0, 2, n_days) # Slight warming trend
24+
values = base_temp + seasonal + noise + trend
25+
26+
# Calculate 14-day rolling average
27+
rolling_window = 14
28+
rolling_avg = pd.Series(values).rolling(window=rolling_window, center=False).mean()
29+
30+
# Create DataFrame for plotting
31+
df = pd.DataFrame({"date": dates, "value": values, "rolling_avg": rolling_avg})
32+
33+
# Reshape data for lets-plot (long format for legend)
34+
df_raw = df[["date", "value"]].copy()
35+
df_raw["series"] = "Raw Data"
36+
df_raw = df_raw.rename(columns={"value": "temp"})
37+
38+
df_rolling = df[["date", "rolling_avg"]].dropna().copy()
39+
df_rolling["series"] = f"{rolling_window}-Day Rolling Avg"
40+
df_rolling = df_rolling.rename(columns={"rolling_avg": "temp"})
41+
42+
df_long = pd.concat([df_raw, df_rolling], ignore_index=True)
43+
44+
# Plot
45+
plot = (
46+
ggplot(df_long, aes(x="date", y="temp", color="series"))
47+
+ geom_line(aes(alpha="series", size="series"))
48+
+ labs(x="Date", y="Temperature (°C)", title="line-timeseries-rolling · letsplot · pyplots.ai")
49+
+ scale_color_manual(name="", values=["#306998", "#FFD43B"])
50+
+ scale_alpha_manual(name="", values=[0.5, 1.0])
51+
+ scale_size_manual(name="", values=[0.8, 2.5])
52+
+ theme_minimal()
53+
+ theme(
54+
plot_title=element_text(size=24),
55+
axis_title=element_text(size=20),
56+
axis_text=element_text(size=16),
57+
legend_text=element_text(size=16),
58+
legend_position="top",
59+
panel_grid_major=element_line(color="#CCCCCC", size=0.5),
60+
)
61+
+ ggsize(1600, 900)
62+
)
63+
64+
# Save PNG (scale 3x for 4800 × 2700 px)
65+
ggsave(plot, "plot.png", path=".", scale=3)
66+
67+
# Save HTML for interactive version
68+
ggsave(plot, "plot.html", path=".")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: letsplot
2+
specification_id: line-timeseries-rolling
3+
created: '2025-12-30T17:50:31Z'
4+
updated: '2025-12-30T18:03:52Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20602454238
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 4.8.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/line-timeseries-rolling/letsplot/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-timeseries-rolling/letsplot/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/line-timeseries-rolling/letsplot/plot.html
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- Excellent dual-layer visualization clearly distinguishing raw data from smoothed
17+
trend
18+
- Appropriate use of alpha/size differentiation to show raw data as secondary and
19+
rolling average as primary
20+
- Realistic temperature sensor scenario with seasonal variation matches specification
21+
perfectly
22+
- Clean ggplot2-style grammar of graphics implementation
23+
- Proper legend placement at top with clear labeling including window size
24+
weaknesses:
25+
- Legend appears as three separate legend groups (color, alpha, size) instead of
26+
a unified legend - consider using guides() to merge them
27+
- Raw data line could be slightly thicker for better visibility while maintaining
28+
transparency

0 commit comments

Comments
 (0)