{"spec_id":"scatter-matrix","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nscatter-matrix: Scatter Plot Matrix\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-09\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\n\nfrom lets_plot import *\nfrom lets_plot.export import ggsave\n\nLetsPlot.setup_html()\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\"]\n\n# Data - Iris-like dataset with 4 variables\nnp.random.seed(42)\nn = 150\n\nspecies = np.repeat([\"Setosa\", \"Versicolor\", \"Virginica\"], n // 3)\n\nsepal_length_setosa = np.random.normal(5.0, 0.35, n // 3)\nsepal_width_setosa = np.random.normal(3.4, 0.38, n // 3)\npetal_length_setosa = np.random.normal(1.5, 0.17, n // 3)\npetal_width_setosa = np.random.normal(0.25, 0.1, n // 3)\n\nsepal_length_versicolor = np.random.normal(5.9, 0.52, n // 3)\nsepal_width_versicolor = np.random.normal(2.8, 0.31, n // 3)\npetal_length_versicolor = np.random.normal(4.3, 0.47, n // 3)\npetal_width_versicolor = np.random.normal(1.3, 0.2, n // 3)\n\nsepal_length_virginica = np.random.normal(6.6, 0.64, n // 3)\nsepal_width_virginica = np.random.normal(3.0, 0.32, n // 3)\npetal_length_virginica = np.random.normal(5.5, 0.55, n // 3)\npetal_width_virginica = np.random.normal(2.0, 0.27, n // 3)\n\ndf = pd.DataFrame(\n    {\n        \"Sepal Length (cm)\": np.concatenate([sepal_length_setosa, sepal_length_versicolor, sepal_length_virginica]),\n        \"Sepal Width (cm)\": np.concatenate([sepal_width_setosa, sepal_width_versicolor, sepal_width_virginica]),\n        \"Petal Length (cm)\": np.concatenate([petal_length_setosa, petal_length_versicolor, petal_length_virginica]),\n        \"Petal Width (cm)\": np.concatenate([petal_width_setosa, petal_width_versicolor, petal_width_virginica]),\n        \"Species\": species,\n    }\n)\n\ndf_plot = df.rename(\n    columns={\n        \"Sepal Length (cm)\": \"Sepal Len\",\n        \"Sepal Width (cm)\": \"Sepal Wid\",\n        \"Petal Length (cm)\": \"Petal Len\",\n        \"Petal Width (cm)\": \"Petal Wid\",\n    }\n)\n\nvariables = [\"Sepal Len\", \"Sepal Wid\", \"Petal Len\", \"Petal Wid\"]\nn_vars = len(variables)\n\n# Build list of plots and their regions for ggbunch\nplots = []\nregions = []\n\n# Calculate cell dimensions\nmargin_top = 0.08\nmargin_bottom = 0.08\nmargin_left = 0.02\nmargin_right = 0.02\navailable_height = 1.0 - margin_top - margin_bottom\navailable_width = 1.0 - margin_left - margin_right\ncell_width = available_width / n_vars\ncell_height = available_height / n_vars\n\n# Create plots for the matrix\nfor i, var_y in enumerate(variables):\n    for j, var_x in enumerate(variables):\n        if i == j:\n            # Diagonal: histogram\n            p = (\n                ggplot(df_plot, aes(x=var_x, fill=\"Species\"))\n                + geom_histogram(alpha=0.7, bins=15, position=\"identity\")\n                + scale_fill_manual(values=IMPRINT)\n                + theme_minimal()\n                + theme(\n                    axis_title=element_blank(),\n                    axis_text=element_text(size=14, color=INK_SOFT),\n                    legend_position=\"none\",\n                    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n                    panel_background=element_rect(fill=PAGE_BG),\n                    panel_grid_minor=element_blank(),\n                    panel_grid_major=element_line(color=INK_SOFT, size=0.2),\n                )\n            )\n        else:\n            # Off-diagonal: scatter plot\n            p = (\n                ggplot(df_plot, aes(x=var_x, y=var_y, color=\"Species\"))\n                + geom_point(size=3.5, alpha=0.7)\n                + scale_color_manual(values=IMPRINT)\n                + theme_minimal()\n                + theme(\n                    axis_title=element_blank(),\n                    axis_text=element_text(size=14, color=INK_SOFT),\n                    legend_position=\"none\",\n                    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n                    panel_background=element_rect(fill=PAGE_BG),\n                    panel_grid_minor=element_blank(),\n                    panel_grid_major=element_line(color=INK_SOFT, size=0.2),\n                )\n            )\n\n        # Add variable names on bottom edge (last row)\n        if i == n_vars - 1:\n            p = p + labs(x=var_x) + theme(axis_title_x=element_text(size=18, color=INK))\n\n        # Add variable names on left edge (first column)\n        if j == 0:\n            p = p + labs(y=var_y) + theme(axis_title_y=element_text(size=18, color=INK))\n\n        plots.append(p)\n        x_pos = margin_left + j * cell_width\n        y_pos = margin_top + i * cell_height\n        regions.append((x_pos, y_pos, cell_width, cell_height, 0, 0))\n\n# Create title plot\ntitle_plot = (\n    ggplot()\n    + geom_blank()\n    + ggtitle(\"scatter-matrix · letsplot · anyplot.ai\")\n    + theme_void()\n    + theme(\n        plot_title=element_text(size=32, hjust=0.5, face=\"bold\", color=INK),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    )\n)\nplots.append(title_plot)\nregions.append((0, 0, 1, margin_top, 0, 0))\n\n# Create legend plot\nlegend_df = pd.DataFrame({\"x\": [1, 2, 3], \"y\": [1, 2, 3], \"Species\": [\"Setosa\", \"Versicolor\", \"Virginica\"]})\nlegend_plot = (\n    ggplot(legend_df, aes(x=\"x\", y=\"y\", color=\"Species\"))\n    + geom_point(size=8)\n    + scale_color_manual(values=IMPRINT)\n    + theme_void()\n    + theme(\n        legend_position=\"bottom\",\n        legend_title=element_text(size=20, color=INK),\n        legend_text=element_text(size=18, color=INK_SOFT),\n        legend_direction=\"horizontal\",\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    )\n    + guides(color=guide_legend(override_aes={\"size\": 10}))\n)\nplots.append(legend_plot)\nregions.append((0.2, 1.0 - margin_bottom, 0.6, margin_bottom, 0, 0))\n\n# Combine into ggbunch with square format\ncombined = ggbunch(plots, regions) + ggsize(1200, 1200)\n\n# Save with scale for high resolution (target ~3600x3600)\nggsave(combined, filename=f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(combined, filename=f\"plot-{THEME}.html\", path=\".\")\n"}