{"spec_id":"subplot-mosaic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nsubplot-mosaic: Mosaic Subplot Layout with Varying Sizes\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\n# Theme configuration\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\n# Okabe-Ito palette (first series is always #009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\"]\n\n# Data - Create diverse datasets for dashboard-style mosaic layout\nnp.random.seed(42)\n\n# Panel A: Wide time series (top, spanning 2 columns)\ndates = pd.date_range(\"2024-01-01\", periods=100, freq=\"D\")\ndf_timeseries = pd.DataFrame(\n    {\"date\": dates, \"value\": np.cumsum(np.random.randn(100)) + 50, \"category\": \"Revenue Trend\"}\n)\n\n# Panel B: Small metric (top right corner)\ndf_gauge = pd.DataFrame({\"metric\": [\"Current\"], \"value\": [78], \"max_value\": [100]})\n\n# Panel C: Bar chart (middle left)\ndf_bars = pd.DataFrame({\"region\": [\"North\", \"South\", \"East\", \"West\", \"Central\"], \"sales\": [45, 38, 52, 29, 41]})\n\n# Panel D: Scatter plot (middle right, spanning 2 rows)\nn_points = 80\ndf_scatter = pd.DataFrame(\n    {\n        \"efficiency\": np.random.uniform(60, 95, n_points),\n        \"output\": np.random.uniform(100, 500, n_points) + np.random.uniform(60, 95, n_points) * 3,\n        \"size\": np.random.uniform(20, 100, n_points),\n    }\n)\n\n# Panel E: Small bar chart (bottom left)\ndf_categories = pd.DataFrame({\"type\": [\"Type A\", \"Type B\", \"Type C\"], \"count\": [24, 18, 31]})\n\n# Panel F: Small area chart (bottom middle)\ndf_area = pd.DataFrame(\n    {\n        \"hour\": list(range(24)),\n        \"traffic\": [10, 8, 5, 4, 6, 15, 35, 55, 48, 42, 38, 45, 50, 48, 52, 60, 65, 55, 40, 30, 25, 20, 15, 12],\n    }\n)\n\n# Create individual charts with proper styling\n\n# Chart A: Wide time series (spans 2 columns at top)\nchart_a = (\n    alt.Chart(df_timeseries)\n    .mark_line(strokeWidth=4, color=IMPRINT[0])\n    .encode(\n        x=alt.X(\"date:T\", title=\"Date\", axis=alt.Axis(labelFontSize=18, titleFontSize=22)),\n        y=alt.Y(\"value:Q\", title=\"Revenue ($K)\", axis=alt.Axis(labelFontSize=18, titleFontSize=22)),\n    )\n    .properties(width=900, height=240, title=alt.Title(\"Monthly Revenue Overview\", fontSize=24))\n)\n\n# Chart B: Gauge-style metric (small, top right)\nchart_b_bg = alt.Chart(df_gauge).mark_arc(innerRadius=50, outerRadius=80, theta=3.14159, theta2=0, color=INK_SOFT)\n\nchart_b_value = (\n    alt.Chart(df_gauge)\n    .mark_arc(\n        innerRadius=50,\n        outerRadius=80,\n        theta=3.14159,\n        theta2=alt.expr(\"3.14159 - (datum.value / datum.max_value) * 3.14159\"),\n        color=IMPRINT[0],\n    )\n    .encode()\n)\n\nchart_b_text = (\n    alt.Chart(df_gauge)\n    .mark_text(fontSize=32, fontWeight=\"bold\", color=IMPRINT[0])\n    .encode(text=alt.Text(\"value:Q\", format=\".0f\"))\n)\n\nchart_b = alt.layer(chart_b_bg, chart_b_value, chart_b_text).properties(\n    width=240, height=240, title=alt.Title(\"Performance Score\", fontSize=22)\n)\n\n# Chart C: Bar chart (middle left)\nchart_c = (\n    alt.Chart(df_bars)\n    .mark_bar(color=IMPRINT[1], cornerRadiusTopLeft=4, cornerRadiusTopRight=4)\n    .encode(\n        x=alt.X(\"region:N\", title=\"Region\", axis=alt.Axis(labelFontSize=16, titleFontSize=20, labelAngle=0)),\n        y=alt.Y(\"sales:Q\", title=\"Sales ($K)\", axis=alt.Axis(labelFontSize=16, titleFontSize=20)),\n        tooltip=[\"region:N\", alt.Tooltip(\"sales:Q\", format=\".1f\")],\n    )\n    .properties(width=320, height=220, title=alt.Title(\"Sales by Region\", fontSize=22))\n)\n\n# Chart D: Scatter plot with legend (spans 2 rows on right side)\nchart_d = (\n    alt.Chart(df_scatter)\n    .mark_circle(opacity=0.7)\n    .encode(\n        x=alt.X(\n            \"efficiency:Q\",\n            title=\"Efficiency (%)\",\n            scale=alt.Scale(domain=[55, 100]),\n            axis=alt.Axis(labelFontSize=16, titleFontSize=20),\n        ),\n        y=alt.Y(\"output:Q\", title=\"Output (units)\", axis=alt.Axis(labelFontSize=16, titleFontSize=20)),\n        size=alt.Size(\"size:Q\", scale=alt.Scale(range=[80, 300]), legend=None),\n        color=alt.Color(\n            \"efficiency:Q\", scale=alt.Scale(scheme=\"viridis\"), legend=alt.Legend(titleFontSize=18, labelFontSize=16)\n        ),\n        tooltip=[\"efficiency:Q\", \"output:Q\", alt.Tooltip(\"size:Q\", format=\".1f\")],\n    )\n    .properties(width=380, height=480, title=alt.Title(\"Efficiency vs Output\", fontSize=22))\n)\n\n# Chart E: Small bar chart (bottom left)\nchart_e = (\n    alt.Chart(df_categories)\n    .mark_bar(color=IMPRINT[4])\n    .encode(\n        x=alt.X(\"type:N\", title=None, axis=alt.Axis(labelFontSize=14)),\n        y=alt.Y(\"count:Q\", title=\"Count\", axis=alt.Axis(labelFontSize=14, titleFontSize=18)),\n        tooltip=[\"type:N\", alt.Tooltip(\"count:Q\", format=\"d\")],\n    )\n    .properties(width=220, height=200, title=alt.Title(\"By Category\", fontSize=20))\n)\n\n# Chart F: Area chart (bottom middle)\nchart_f = (\n    alt.Chart(df_area)\n    .mark_area(color=IMPRINT[2], opacity=0.7, line={\"color\": IMPRINT[2], \"strokeWidth\": 3})\n    .encode(\n        x=alt.X(\"hour:Q\", title=\"Hour\", axis=alt.Axis(labelFontSize=14, titleFontSize=18)),\n        y=alt.Y(\"traffic:Q\", title=\"Traffic\", axis=alt.Axis(labelFontSize=14, titleFontSize=18)),\n        tooltip=[\"hour:Q\", alt.Tooltip(\"traffic:Q\", format=\"d\")],\n    )\n    .properties(width=260, height=200, title=alt.Title(\"Daily Traffic Pattern\", fontSize=20))\n)\n\n# Build mosaic layout with empty cell using concatenation\n# Layout pattern: \"AAB\" (A=time series spanning 2 cols, B=gauge)\n#                 \"CDD\" (C=bars, D=scatter spanning 2 cols and 2 rows)\n#                 \"EFD\" (E=categories, F=area, D continues from above)\n\n# Top row: time series (wide) + gauge\ntop_row = alt.hconcat(chart_a, chart_b, spacing=30)\n\n# Middle row: bars + scatter (scatter spans into bottom row)\nleft_middle = chart_c\nleft_bottom = alt.hconcat(chart_e, chart_f, spacing=20)\nleft_column = alt.vconcat(left_middle, left_bottom, spacing=20)\n\n# Combine middle and bottom rows\nmiddle_bottom_row = alt.hconcat(left_column, chart_d, spacing=30)\n\n# Combine all rows\nmosaic = (\n    alt.vconcat(top_row, middle_bottom_row, spacing=30)\n    .properties(\n        title=alt.Title(\"subplot-mosaic · altair · anyplot.ai\", fontSize=32, anchor=\"middle\", offset=20),\n        background=PAGE_BG,\n    )\n    .configure(background=PAGE_BG)\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT, strokeWidth=0)\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_title(color=INK, fontSize=32)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save outputs with theme-suffixed filenames\nmosaic.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nmosaic.save(f\"plot-{THEME}.html\")\n"}