Skip to content

Commit c69922a

Browse files
feat(altair): implement line-timeseries-rolling (#2819)
## Implementation: `line-timeseries-rolling` - altair Implements the **altair** version of `line-timeseries-rolling`. **File:** `plots/line-timeseries-rolling/implementations/altair.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20602450785)* --------- 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 18eb737 commit c69922a

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
""" pyplots.ai
2+
line-timeseries-rolling: Time Series with Rolling Average Overlay
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 93/100 | Created: 2025-12-30
5+
"""
6+
7+
import altair as alt
8+
import numpy as np
9+
import pandas as pd
10+
11+
12+
# Data - Daily temperature readings over 6 months with 7-day rolling average
13+
np.random.seed(42)
14+
dates = pd.date_range("2024-01-01", periods=180, freq="D")
15+
16+
# Simulate temperature with seasonal trend + noise (winter to early summer)
17+
day_of_year = np.arange(180)
18+
# Temperature rises from winter (Jan) toward summer (Jun)
19+
seasonal_trend = 10 + 12 * np.sin(2 * np.pi * (day_of_year - 90) / 365)
20+
noise = np.random.normal(0, 3, 180)
21+
raw_values = seasonal_trend + noise
22+
23+
df = pd.DataFrame({"date": dates, "value": raw_values})
24+
25+
# Calculate 7-day rolling average
26+
df["rolling_avg"] = df["value"].rolling(window=7, center=True).mean()
27+
28+
# Prepare data for layered chart with proper legend
29+
df_long = pd.melt(
30+
df, id_vars=["date"], value_vars=["value", "rolling_avg"], var_name="series", value_name="temperature"
31+
)
32+
33+
# Rename series for cleaner legend
34+
df_long["series"] = df_long["series"].map({"value": "Raw Data", "rolling_avg": "7-Day Rolling Average"})
35+
36+
# Remove NaN values from rolling average
37+
df_long = df_long.dropna()
38+
39+
# Create chart with both lines
40+
chart = (
41+
alt.Chart(df_long)
42+
.mark_line()
43+
.encode(
44+
x=alt.X("date:T", title="Date", axis=alt.Axis(labelFontSize=18, titleFontSize=22, format="%b %d")),
45+
y=alt.Y(
46+
"temperature:Q",
47+
title="Temperature (°C)",
48+
axis=alt.Axis(labelFontSize=18, titleFontSize=22),
49+
scale=alt.Scale(zero=False),
50+
),
51+
color=alt.Color(
52+
"series:N",
53+
scale=alt.Scale(domain=["Raw Data", "7-Day Rolling Average"], range=["#306998", "#FFD43B"]),
54+
legend=alt.Legend(title="Series", labelFontSize=16, titleFontSize=18, orient="top-right", offset=10),
55+
),
56+
strokeWidth=alt.condition(alt.datum.series == "7-Day Rolling Average", alt.value(4), alt.value(1.5)),
57+
opacity=alt.condition(alt.datum.series == "7-Day Rolling Average", alt.value(1.0), alt.value(0.5)),
58+
)
59+
.properties(
60+
width=1600,
61+
height=900,
62+
title=alt.Title("line-timeseries-rolling · altair · pyplots.ai", fontSize=28, anchor="middle"),
63+
)
64+
.configure_axis(gridOpacity=0.3, gridDash=[4, 4])
65+
.configure_view(strokeWidth=0)
66+
)
67+
68+
# Save
69+
chart.save("plot.png", scale_factor=3.0)
70+
chart.save("plot.html")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library: altair
2+
specification_id: line-timeseries-rolling
3+
created: '2025-12-30T17:49:27Z'
4+
updated: '2025-12-30T17:59:24Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20602450785
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 6.0.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/line-timeseries-rolling/altair/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-timeseries-rolling/altair/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/line-timeseries-rolling/altair/plot.html
13+
quality_score: 93
14+
review:
15+
strengths:
16+
- Excellent use of Altair declarative grammar with conditional encoding for strokeWidth
17+
and opacity
18+
- Clean data transformation using pd.melt for tidy/long format suitable for Altair
19+
- 'Well-chosen color scheme (#306998 blue, #FFD43B yellow) that is colorblind-safe'
20+
- Realistic temperature data scenario with clear seasonal trend
21+
- Proper use of centered rolling window (center=True) for accurate moving average
22+
alignment
23+
weaknesses:
24+
- Legend placement overlaps slightly with data points in the upper-right corner;
25+
could use orient=bottom or place outside the plot area

0 commit comments

Comments
 (0)