Skip to content

Commit 9c97479

Browse files
update(bubble-basic): seaborn — comprehensive quality review and improvement (#4262)
## Summary Updated **seaborn** implementation for **bubble-basic**. **Changes:** comprehensive quality review and improvement ### Changes - Replaced generic x/y data with realistic, domain-relevant dataset - Improved visual design: white bubble edges, subtler grid, better alpha - Area-based bubble scaling per spec requirement - Meaningful axis labels with units - Enhanced library-specific feature usage - Quality self-assessment: see agent report ## Test Plan - [x] Preview images uploaded to GCS staging - [x] Implementation file passes ruff format/check - [x] Metadata YAML updated with current versions - [ ] Automated review triggered --- Generated with [Claude Code](https://claude.com/claude-code) `/update` command --------- 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 b88dedc commit 9c97479

2 files changed

Lines changed: 197 additions & 146 deletions

File tree

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" pyplots.ai
22
bubble-basic: Basic Bubble Chart
3-
Library: seaborn 0.13.2 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: seaborn 0.13.2 | Python 3.14.3
4+
Quality: 91/100 | Updated: 2026-02-15
55
"""
66

77
import matplotlib.pyplot as plt
@@ -10,50 +10,78 @@
1010
import seaborn as sns
1111

1212

13-
# Data
13+
# Data — World cities: GDP per capita vs life expectancy, bubble = population
1414
np.random.seed(42)
15-
n_points = 50
15+
n_cities = 40
1616

17-
x = np.random.randn(n_points) * 15 + 50
18-
y = x * 0.6 + np.random.randn(n_points) * 10 + 20
19-
size_values = np.random.rand(n_points) * 80 + 20 # Range 20-100
17+
gdp_per_capita = np.linspace(5, 85, n_cities) + np.random.normal(0, 5, n_cities)
18+
gdp_per_capita = np.clip(gdp_per_capita, 5, 90)
2019

21-
df = pd.DataFrame({"x": x, "y": y, "size": size_values})
20+
life_expectancy = 60 + 0.22 * gdp_per_capita + np.random.normal(0, 2.5, n_cities)
21+
life_expectancy = np.clip(life_expectancy, 58, 84)
22+
23+
population_millions = np.random.lognormal(mean=1.0, sigma=1.0, size=n_cities)
24+
population_millions = np.clip(population_millions, 0.8, 30)
25+
26+
# Assign regions based on GDP tiers for color grouping (4th dimension)
27+
region = np.where(
28+
gdp_per_capita < 25,
29+
"Emerging",
30+
np.where(gdp_per_capita < 50, "Developing", np.where(gdp_per_capita < 70, "Advanced", "Elite")),
31+
)
32+
33+
df = pd.DataFrame(
34+
{
35+
"GDP per Capita ($ thousands)": np.round(gdp_per_capita, 1),
36+
"Life Expectancy (years)": np.round(life_expectancy, 1),
37+
"Population (M)": np.round(population_millions, 1),
38+
"Economic Tier": region,
39+
}
40+
)
41+
42+
# Palette — Python Blue anchored, colorblind-safe progression
43+
palette = {"Emerging": "#c44e52", "Developing": "#dd8452", "Advanced": "#55a868", "Elite": "#306998"}
2244

2345
# Plot
2446
fig, ax = plt.subplots(figsize=(16, 9))
2547

26-
# Scale sizes for visibility at 4800x2700 - map to area range 100-2000
27-
size_scaled = (df["size"] - df["size"].min()) / (df["size"].max() - df["size"].min())
28-
sizes = size_scaled * 1900 + 100
29-
30-
sns.scatterplot(data=df, x="x", y="y", size=sizes, sizes=(100, 2000), alpha=0.6, color="#306998", legend=False, ax=ax)
31-
32-
# Size legend - create proxy bubbles for legend
33-
legend_sizes = [20, 50, 80]
34-
legend_markers = []
35-
for s in legend_sizes:
36-
scaled = (s - df["size"].min()) / (df["size"].max() - df["size"].min())
37-
marker_size = scaled * 1900 + 100
38-
marker = ax.scatter([], [], s=marker_size, c="#306998", alpha=0.6)
39-
legend_markers.append(marker)
40-
41-
ax.legend(
42-
legend_markers,
43-
[f"Size: {s}" for s in legend_sizes],
44-
title="Bubble Size",
45-
loc="upper left",
46-
fontsize=14,
47-
title_fontsize=16,
48-
framealpha=0.9,
48+
sns.scatterplot(
49+
data=df,
50+
x="GDP per Capita ($ thousands)",
51+
y="Life Expectancy (years)",
52+
size="Population (M)",
53+
hue="Economic Tier",
54+
hue_order=["Emerging", "Developing", "Advanced", "Elite"],
55+
sizes=(150, 2200),
56+
alpha=0.65,
57+
palette=palette,
58+
edgecolor="white",
59+
linewidth=1.0,
60+
legend="brief",
61+
ax=ax,
4962
)
5063

51-
# Labels and styling
52-
ax.set_xlabel("X Value", fontsize=20)
53-
ax.set_ylabel("Y Value", fontsize=20)
54-
ax.set_title("bubble-basic · seaborn · pyplots.ai", fontsize=24)
64+
# Style
65+
ax.set_xlabel("GDP per Capita ($ thousands)", fontsize=20)
66+
ax.set_ylabel("Life Expectancy (years)", fontsize=20)
67+
ax.set_title("bubble-basic · seaborn · pyplots.ai", fontsize=24, fontweight="medium", pad=16)
5568
ax.tick_params(axis="both", labelsize=16)
56-
ax.grid(True, alpha=0.3, linestyle="--")
69+
ax.spines["top"].set_visible(False)
70+
ax.spines["right"].set_visible(False)
71+
ax.yaxis.grid(True, alpha=0.15, linewidth=0.8)
72+
ax.xaxis.grid(True, alpha=0.08, linewidth=0.5)
73+
74+
# Refine legend — place in lower-right to avoid data overlap
75+
legend = ax.get_legend()
76+
legend.set_title("Economic Tier / Population (M)", prop={"size": 14})
77+
legend.get_title().set_fontweight("semibold")
78+
for text in legend.get_texts():
79+
text.set_fontsize(13)
80+
legend.set_frame_on(True)
81+
legend.get_frame().set_alpha(0.92)
82+
legend.get_frame().set_edgecolor("#cccccc")
83+
legend.get_frame().set_facecolor("white")
84+
sns.move_legend(ax, "lower right", frameon=True)
5785

5886
plt.tight_layout()
5987
plt.savefig("plot.png", dpi=300, bbox_inches="tight")

0 commit comments

Comments
 (0)