{"spec_id":"subplot-grid","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nsubplot-grid: Subplot Grid Layout\nLibrary: letsplot 4.9.0 | 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 lets_plot import *\n\n\nLetsPlot.setup_html()\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme-adaptive colors\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 (colorblind-safe)\nIMPRINT = [\n    \"#009E73\",  # 1: brand green\n    \"#C475FD\",  # 2: vermillion\n    \"#4467A3\",  # 3: blue\n    \"#BD8233\",  # 4: reddish purple\n    \"#AE3030\",  # 5: orange\n    \"#2ABCCD\",  # 6: sky blue\n    \"#954477\",  # 7: yellow\n]\n\n# Data - Financial dashboard theme\nnp.random.seed(42)\n\n# Generate 60 days of stock-like data\ndays = 60\nday_nums = np.arange(days)\n\n# Price data (cumulative random walk)\nreturns = np.random.normal(0.001, 0.02, days)\nprice = 100 * np.cumprod(1 + returns)\n\n# Volume data (random with some correlation to price moves)\nbase_volume = 1_000_000\nvolume = base_volume * (1 + 0.5 * np.abs(returns) / 0.02 + np.random.uniform(0, 0.5, days))\n\n# Daily returns for histogram\ndaily_returns = np.diff(np.log(price)) * 100  # Log returns as percentage\n\n# Create DataFrames for each subplot\nprice_df = pd.DataFrame({\"day\": day_nums, \"price\": price})\nvolume_df = pd.DataFrame({\"day\": day_nums, \"volume\": volume / 1_000_000})  # In millions\nreturns_df = pd.DataFrame({\"return\": daily_returns})\n\n# Rolling 10-day average for price\nprice_df[\"rolling_avg\"] = pd.Series(price).rolling(window=10, min_periods=1).mean()\n\n# Theme-adaptive plot styling\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_grid_major=element_line(color=INK_SOFT, size=0.3),\n    panel_grid_minor=element_blank(),\n    axis_title=element_text(size=20, color=INK),\n    axis_text=element_text(size=16, color=INK_SOFT),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    axis_ticks=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(size=24, color=INK),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=16),\n    legend_title=element_text(color=INK, size=16),\n)\n\n# Create individual plots\n\n# Top left: Price line chart (brand green for primary, orange for secondary)\nprice_plot = (\n    ggplot(price_df, aes(x=\"day\", y=\"price\"))\n    + geom_line(color=IMPRINT[0], size=1.5)\n    + geom_line(aes(y=\"rolling_avg\"), color=IMPRINT[1], size=1.2, linetype=\"dashed\")\n    + labs(x=\"Trading Day\", y=\"Price ($)\", title=\"Stock Price with 10-Day Moving Average\")\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Top right: Volume bar chart (blue)\nvolume_plot = (\n    ggplot(volume_df, aes(x=\"day\", y=\"volume\"))\n    + geom_bar(stat=\"identity\", fill=IMPRINT[2], alpha=0.8, width=0.8)\n    + labs(x=\"Trading Day\", y=\"Volume (Millions)\", title=\"Daily Trading Volume\")\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Bottom left: Returns histogram (reddish purple bars, brand green reference line)\nreturns_plot = (\n    ggplot(returns_df, aes(x=\"return\"))\n    + geom_histogram(fill=IMPRINT[3], color=INK_SOFT, bins=20, alpha=0.8)\n    + geom_vline(xintercept=0, color=IMPRINT[0], size=1.5, linetype=\"dashed\")\n    + labs(x=\"Daily Return (%)\", y=\"Frequency\", title=\"Distribution of Daily Returns\")\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Bottom right: Scatter plot - price vs volume relationship\nscatter_df = pd.DataFrame({\"abs_return\": np.abs(daily_returns), \"volume\": volume[1:] / 1_000_000})\nscatter_plot = (\n    ggplot(scatter_df, aes(x=\"abs_return\", y=\"volume\"))\n    + geom_point(color=IMPRINT[4], size=4, alpha=0.7)\n    + geom_smooth(method=\"lm\", color=IMPRINT[0], size=1.5, fill=None)\n    + labs(x=\"Absolute Return (%)\", y=\"Volume (Millions)\", title=\"Volume vs Price Movement\")\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Combine into 2x2 grid using gggrid\ngrid_plot = gggrid([price_plot, volume_plot, returns_plot, scatter_plot], ncol=2)\n\n# Add overall title\nfinal_plot = grid_plot + ggsize(1600, 900) + ggtitle(\"letsplot · anyplot.ai\") + anyplot_theme\n\n# Save as PNG (scale=3 for 4800x2700, path='.' to save in current directory)\nggsave(final_plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\n\n# Also save as HTML for interactive version\nggsave(final_plot, f\"plot-{THEME}.html\", path=\".\")\n"}