Skip to content

Commit 8f1bd97

Browse files
feat(altair): implement scatter-animated-controls (#3081)
## Implementation: `scatter-animated-controls` - altair Implements the **altair** version of `scatter-animated-controls`. **File:** `plots/scatter-animated-controls/implementations/altair.py` **Parent Issue:** #3067 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20620303747)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent e3d7fdc commit 8f1bd97

2 files changed

Lines changed: 156 additions & 0 deletions

File tree

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
""" pyplots.ai
2+
scatter-animated-controls: Animated Scatter Plot with Play Controls
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import altair as alt
8+
import numpy as np
9+
import pandas as pd
10+
11+
12+
# Data: Simulated country development metrics over years
13+
np.random.seed(42)
14+
15+
# Create 12 countries tracked over 20 years (showing 4 key time points)
16+
countries = [
17+
"Country A",
18+
"Country B",
19+
"Country C",
20+
"Country D",
21+
"Country E",
22+
"Country F",
23+
"Country G",
24+
"Country H",
25+
"Country I",
26+
"Country J",
27+
"Country K",
28+
"Country L",
29+
]
30+
years = [2000, 2007, 2014, 2021] # Key time points for faceted view
31+
32+
# Base values for each country (GDP per capita in thousands, life expectancy)
33+
base_gdp = np.array([5, 8, 12, 15, 20, 25, 3, 10, 18, 30, 6, 22])
34+
base_life = np.array([55, 60, 65, 70, 72, 75, 50, 62, 68, 78, 58, 74])
35+
population = np.array([50, 80, 120, 45, 200, 30, 150, 90, 60, 25, 180, 40]) # In millions
36+
37+
# Regions for color coding
38+
regions = [
39+
"Region 1",
40+
"Region 2",
41+
"Region 1",
42+
"Region 2",
43+
"Region 3",
44+
"Region 3",
45+
"Region 1",
46+
"Region 2",
47+
"Region 3",
48+
"Region 3",
49+
"Region 1",
50+
"Region 2",
51+
]
52+
53+
data = []
54+
for i, country in enumerate(countries):
55+
for j, year in enumerate(years):
56+
# Simulate growth over time with some variation
57+
growth_factor = 1 + j * 0.15 + np.random.uniform(-0.05, 0.1)
58+
life_improvement = j * 2.5 + np.random.uniform(-1, 2)
59+
60+
gdp = base_gdp[i] * growth_factor
61+
life_exp = min(85, base_life[i] + life_improvement)
62+
pop = population[i] * (1 + j * 0.02) # Slight population growth
63+
64+
data.append(
65+
{
66+
"Country": country,
67+
"Year": year,
68+
"GDP per Capita (thousands USD)": round(gdp, 1),
69+
"Life Expectancy (years)": round(life_exp, 1),
70+
"Population (millions)": round(pop, 1),
71+
"Region": regions[i],
72+
}
73+
)
74+
75+
df = pd.DataFrame(data)
76+
77+
# Color palette
78+
color_scale = alt.Scale(domain=["Region 1", "Region 2", "Region 3"], range=["#306998", "#FFD43B", "#6B8E23"])
79+
80+
# Create faceted scatter plot showing evolution across key years
81+
chart = (
82+
alt.Chart(df)
83+
.mark_circle(opacity=0.8, stroke="#333333", strokeWidth=1)
84+
.encode(
85+
x=alt.X(
86+
"GDP per Capita (thousands USD):Q", title="GDP per Capita (thousands USD)", scale=alt.Scale(domain=[0, 50])
87+
),
88+
y=alt.Y("Life Expectancy (years):Q", title="Life Expectancy (years)", scale=alt.Scale(domain=[45, 90])),
89+
size=alt.Size(
90+
"Population (millions):Q",
91+
scale=alt.Scale(range=[100, 2000]),
92+
legend=alt.Legend(title="Population (millions)", titleFontSize=16, labelFontSize=14),
93+
),
94+
color=alt.Color(
95+
"Region:N", scale=color_scale, legend=alt.Legend(title="Region", titleFontSize=16, labelFontSize=14)
96+
),
97+
tooltip=[
98+
"Country",
99+
"Year",
100+
"GDP per Capita (thousands USD)",
101+
"Life Expectancy (years)",
102+
"Population (millions)",
103+
"Region",
104+
],
105+
)
106+
.properties(width=350, height=550)
107+
.facet(
108+
column=alt.Column(
109+
"Year:O", header=alt.Header(title=None, labelFontSize=24, labelFontWeight="bold", labelColor="#333333")
110+
)
111+
)
112+
.properties(
113+
title=alt.Title(
114+
"scatter-animated-controls · altair · pyplots.ai",
115+
fontSize=28,
116+
anchor="middle",
117+
subtitle="Country Development Metrics Across Key Years (Static Faceted View)",
118+
subtitleFontSize=18,
119+
subtitleColor="#666666",
120+
)
121+
)
122+
.configure_axis(labelFontSize=14, titleFontSize=18, gridColor="#E0E0E0", gridOpacity=0.5)
123+
.configure_view(strokeWidth=0)
124+
.configure_legend(orient="right", padding=20)
125+
)
126+
127+
# Save outputs
128+
chart.save("plot.png", scale_factor=3.0)
129+
chart.save("plot.html")
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library: altair
2+
specification_id: scatter-animated-controls
3+
created: '2025-12-31T13:51:00Z'
4+
updated: '2025-12-31T13:54:38Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20620303747
7+
issue: 3067
8+
python_version: 3.13.11
9+
library_version: 6.0.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/altair/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/altair/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/altair/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent adaptation of animated concept to static faceted view, following spec
17+
guidance for libraries without animation
18+
- Clean Gapminder-inspired data with realistic development metrics across multiple
19+
decades
20+
- Good use of Altair declarative grammar with proper encoding types (Q, N, O)
21+
- Effective use of size encoding for population and color for regions
22+
- Proper scale domains ensure consistent axes across all facets for easy comparison
23+
weaknesses:
24+
- Year labels at top of facets could be slightly larger for emphasis
25+
- Legend placement creates slight right-margin crowding; could benefit from more
26+
padding
27+
- Missing interactive() call for HTML output which would enable zoom/pan exploration

0 commit comments

Comments
 (0)