|
1 | 1 | """ pyplots.ai |
2 | 2 | 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 |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import matplotlib.pyplot as plt |
|
10 | 10 | import seaborn as sns |
11 | 11 |
|
12 | 12 |
|
13 | | -# Data |
| 13 | +# Data — World cities: GDP per capita vs life expectancy, bubble = population |
14 | 14 | np.random.seed(42) |
15 | | -n_points = 50 |
| 15 | +n_cities = 40 |
16 | 16 |
|
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) |
20 | 19 |
|
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"} |
22 | 44 |
|
23 | 45 | # Plot |
24 | 46 | fig, ax = plt.subplots(figsize=(16, 9)) |
25 | 47 |
|
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, |
49 | 62 | ) |
50 | 63 |
|
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) |
55 | 68 | 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) |
57 | 85 |
|
58 | 86 | plt.tight_layout() |
59 | 87 | plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
0 commit comments