{"spec_id":"subplot-grid","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nsubplot-grid: Subplot Grid Layout\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-13\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_bar,\n    geom_histogram,\n    geom_line,\n    geom_point,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_fill_manual,\n    scale_x_continuous,\n    stat_smooth,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\nBRAND = \"#009E73\"\nSECONDARY = \"#C475FD\"\n\n# Data - Product performance dashboard\nnp.random.seed(42)\n\n# Daily product metrics\nn_days = 40\ndays = pd.date_range(\"2024-01-01\", periods=n_days, freq=\"D\")\nproducts = [\"A\", \"B\"]\n\n# Generate time series data\ndata_list = []\nfor product in products:\n    base = 100 if product == \"A\" else 85\n    trend = 0.5 if product == \"A\" else 0.8\n    sales = base + np.arange(n_days) * trend + np.random.randn(n_days) * 10\n    data_list.append(pd.DataFrame({\"date\": days, \"sales\": sales, \"product\": product}))\n\ndf_timeseries = pd.concat(data_list, ignore_index=True)\ndf_timeseries[\"day_num\"] = (df_timeseries[\"date\"] - df_timeseries[\"date\"].min()).dt.days\n\n# Category breakdown data\ncategories = [\"Q1\", \"Q2\", \"Q3\", \"Q4\"]\nrevenues = [45, 32, 28, 18]\ndf_category = pd.DataFrame({\"category\": categories, \"revenue\": revenues})\ndf_category[\"category\"] = pd.Categorical(df_category[\"category\"], categories=categories, ordered=True)\n\n# Product distribution data\ndf_prod_a = df_timeseries[df_timeseries[\"product\"] == \"A\"][\"sales\"]\n\n# Scatter data - relationship between units sold and profit margin\nunits = np.random.uniform(100, 500, 60)\nmargin = 20 + 0.03 * units + np.random.randn(60) * 5\ndf_scatter = pd.DataFrame({\"units\": units, \"margin\": margin})\n\n# Okabe-Ito palette for categorical data\nokabe_ito = [BRAND, SECONDARY, \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Shared theme for all plots - sized for 4800x2700 canvas\nbase_theme = theme_minimal() + theme(\n    plot_title=element_text(size=22, face=\"bold\", ha=\"center\", margin={\"b\": 10}),\n    axis_title=element_text(size=20, color=INK),\n    axis_text=element_text(size=16, color=INK_SOFT),\n    legend_text=element_text(size=16, color=INK_SOFT),\n    legend_title=element_text(size=18, face=\"bold\", color=INK),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n    panel_background=element_rect(fill=PAGE_BG, color=None),\n    plot_background=element_rect(fill=PAGE_BG, color=None),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_position=\"top\",\n    plot_margin=0.01,\n)\n\n# Plot 1: Sales trend over time (Line chart)\np1 = (\n    ggplot(df_timeseries, aes(x=\"day_num\", y=\"sales\", color=\"product\"))\n    + geom_line(size=1.2)\n    + geom_point(size=3, alpha=0.8)\n    + stat_smooth(method=\"lm\", se=False, linetype=\"dashed\", size=0.8, color=INK_SOFT)\n    + scale_color_manual(values=okabe_ito[:2])\n    + labs(title=\"Sales Trend\", x=\"Day\", y=\"Sales (Units)\", color=\"Product\")\n    + base_theme\n)\n\n# Plot 2: Revenue by category (Bar chart)\np2 = (\n    ggplot(df_category, aes(x=\"category\", y=\"revenue\", fill=\"category\"))\n    + geom_bar(stat=\"identity\", width=0.7, show_legend=False)\n    + scale_fill_manual(values=okabe_ito[:4])\n    + labs(title=\"Quarterly Revenue\", x=\"Quarter\", y=\"Revenue (k$)\")\n    + base_theme\n)\n\n# Plot 3: Sales distribution histogram for Product A\ndf_hist = pd.DataFrame({\"sales\": df_prod_a.values})\np3 = (\n    ggplot(df_hist, aes(x=\"sales\"))\n    + geom_histogram(bins=10, fill=BRAND, color=PAGE_BG, alpha=0.8)\n    + scale_x_continuous(breaks=[90, 105, 120])\n    + labs(title=\"Sales Distribution (Product A)\", x=\"Sales (Units)\", y=\"Frequency\")\n    + base_theme\n)\n\n# Plot 4: Units vs Margin scatter plot\np4 = (\n    ggplot(df_scatter, aes(x=\"units\", y=\"margin\"))\n    + geom_point(size=4, color=BRAND, alpha=0.7)\n    + stat_smooth(method=\"lm\", color=SECONDARY, se=True, fill=SECONDARY, alpha=0.15, size=0.8)\n    + labs(title=\"Units vs Margin\", x=\"Units Sold\", y=\"Profit Margin (%)\")\n    + base_theme\n)\n\n# Compose into 2x2 grid using plotnine's composition operators\ntop_row = p1 | p2\nbottom_row = p3 | p4\ngrid = top_row / bottom_row\n\n# Draw the grid and customize overall layout\nfig = grid.draw()\nfig.set_size_inches(16, 10)\nfig.patch.set_facecolor(PAGE_BG)\nfig.subplots_adjust(top=0.88, bottom=0.10, hspace=0.35, wspace=0.28)\n\n# Add main title with better positioning\nfig.suptitle(\"subplot-grid · plotnine · anyplot.ai\", fontsize=28, fontweight=\"bold\", y=0.96, color=INK)\n\nfig.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}