{"spec_id":"scatter-marginal","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nscatter-marginal: Scatter Plot with Marginal Distributions\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-09\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    coord_flip,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_histogram,\n    geom_point,\n    ggplot,\n    labs,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\nfrom plotnine.composition import plot_spacer\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Okabe-Ito palette\nSCATTER_COLOR = \"#009E73\"  # Brand green (position 1)\nMARGINAL_COLOR = \"#C475FD\"  # Vermillion (position 2)\n\n# Data - Bivariate data with correlation\nnp.random.seed(42)\nn = 200\nstudy_hours = np.random.normal(25, 8, n)\nstudy_hours = np.clip(study_hours, 5, 45)\nexam_score = 35 + 1.5 * study_hours + np.random.normal(0, 8, n)\nexam_score = np.clip(exam_score, 30, 100)\ndf = pd.DataFrame({\"study_hours\": study_hours, \"exam_score\": exam_score})\n\n# Layout dimensions for 4800x2700 output\nmain_w, main_h = 12, 6.5\nmarg_w, marg_h = 4, 2.5\n\n# Shared theme - L-shaped spine (left + bottom only)\nbase_theme = theme_minimal() + 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, size=0.3, alpha=0.10),\n    panel_grid_minor=element_blank(),\n    panel_border=element_blank(),\n    axis_title=element_text(color=INK, size=20, weight=\"bold\"),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    axis_ticks=element_line(color=INK_SOFT, size=0.4),\n    plot_title=element_text(color=INK, size=24, weight=\"bold\", margin={\"t\": 10, \"b\": 10}),\n)\n\n# Top histogram (x distribution)\ntop_hist = (\n    ggplot(df, aes(x=\"study_hours\"))\n    + geom_histogram(bins=15, fill=MARGINAL_COLOR, color=INK_SOFT, alpha=0.7, size=0.3)\n    + scale_x_continuous(limits=(0, 50))\n    + labs(x=\"\", y=\"\", title=\"scatter-marginal · plotnine · anyplot.ai\")\n    + base_theme\n    + theme(\n        figure_size=(main_w, marg_h),\n        axis_text_x=element_blank(),\n        axis_ticks_major_x=element_blank(),\n        axis_ticks_minor_x=element_blank(),\n        axis_line_x=element_blank(),\n        axis_title_y=element_blank(),\n        axis_text_y=element_blank(),\n        axis_ticks_major_y=element_blank(),\n        axis_ticks_minor_y=element_blank(),\n        axis_line_y=element_blank(),\n        panel_grid_major=element_blank(),\n    )\n)\n\n# Right histogram (y distribution)\nright_hist = (\n    ggplot(df, aes(x=\"exam_score\"))\n    + geom_histogram(bins=15, fill=MARGINAL_COLOR, color=INK_SOFT, alpha=0.7, size=0.3)\n    + coord_flip()\n    + scale_x_continuous(limits=(30, 105))\n    + labs(x=\"\", y=\"\")\n    + base_theme\n    + theme(\n        figure_size=(marg_w, main_h),\n        axis_text_y=element_blank(),\n        axis_ticks_major_y=element_blank(),\n        axis_ticks_minor_y=element_blank(),\n        axis_line_y=element_blank(),\n        axis_title_x=element_blank(),\n        axis_text_x=element_blank(),\n        axis_ticks_major_x=element_blank(),\n        axis_ticks_minor_x=element_blank(),\n        axis_line_x=element_blank(),\n        panel_grid_major=element_blank(),\n    )\n)\n\n# Main scatter plot\nscatter_plot = (\n    ggplot(df, aes(x=\"study_hours\", y=\"exam_score\"))\n    + geom_point(size=3.5, alpha=0.6, color=SCATTER_COLOR)\n    + scale_x_continuous(limits=(0, 50))\n    + scale_y_continuous(limits=(30, 105))\n    + labs(x=\"Study Hours per Week\", y=\"Exam Score (%)\")\n    + base_theme\n    + theme(figure_size=(main_w, main_h))\n)\n\n# Spacer\nspacer = plot_spacer() + theme(figure_size=(marg_w, marg_h))\n\n# Compose layout\ncomposed = (top_hist | spacer) / (scatter_plot | right_hist)\n\n# Save\nfig = composed.draw()\nfig.set_size_inches(16, 9)\nfig.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}