|
| 1 | +""" pyplots.ai |
| 2 | +facet-grid: Faceted Grid Plot |
| 3 | +Library: bokeh 3.8.1 | Python 3.13.11 |
| 4 | +Quality: 90/100 | Created: 2025-12-30 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import pandas as pd |
| 9 | +from bokeh.io import export_png |
| 10 | +from bokeh.layouts import column, gridplot |
| 11 | +from bokeh.models import ColumnDataSource, Div |
| 12 | +from bokeh.plotting import figure |
| 13 | + |
| 14 | + |
| 15 | +# Data - Product performance across regions and seasons |
| 16 | +np.random.seed(42) |
| 17 | + |
| 18 | +regions = ["North", "South", "East"] |
| 19 | +seasons = ["Spring", "Summer", "Fall", "Winter"] |
| 20 | + |
| 21 | +data = [] |
| 22 | +for region in regions: |
| 23 | + for season in seasons: |
| 24 | + n_points = 25 |
| 25 | + # Base values vary by region |
| 26 | + base_x = {"North": 20, "South": 30, "East": 25}[region] |
| 27 | + base_y = {"North": 50, "South": 70, "East": 60}[region] |
| 28 | + |
| 29 | + # Seasonal adjustments |
| 30 | + season_adj = {"Spring": 1.0, "Summer": 1.3, "Fall": 0.9, "Winter": 0.7}[season] |
| 31 | + |
| 32 | + x = np.random.normal(base_x, 5, n_points) |
| 33 | + y = base_y * season_adj + x * 0.8 + np.random.normal(0, 8, n_points) |
| 34 | + |
| 35 | + for xi, yi in zip(x, y, strict=True): |
| 36 | + data.append({"marketing_spend": xi, "sales": yi, "region": region, "season": season}) |
| 37 | + |
| 38 | +df = pd.DataFrame(data) |
| 39 | + |
| 40 | +# Colors for each region - darker orange instead of yellow for better visibility |
| 41 | +colors = {"North": "#306998", "South": "#E69F00", "East": "#4ECDC4"} |
| 42 | + |
| 43 | +# Create grid of plots (rows=seasons, cols=regions) |
| 44 | +# Target: 4800x2700 total. With 3 cols, 4 rows + title/legend: |
| 45 | +# - Subplot width: 4800 / 3 = 1600 |
| 46 | +# - Subplot height: (2700 - 120 title - 100 legend) / 4 = 620 |
| 47 | +plots = [] |
| 48 | + |
| 49 | +for season in seasons: |
| 50 | + row_plots = [] |
| 51 | + for region in regions: |
| 52 | + subset = df[(df["region"] == region) & (df["season"] == season)] |
| 53 | + source = ColumnDataSource(data={"x": subset["marketing_spend"], "y": subset["sales"]}) |
| 54 | + |
| 55 | + # Create figure for each cell |
| 56 | + p = figure(width=1600, height=620, x_range=(5, 50), y_range=(20, 130), tools="") |
| 57 | + |
| 58 | + # Add scatter points |
| 59 | + p.scatter( |
| 60 | + x="x", y="y", source=source, size=18, color=colors[region], alpha=0.7, line_color="#333333", line_width=1 |
| 61 | + ) |
| 62 | + |
| 63 | + # Add facet label in top-left corner |
| 64 | + p.text( |
| 65 | + x=[8], |
| 66 | + y=[122], |
| 67 | + text=[f"{region} · {season}"], |
| 68 | + text_font_size="18pt", |
| 69 | + text_color="#333333", |
| 70 | + text_font_style="bold", |
| 71 | + ) |
| 72 | + |
| 73 | + # Style axes |
| 74 | + p.xaxis.axis_label = "Marketing Spend ($K)" if season == seasons[-1] else "" |
| 75 | + p.yaxis.axis_label = "Sales ($K)" if region == regions[0] else "" |
| 76 | + p.xaxis.axis_label_text_font_size = "20pt" |
| 77 | + p.yaxis.axis_label_text_font_size = "20pt" |
| 78 | + p.xaxis.major_label_text_font_size = "16pt" |
| 79 | + p.yaxis.major_label_text_font_size = "16pt" |
| 80 | + |
| 81 | + # Grid styling |
| 82 | + p.xgrid.grid_line_alpha = 0.3 |
| 83 | + p.ygrid.grid_line_alpha = 0.3 |
| 84 | + p.xgrid.grid_line_dash = "dashed" |
| 85 | + p.ygrid.grid_line_dash = "dashed" |
| 86 | + |
| 87 | + # Background |
| 88 | + p.background_fill_color = "#fafafa" |
| 89 | + |
| 90 | + row_plots.append(p) |
| 91 | + plots.append(row_plots) |
| 92 | + |
| 93 | +# Create gridplot layout |
| 94 | +grid = gridplot(plots, toolbar_location=None, merge_tools=False) |
| 95 | + |
| 96 | +# Add overall title using Div for cleaner implementation |
| 97 | +title_div = Div( |
| 98 | + text="<h1 style='text-align: center; color: #333333; font-size: 32pt; margin: 20px 0;'>" |
| 99 | + "facet-grid · bokeh · pyplots.ai</h1>", |
| 100 | + width=4800, |
| 101 | + height=120, |
| 102 | +) |
| 103 | + |
| 104 | +# Create legend using Div for region-color mapping |
| 105 | +legend_html = ( |
| 106 | + "<div style='text-align: center; font-size: 18pt; padding: 20px 0;'>" |
| 107 | + "<span style='font-weight: bold; margin-right: 30px;'>Region:</span>" |
| 108 | +) |
| 109 | +for region, color in colors.items(): |
| 110 | + legend_html += ( |
| 111 | + f"<span style='margin-right: 40px;'>" |
| 112 | + f"<span style='display: inline-block; width: 20px; height: 20px; " |
| 113 | + f"background-color: {color}; border: 1px solid #333; border-radius: 50%; " |
| 114 | + f"vertical-align: middle; margin-right: 8px;'></span>" |
| 115 | + f"{region}</span>" |
| 116 | + ) |
| 117 | +legend_html += "</div>" |
| 118 | + |
| 119 | +legend_div = Div(text=legend_html, width=4800, height=80) |
| 120 | + |
| 121 | +# Combine title, grid, and legend using column layout |
| 122 | +final_layout = column(title_div, grid, legend_div) |
| 123 | + |
| 124 | +# Save |
| 125 | +export_png(final_layout, filename="plot.png") |
0 commit comments