Skip to content

Commit 9d4a987

Browse files
feat(plotnine): implement scatter-animated-controls (#3084)
## Implementation: `scatter-animated-controls` - plotnine Implements the **plotnine** version of `scatter-animated-controls`. **File:** `plots/scatter-animated-controls/implementations/plotnine.py` **Parent Issue:** #3067 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20620303821)* --------- 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 d578000 commit 9d4a987

2 files changed

Lines changed: 146 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
""" pyplots.ai
2+
scatter-animated-controls: Animated Scatter Plot with Play Controls
3+
Library: plotnine 0.15.2 | Python 3.13.11
4+
Quality: 90/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from plotnine import (
10+
aes,
11+
element_blank,
12+
element_line,
13+
element_rect,
14+
element_text,
15+
facet_wrap,
16+
geom_point,
17+
ggplot,
18+
labs,
19+
scale_color_brewer,
20+
scale_size_continuous,
21+
scale_x_continuous,
22+
scale_y_continuous,
23+
theme,
24+
theme_minimal,
25+
)
26+
27+
28+
# Data - Simulated country metrics over time (Gapminder-style)
29+
np.random.seed(42)
30+
31+
countries = ["Nordland", "Australis", "Eastoria", "Pacifica", "Centralia", "Westmark"]
32+
regions = ["Europe", "Americas", "Asia", "Asia", "Africa", "Europe"]
33+
years = list(range(2000, 2021, 2)) # 11 time points: 2000-2020 every 2 years
34+
n_countries = len(countries)
35+
n_years = len(years)
36+
37+
# Generate base values for each country with distinct trajectories
38+
base_gdp = np.array([25000, 12000, 8000, 3000, 1500, 35000])
39+
base_life_exp = np.array([76, 72, 65, 58, 52, 79])
40+
base_population = np.array([10, 45, 200, 130, 90, 25]) # in millions
41+
42+
data = []
43+
for i, country in enumerate(countries):
44+
for j, year in enumerate(years):
45+
# Different growth patterns per region
46+
if regions[i] == "Asia":
47+
gdp_growth = 1 + 0.08 * j + np.random.uniform(-0.02, 0.02) # Fast growth
48+
elif regions[i] == "Africa":
49+
gdp_growth = 1 + 0.04 * j + np.random.uniform(-0.03, 0.03) # Moderate with volatility
50+
else:
51+
gdp_growth = 1 + 0.025 * j + np.random.uniform(-0.01, 0.01) # Steady growth
52+
53+
gdp = base_gdp[i] * gdp_growth
54+
55+
# Life expectancy increases at different rates
56+
life_gain = 0.4 if regions[i] == "Africa" else 0.25
57+
life_exp = base_life_exp[i] + life_gain * j + np.random.uniform(-0.3, 0.3)
58+
59+
# Population growth varies
60+
pop_rate = 0.025 if regions[i] in ["Asia", "Africa"] else 0.008
61+
pop_growth = 1 + pop_rate * j + np.random.uniform(-0.005, 0.005)
62+
population = base_population[i] * pop_growth
63+
64+
data.append(
65+
{
66+
"country": country,
67+
"region": regions[i],
68+
"year": year,
69+
"gdp_per_capita": gdp,
70+
"life_expectancy": life_exp,
71+
"population": population,
72+
}
73+
)
74+
75+
df = pd.DataFrame(data)
76+
77+
# Create faceted scatter plot showing key time points
78+
# Select 6 key time points for facets (fills 2x3 grid perfectly)
79+
key_years = [2000, 2004, 2008, 2012, 2016, 2020]
80+
df_faceted = df[df["year"].isin(key_years)].copy()
81+
82+
plot = (
83+
ggplot(df_faceted, aes(x="gdp_per_capita", y="life_expectancy", color="region", size="population"))
84+
+ geom_point(alpha=0.85)
85+
+ facet_wrap("~year", ncol=3)
86+
+ labs(
87+
x="GDP per Capita (USD)",
88+
y="Life Expectancy (years)",
89+
title="scatter-animated-controls · plotnine · pyplots.ai",
90+
color="Region",
91+
size="Population (M)",
92+
)
93+
+ scale_color_brewer(type="qual", palette="Dark2")
94+
+ scale_size_continuous(range=(4, 18), breaks=[25, 100, 200])
95+
+ scale_x_continuous(breaks=[0, 30000, 60000], labels=["0", "30k", "60k"], limits=(-2000, 65000))
96+
+ scale_y_continuous(breaks=[55, 65, 75, 85], limits=(50, 88))
97+
+ theme_minimal()
98+
+ theme(
99+
figure_size=(16, 9),
100+
text=element_text(size=12),
101+
axis_title=element_text(size=18),
102+
axis_text=element_text(size=10),
103+
axis_text_x=element_text(size=9),
104+
plot_title=element_text(size=22, ha="center", weight="bold"),
105+
legend_text=element_text(size=12),
106+
legend_title=element_text(size=14, weight="bold"),
107+
legend_position="right",
108+
legend_box_spacing=0.4,
109+
strip_text=element_text(size=16, weight="bold"),
110+
strip_background=element_rect(fill="#e8e8e8", color="#cccccc", size=0.5),
111+
panel_spacing_x=0.15,
112+
panel_spacing_y=0.15,
113+
panel_grid_major=element_line(color="#dddddd", size=0.4),
114+
panel_grid_minor=element_blank(),
115+
panel_background=element_rect(fill="#fafafa"),
116+
)
117+
)
118+
119+
# Save plot
120+
plot.save("plot.png", dpi=300, verbose=False)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library: plotnine
2+
specification_id: scatter-animated-controls
3+
created: '2025-12-31T13:52:08Z'
4+
updated: '2025-12-31T14:13:12Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20620303821
7+
issue: 3067
8+
python_version: 3.13.11
9+
library_version: 0.15.2
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/plotnine/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/plotnine/plot_thumb.png
12+
preview_html: null
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Excellent Gapminder-style data scenario with realistic country trajectories showing
17+
different regional growth patterns
18+
- Clean faceted layout that effectively communicates temporal evolution without
19+
animation
20+
- Good use of Dark2 colorblind-safe palette with appropriate alpha for point visibility
21+
- Well-structured code following KISS principles with comprehensive theme customization
22+
- Proper use of scale_size_continuous with meaningful breaks for population encoding
23+
weaknesses:
24+
- Size legend shows three solid circles that appear similar in rendering - may not
25+
clearly show size differentiation
26+
- X-axis tick labels could be slightly larger for better readability at full resolution

0 commit comments

Comments
 (0)