{"spec_id":"subplot-mosaic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nsubplot-mosaic: Mosaic Subplot Layout with Varying Sizes\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 95/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.layouts import Spacer, column, row\nfrom bokeh.models import ColumnDataSource\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\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\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\nnp.random.seed(42)\n\n# Data for various subplots - Dashboard with business metrics\n\n# A: Large overview time series (top left, spans 2 cols)\ndays = np.arange(1, 91)\nrevenue = 50000 + np.cumsum(np.random.randn(90) * 2000) + days * 300\nsource_a = ColumnDataSource(data={\"day\": days, \"revenue\": revenue})\n\n# B: Scatter plot (top right) - two product lines\nproducts_a = np.random.rand(25) * 100\nprofit_margin_a = products_a * 0.35 + np.random.randn(25) * 4 + 12\nproducts_b = np.random.rand(25) * 100\nprofit_margin_b = products_b * 0.25 + np.random.randn(25) * 5 + 8\nsource_b1 = ColumnDataSource(data={\"products\": products_a, \"profit_margin\": profit_margin_a})\nsource_b2 = ColumnDataSource(data={\"products\": products_b, \"profit_margin\": profit_margin_b})\n\n# C: Bar chart - Categories (middle left)\ncategories = [\"Electronics\", \"Clothing\", \"Food\", \"Books\"]\nsales = [45000, 32000, 28000, 18000]\nsource_c = ColumnDataSource(data={\"category\": categories, \"sales\": sales})\n\n# D: Line chart - Monthly trend (bottom, spans full width) - two years\nmonths = np.arange(1, 13)\norders_2023 = [1200, 1400, 1100, 1600, 1800, 2100, 1900, 2200, 2400, 2100, 2300, 2800]\norders_2024 = [1400, 1600, 1350, 1850, 2100, 2400, 2200, 2500, 2700, 2350, 2600, 3100]\nsource_d1 = ColumnDataSource(data={\"month\": months, \"orders\": orders_2023})\nsource_d2 = ColumnDataSource(data={\"month\": months, \"orders\": orders_2024})\n\n# E: Conversion rate trend (two channels)\nweeks = np.arange(1, 13)\nconversion_web = 3.2 + np.cumsum(np.random.randn(12) * 0.15)\nconversion_mobile = 2.8 + np.cumsum(np.random.randn(12) * 0.18)\nsource_e1 = ColumnDataSource(data={\"week\": weeks, \"conversion\": conversion_web})\nsource_e2 = ColumnDataSource(data={\"week\": weeks, \"conversion\": conversion_mobile})\n\n# F: Customer satisfaction\nquarters = [\"Q1\", \"Q2\", \"Q3\", \"Q4\"]\nsatisfaction = [78, 82, 85, 88]\nsource_f = ColumnDataSource(data={\"quarter\": quarters, \"satisfaction\": satisfaction})\n\n\ndef style_figure(fig):\n    fig.background_fill_color = PAGE_BG\n    fig.border_fill_color = PAGE_BG\n    fig.outline_line_color = INK_SOFT\n    fig.title.text_color = INK\n    fig.title.text_font_size = \"28pt\"\n    fig.xaxis.axis_label_text_color = INK\n    fig.yaxis.axis_label_text_color = INK\n    fig.xaxis.axis_label_text_font_size = \"22pt\"\n    fig.yaxis.axis_label_text_font_size = \"22pt\"\n    fig.xaxis.major_label_text_color = INK_SOFT\n    fig.yaxis.major_label_text_color = INK_SOFT\n    fig.xaxis.major_label_text_font_size = \"18pt\"\n    fig.yaxis.major_label_text_font_size = \"18pt\"\n    fig.xaxis.axis_line_color = INK_SOFT\n    fig.yaxis.axis_line_color = INK_SOFT\n    fig.xgrid.grid_line_color = INK\n    fig.ygrid.grid_line_color = INK\n    fig.xgrid.grid_line_alpha = 0.10\n    fig.ygrid.grid_line_alpha = 0.10\n    fig.toolbar_location = None\n\n\n# A: Large Revenue Overview (spans 2 columns, taller)\np_a = figure(\n    width=3200, height=1100, title=\"Quarterly Revenue Overview\", x_axis_label=\"Day\", y_axis_label=\"Revenue ($)\"\n)\np_a.line(\"day\", \"revenue\", source=source_a, line_width=4, color=IMPRINT[0], legend_label=\"Daily Revenue\")\np_a.scatter(\"day\", \"revenue\", source=source_a, size=15, color=IMPRINT[0], alpha=0.7)\np_a.legend.label_text_font_size = \"16pt\"\np_a.legend.background_fill_color = ELEVATED_BG\np_a.legend.border_line_color = INK_SOFT\np_a.legend.label_text_color = INK_SOFT\nstyle_figure(p_a)\n\n# B: Product Profitability Scatter with two product lines\np_b = figure(\n    width=1600, height=1100, title=\"Product Profitability\", x_axis_label=\"Units Sold\", y_axis_label=\"Profit Margin (%)\"\n)\np_b.scatter(\n    \"products\", \"profit_margin\", source=source_b1, size=18, color=IMPRINT[0], alpha=0.7, legend_label=\"Premium Line\"\n)\np_b.scatter(\n    \"products\", \"profit_margin\", source=source_b2, size=18, color=IMPRINT[1], alpha=0.7, legend_label=\"Standard Line\"\n)\np_b.legend.label_text_font_size = \"14pt\"\np_b.legend.background_fill_color = ELEVATED_BG\np_b.legend.border_line_color = INK_SOFT\np_b.legend.label_text_color = INK_SOFT\np_b.title.text_font_size = \"24pt\"\np_b.xaxis.axis_label_text_font_size = \"18pt\"\np_b.yaxis.axis_label_text_font_size = \"18pt\"\np_b.xaxis.major_label_text_font_size = \"14pt\"\np_b.yaxis.major_label_text_font_size = \"14pt\"\nstyle_figure(p_b)\n\n# C: Category Sales Bar Chart\np_c = figure(\n    width=2400,\n    height=900,\n    x_range=categories,\n    title=\"Sales by Category\",\n    x_axis_label=\"Category\",\n    y_axis_label=\"Sales ($)\",\n)\np_c.vbar(\n    x=\"category\",\n    top=\"sales\",\n    source=source_c,\n    width=0.7,\n    color=IMPRINT[0],\n    alpha=0.85,\n    line_color=PAGE_BG,\n    line_width=2,\n    legend_label=\"Q4 2024 Sales\",\n)\np_c.legend.label_text_font_size = \"14pt\"\np_c.legend.background_fill_color = ELEVATED_BG\np_c.legend.border_line_color = INK_SOFT\np_c.legend.label_text_color = INK_SOFT\np_c.title.text_font_size = \"24pt\"\np_c.xaxis.axis_label_text_font_size = \"18pt\"\np_c.yaxis.axis_label_text_font_size = \"18pt\"\np_c.xaxis.major_label_text_font_size = \"14pt\"\np_c.yaxis.major_label_text_font_size = \"14pt\"\np_c.xaxis.major_label_orientation = 0.3\nstyle_figure(p_c)\n\n# Empty cell spacer (gap in mosaic)\nempty_spacer = Spacer(width=2400, height=450, background=ELEVATED_BG)\n\n# F: Customer Satisfaction Bar\np_f = figure(\n    width=2400,\n    height=450,\n    x_range=quarters,\n    title=\"Customer Satisfaction\",\n    x_axis_label=\"Quarter\",\n    y_axis_label=\"Score\",\n)\np_f.vbar(\n    x=\"quarter\",\n    top=\"satisfaction\",\n    source=source_f,\n    width=0.6,\n    color=IMPRINT[1],\n    alpha=0.9,\n    line_color=PAGE_BG,\n    line_width=2,\n    legend_label=\"Satisfaction Score\",\n)\np_f.legend.label_text_font_size = \"12pt\"\np_f.legend.background_fill_color = ELEVATED_BG\np_f.legend.border_line_color = INK_SOFT\np_f.legend.label_text_color = INK_SOFT\np_f.title.text_font_size = \"22pt\"\np_f.xaxis.axis_label_text_font_size = \"16pt\"\np_f.yaxis.axis_label_text_font_size = \"16pt\"\np_f.xaxis.major_label_text_font_size = \"14pt\"\np_f.yaxis.major_label_text_font_size = \"14pt\"\nstyle_figure(p_f)\n\n# D: Monthly Orders Trend (full width bottom) - comparing two years\np_d = figure(\n    width=4800, height=700, title=\"subplot-mosaic · bokeh · anyplot.ai\", x_axis_label=\"Month\", y_axis_label=\"Orders\"\n)\np_d.line(\"month\", \"orders\", source=source_d1, line_width=5, color=IMPRINT[0], legend_label=\"2023\")\np_d.scatter(\"month\", \"orders\", source=source_d1, size=18, color=IMPRINT[0])\np_d.line(\"month\", \"orders\", source=source_d2, line_width=5, color=IMPRINT[1], legend_label=\"2024\")\np_d.scatter(\"month\", \"orders\", source=source_d2, size=18, color=IMPRINT[1])\np_d.legend.label_text_font_size = \"18pt\"\np_d.legend.background_fill_color = ELEVATED_BG\np_d.legend.border_line_color = INK_SOFT\np_d.legend.label_text_color = INK_SOFT\np_d.legend.orientation = \"horizontal\"\np_d.title.text_font_size = \"32pt\"\np_d.xaxis.axis_label_text_font_size = \"22pt\"\np_d.yaxis.axis_label_text_font_size = \"22pt\"\np_d.xaxis.major_label_text_font_size = \"18pt\"\np_d.yaxis.major_label_text_font_size = \"18pt\"\nstyle_figure(p_d)\n\n# Create mosaic layout: AAB / C.F / DDD pattern\nrow1 = row(p_a, p_b)\nrow2 = row(p_c, column(empty_spacer, p_f))\nlayout = column(row1, row2, p_d)\n\n# Write interactive HTML\noutput_file(f\"plot-{THEME}.html\")\nsave(layout)\n\n# Screenshot with headless Chrome — Selenium 4 / Selenium Manager\nW, H = 4800, 2700\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}