Skip to content

Commit 6f28863

Browse files
feat(matplotlib): implement facet-grid (#6513)
## Implementation: `facet-grid` - python/matplotlib Implements the **python/matplotlib** version of `facet-grid`. **File:** `plots/facet-grid/implementations/python/matplotlib.py` **Parent Issue:** #2696 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/25776150899)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 27a1eef commit 6f28863

2 files changed

Lines changed: 256 additions & 180 deletions

File tree

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,137 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
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
55
"""
66

7-
import matplotlib.pyplot as plt
8-
import numpy as np
9-
import pandas as pd
7+
import os
8+
import sys
109

1110

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+
1239
# Data
1340
np.random.seed(42)
1441

15-
# Create sample data with two categorical variables for faceting
1642
regions = ["North", "South", "East"]
1743
seasons = ["Spring", "Summer", "Fall", "Winter"]
1844

1945
data = []
2046
for region in regions:
2147
for season in seasons:
2248
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}
2752

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)
3158

3259
for t, e in zip(temp, energy, strict=True):
3360
data.append({"Temperature": t, "Energy": e, "Region": region, "Season": season})
3461

3562
df = pd.DataFrame(data)
3663

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+
)
3969

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)}
4272

43-
# Create scatter plots in each facet
73+
# Create scatter plots in each facet with trend lines
4474
for i, region in enumerate(regions):
4575
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+
4779
subset = df[(df["Region"] == region) & (df["Season"] == season)]
4880

81+
# Scatter plot
4982
ax.scatter(
5083
subset["Temperature"],
5184
subset["Energy"],
5285
s=120,
5386
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,
5791
)
5892

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)
61103

62104
# Column headers (top row only)
63105
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([])
65117

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)
71120

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)
74127

75128
# 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+
)
78133

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)
81136

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

Comments
 (0)