|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | facet-grid: Faceted Grid Plot |
3 | | -Library: matplotlib 3.10.8 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-30 |
| 3 | +Library: matplotlib 3.10.9 | Python 3.13.13 |
| 4 | +Quality: 96/100 | Updated: 2026-05-13 |
5 | 5 | """ |
6 | 6 |
|
7 | | -import matplotlib.pyplot as plt |
8 | | -import numpy as np |
9 | | -import pandas as pd |
| 7 | +import os |
| 8 | +import sys |
10 | 9 |
|
11 | 10 |
|
| 11 | +# Remove the script's directory from sys.path to avoid shadowing matplotlib package |
| 12 | +script_dir = os.path.dirname(os.path.abspath(__file__)) |
| 13 | +if script_dir in sys.path: |
| 14 | + sys.path.remove(script_dir) |
| 15 | + |
| 16 | +import matplotlib.pyplot as plt # noqa: E402 |
| 17 | +import numpy as np # noqa: E402 |
| 18 | +import pandas as pd # noqa: E402 |
| 19 | +from matplotlib import gridspec # noqa: E402 |
| 20 | +from scipy.stats import linregress # noqa: E402 |
| 21 | + |
| 22 | + |
| 23 | +# Theme tokens |
| 24 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 25 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 26 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 27 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 28 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 29 | +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" |
| 30 | + |
| 31 | +# Okabe-Ito palette - first series is always #009E73 |
| 32 | +OKABE_ITO = [ |
| 33 | + "#009E73", # brand green |
| 34 | + "#D55E00", # vermillion |
| 35 | + "#0072B2", # blue |
| 36 | + "#CC79A7", # reddish purple |
| 37 | +] |
| 38 | + |
12 | 39 | # Data |
13 | 40 | np.random.seed(42) |
14 | 41 |
|
15 | | -# Create sample data with two categorical variables for faceting |
16 | 42 | regions = ["North", "South", "East"] |
17 | 43 | seasons = ["Spring", "Summer", "Fall", "Winter"] |
18 | 44 |
|
19 | 45 | data = [] |
20 | 46 | for region in regions: |
21 | 47 | for season in seasons: |
22 | 48 | n_points = 25 |
23 | | - # Generate temperature vs energy consumption relationship |
24 | | - # Different patterns per region/season combination |
25 | | - base_temp = {"North": 10, "South": 25, "East": 18}[region] |
26 | | - season_offset = {"Spring": 5, "Summer": 15, "Fall": 0, "Winter": -10}[season] |
| 49 | + # Base temperature varies by region and season |
| 50 | + base_temps = {"North": 5, "South": 20, "East": 15} |
| 51 | + season_offsets = {"Spring": 5, "Summer": 10, "Fall": 2, "Winter": -8} |
27 | 52 |
|
28 | | - temp = np.random.normal(base_temp + season_offset, 5, n_points) |
29 | | - # Energy consumption varies with temperature |
30 | | - energy = 100 + (temp - 15) ** 2 * 0.3 + np.random.normal(0, 10, n_points) |
| 53 | + base_temp = base_temps[region] + season_offsets[season] |
| 54 | + temp = np.random.normal(base_temp, 4, n_points) |
| 55 | + |
| 56 | + # Energy consumption: U-shaped relationship with temperature |
| 57 | + energy = 120 + (temp - base_temp) ** 2 * 0.2 + np.random.normal(0, 8, n_points) |
31 | 58 |
|
32 | 59 | for t, e in zip(temp, energy, strict=True): |
33 | 60 | data.append({"Temperature": t, "Energy": e, "Region": region, "Season": season}) |
34 | 61 |
|
35 | 62 | df = pd.DataFrame(data) |
36 | 63 |
|
37 | | -# Plot - Faceted grid: rows = regions, columns = seasons |
38 | | -fig, axes = plt.subplots(nrows=len(regions), ncols=len(seasons), figsize=(16, 9), sharex=True, sharey=True) |
| 64 | +# Create figure with GridSpec for sophisticated layout control |
| 65 | +fig = plt.figure(figsize=(16, 9), facecolor=PAGE_BG) |
| 66 | +gs = gridspec.GridSpec( |
| 67 | + len(regions), len(seasons), figure=fig, hspace=0.35, wspace=0.3, left=0.08, right=0.98, top=0.92, bottom=0.10 |
| 68 | +) |
39 | 69 |
|
40 | | -# Colors for regions |
41 | | -colors = {"North": "#306998", "South": "#FFD43B", "East": "#4DAF4A"} |
| 70 | +# Color map: regions to Okabe-Ito palette |
| 71 | +region_colors = {region: OKABE_ITO[i] for i, region in enumerate(regions)} |
42 | 72 |
|
43 | | -# Create scatter plots in each facet |
| 73 | +# Create scatter plots in each facet with trend lines |
44 | 74 | for i, region in enumerate(regions): |
45 | 75 | for j, season in enumerate(seasons): |
46 | | - ax = axes[i, j] |
| 76 | + ax = fig.add_subplot(gs[i, j]) |
| 77 | + ax.set_facecolor(PAGE_BG) |
| 78 | + |
47 | 79 | subset = df[(df["Region"] == region) & (df["Season"] == season)] |
48 | 80 |
|
| 81 | + # Scatter plot |
49 | 82 | ax.scatter( |
50 | 83 | subset["Temperature"], |
51 | 84 | subset["Energy"], |
52 | 85 | s=120, |
53 | 86 | alpha=0.7, |
54 | | - color=colors[region], |
55 | | - edgecolor="white", |
56 | | - linewidth=0.5, |
| 87 | + color=region_colors[region], |
| 88 | + edgecolors="white", |
| 89 | + linewidth=0.8, |
| 90 | + zorder=3, |
57 | 91 | ) |
58 | 92 |
|
59 | | - # Add grid |
60 | | - ax.grid(True, alpha=0.3, linestyle="--") |
| 93 | + # Trend line for visual emphasis and pattern highlighting |
| 94 | + if len(subset) > 1: |
| 95 | + x_sorted = np.sort(subset["Temperature"].values) |
| 96 | + slope, intercept, _, _, _ = linregress(subset["Temperature"].values, subset["Energy"].values) |
| 97 | + y_trend = slope * x_sorted + intercept |
| 98 | + ax.plot(x_sorted, y_trend, color=INK_MUTED, linewidth=2.5, alpha=0.4, linestyle="-", zorder=1) |
| 99 | + |
| 100 | + # Subtle grid |
| 101 | + ax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK) |
| 102 | + ax.xaxis.grid(True, alpha=0.08, linewidth=0.6, color=INK_SOFT) |
61 | 103 |
|
62 | 104 | # Column headers (top row only) |
63 | 105 | if i == 0: |
64 | | - ax.set_title(season, fontsize=18, fontweight="bold") |
| 106 | + ax.set_title(season, fontsize=20, color=INK, fontweight="semibold") |
| 107 | + |
| 108 | + # Row labels (left side) |
| 109 | + if j == 0: |
| 110 | + ax.set_ylabel(region, fontsize=20, color=INK, fontweight="semibold") |
| 111 | + |
| 112 | + # Only show labels on outer edges |
| 113 | + if i < len(regions) - 1: |
| 114 | + ax.set_xticklabels([]) |
| 115 | + if j > 0: |
| 116 | + ax.set_yticklabels([]) |
65 | 117 |
|
66 | | - # Row labels (rightmost column only) |
67 | | - if j == len(seasons) - 1: |
68 | | - ax.annotate( |
69 | | - region, xy=(1.05, 0.5), xycoords="axes fraction", fontsize=16, fontweight="bold", va="center", ha="left" |
70 | | - ) |
| 118 | + # Tick styling |
| 119 | + ax.tick_params(axis="both", labelsize=14, colors=INK_SOFT, labelcolor=INK_SOFT, length=5, width=0.8) |
71 | 120 |
|
72 | | - # Tick parameters |
73 | | - ax.tick_params(axis="both", labelsize=12) |
| 121 | + # Spine styling |
| 122 | + for spine in ("top", "right"): |
| 123 | + ax.spines[spine].set_visible(False) |
| 124 | + for spine in ("left", "bottom"): |
| 125 | + ax.spines[spine].set_color(INK_SOFT) |
| 126 | + ax.spines[spine].set_linewidth(1.2) |
74 | 127 |
|
75 | 128 | # Shared axis labels |
76 | | -fig.supxlabel("Temperature (°C)", fontsize=20, y=0.02) |
77 | | -fig.supylabel("Energy Consumption (kWh)", fontsize=20, x=0.02) |
| 129 | +fig.text(0.5, 0.02, "Temperature (°C)", ha="center", fontsize=22, color=INK, fontweight="medium") |
| 130 | +fig.text( |
| 131 | + 0.01, 0.5, "Energy Consumption (kWh)", va="center", rotation="vertical", fontsize=22, color=INK, fontweight="medium" |
| 132 | +) |
78 | 133 |
|
79 | | -# Main title |
80 | | -fig.suptitle("facet-grid · matplotlib · pyplots.ai", fontsize=24, y=0.98) |
| 134 | +# Main title with enhanced typography |
| 135 | +fig.suptitle("facet-grid · matplotlib · anyplot.ai", fontsize=26, fontweight="semibold", color=INK, y=0.97) |
81 | 136 |
|
82 | | -plt.tight_layout(rect=[0.04, 0.04, 0.95, 0.95]) |
83 | | -plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
| 137 | +plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG) |
0 commit comments