|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | facet-grid: Faceted Grid Plot |
3 | | -Library: seaborn 0.13.2 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-30 |
| 3 | +Library: seaborn 0.13.2 | Python 3.13.13 |
| 4 | +Quality: 90/100 | Updated: 2026-05-13 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
| 9 | +import matplotlib.pyplot as plt |
7 | 10 | import numpy as np |
8 | 11 | import pandas as pd |
9 | 12 | import seaborn as sns |
10 | 13 |
|
11 | 14 |
|
12 | | -# Data |
| 15 | +# Theme tokens |
| 16 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 17 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 18 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 19 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 20 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 21 | +INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" |
| 22 | +BRAND = "#009E73" |
| 23 | + |
| 24 | +# Data - Differentiated scenario: Production Cost vs Profit Margin by Product Line and Month |
13 | 25 | np.random.seed(42) |
14 | 26 |
|
15 | | -# Create dataset with two categorical faceting variables |
16 | | -categories_row = ["Region A", "Region B", "Region C"] |
17 | | -categories_col = ["Q1", "Q2", "Q3", "Q4"] |
| 27 | +product_lines = ["Electronics", "Apparel", "Food"] |
| 28 | +months = ["Jan", "Feb", "Mar", "Apr"] |
18 | 29 |
|
19 | 30 | data = [] |
20 | | -for row_cat in categories_row: |
21 | | - for col_cat in categories_col: |
22 | | - n_points = 25 |
23 | | - # Vary the relationship by region and quarter |
24 | | - base_slope = 0.6 + 0.2 * categories_row.index(row_cat) |
25 | | - intercept = 10 + 5 * categories_col.index(col_cat) |
26 | | - |
27 | | - x = np.random.uniform(5, 30, n_points) |
28 | | - noise = np.random.normal(0, 3, n_points) |
29 | | - y = intercept + base_slope * x + noise |
| 31 | +for product_idx, product in enumerate(product_lines): |
| 32 | + for month_idx, month in enumerate(months): |
| 33 | + n_points = 30 |
| 34 | + # Vary profit margin by product line (Electronics: high margin but higher cost, |
| 35 | + # Apparel: moderate, Food: low margin, high volume) |
| 36 | + base_margin = 15 + 10 * product_idx |
| 37 | + margin_noise = np.random.normal(0, 3, n_points) |
| 38 | + |
| 39 | + # Cost varies by month (seasonality) |
| 40 | + base_cost = 800 + 200 * month_idx |
| 41 | + cost_var = np.random.uniform(-100, 100, n_points) |
| 42 | + |
| 43 | + # Profit margin increases with cost for some products |
| 44 | + cost = base_cost + cost_var |
| 45 | + profit_margin = base_margin + 0.01 * (cost - base_cost) + margin_noise |
| 46 | + profit_margin = np.clip(profit_margin, 5, 40) |
30 | 47 |
|
31 | 48 | for i in range(n_points): |
32 | 49 | data.append( |
33 | | - {"Marketing Spend ($k)": x[i], "Sales Revenue ($k)": y[i], "Region": row_cat, "Quarter": col_cat} |
| 50 | + { |
| 51 | + "Production Cost ($)": cost[i], |
| 52 | + "Profit Margin (%)": profit_margin[i], |
| 53 | + "Product Line": product, |
| 54 | + "Month": month, |
| 55 | + } |
34 | 56 | ) |
35 | 57 |
|
36 | 58 | df = pd.DataFrame(data) |
37 | 59 |
|
38 | | -# Plot |
39 | | -sns.set_context("talk", font_scale=1.3) |
40 | | -sns.set_style("whitegrid") |
| 60 | +# Setup theme |
| 61 | +sns.set_theme( |
| 62 | + style="ticks", |
| 63 | + rc={ |
| 64 | + "figure.facecolor": PAGE_BG, |
| 65 | + "axes.facecolor": PAGE_BG, |
| 66 | + "axes.edgecolor": INK_SOFT, |
| 67 | + "axes.labelcolor": INK, |
| 68 | + "text.color": INK, |
| 69 | + "xtick.color": INK_SOFT, |
| 70 | + "ytick.color": INK_SOFT, |
| 71 | + "grid.color": INK_MUTED, |
| 72 | + "grid.alpha": 0.10, |
| 73 | + "legend.facecolor": ELEVATED_BG, |
| 74 | + "legend.edgecolor": INK_SOFT, |
| 75 | + }, |
| 76 | +) |
41 | 77 |
|
42 | | -g = sns.FacetGrid(df, row="Region", col="Quarter", height=4.5, aspect=1.1, margin_titles=True) |
| 78 | +# Plot |
| 79 | +g = sns.FacetGrid(df, row="Product Line", col="Month", height=3.8, aspect=1.1, margin_titles=True) |
43 | 80 |
|
| 81 | +# Map scatterplot with regression line |
44 | 82 | g.map_dataframe( |
45 | | - sns.scatterplot, |
46 | | - x="Marketing Spend ($k)", |
47 | | - y="Sales Revenue ($k)", |
48 | | - color="#306998", |
49 | | - s=150, |
50 | | - alpha=0.7, |
51 | | - edgecolor="white", |
52 | | - linewidth=0.5, |
| 83 | + sns.regplot, |
| 84 | + x="Production Cost ($)", |
| 85 | + y="Profit Margin (%)", |
| 86 | + scatter_kws={"color": BRAND, "s": 140, "alpha": 0.75, "edgecolor": PAGE_BG, "linewidths": 0.8}, |
| 87 | + line_kws={"color": INK_SOFT, "linewidth": 2.5, "alpha": 0.6}, |
| 88 | + ci=None, |
53 | 89 | ) |
54 | 90 |
|
55 | 91 | # Styling |
56 | | -g.set_titles(row_template="{row_name}", col_template="{col_name}", size=20) |
57 | | -g.set_axis_labels("Marketing Spend ($k)", "Sales Revenue ($k)", fontsize=18) |
| 92 | +g.set_titles(row_template="{row_name}", col_template="{col_name}", size=18, fontweight="medium") |
58 | 93 |
|
59 | | -for ax in g.axes.flat: |
| 94 | +# Remove y-axis label repetition: only show on leftmost column |
| 95 | +for i, ax in enumerate(g.axes.flat): |
60 | 96 | ax.tick_params(axis="both", labelsize=14) |
61 | | - ax.grid(True, alpha=0.3, linestyle="--") |
| 97 | + ax.grid(True, alpha=0.12, linestyle="-", linewidth=0.7) |
| 98 | + |
| 99 | + # Only leftmost column keeps y-axis label |
| 100 | + if i % 4 != 0: |
| 101 | + ax.set_ylabel("") |
| 102 | + |
| 103 | +# Set labels once globally |
| 104 | +g.set_axis_labels("Production Cost ($)", "Profit Margin (%)", fontsize=18) |
62 | 105 |
|
63 | | -g.figure.suptitle("facet-grid · seaborn · pyplots.ai", fontsize=26, fontweight="bold", y=1.02) |
| 106 | +# Add main title |
| 107 | +g.figure.suptitle("facet-grid · seaborn · anyplot.ai", fontsize=26, fontweight="medium", y=0.995, color=INK) |
64 | 108 |
|
65 | 109 | g.tight_layout() |
66 | 110 |
|
67 | 111 | # Save |
68 | | -g.savefig("plot.png", dpi=300, bbox_inches="tight") |
| 112 | +plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG) |
0 commit comments