{"spec_id":"scatter-matrix","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nscatter-matrix: Scatter Plot Matrix\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 82/100 | Updated: 2026-05-09\n\"\"\"\n\nimport os\nimport sys\n\n\n# Handle module shadowing by removing current directory from path\nsys.path = [p for p in sys.path if os.path.abspath(p) != os.getcwd()]\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\n# Theme tokens\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 always #009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\n\n# Data - Iris-like dataset with 4 variables and 3 species\nnp.random.seed(42)\n\nn_per_species = 50\ndata = []\n\n# Setosa - smaller flowers\nfor _ in range(n_per_species):\n    data.append(\n        {\n            \"Sepal Length (cm)\": np.random.normal(5.0, 0.35),\n            \"Sepal Width (cm)\": np.random.normal(3.4, 0.38),\n            \"Petal Length (cm)\": np.random.normal(1.5, 0.17),\n            \"Petal Width (cm)\": np.random.normal(0.25, 0.1),\n            \"Species\": \"Setosa\",\n        }\n    )\n\n# Versicolor - medium flowers\nfor _ in range(n_per_species):\n    data.append(\n        {\n            \"Sepal Length (cm)\": np.random.normal(5.9, 0.52),\n            \"Sepal Width (cm)\": np.random.normal(2.8, 0.31),\n            \"Petal Length (cm)\": np.random.normal(4.3, 0.47),\n            \"Petal Width (cm)\": np.random.normal(1.3, 0.2),\n            \"Species\": \"Versicolor\",\n        }\n    )\n\n# Virginica - larger flowers\nfor _ in range(n_per_species):\n    data.append(\n        {\n            \"Sepal Length (cm)\": np.random.normal(6.6, 0.64),\n            \"Sepal Width (cm)\": np.random.normal(3.0, 0.32),\n            \"Petal Length (cm)\": np.random.normal(5.5, 0.55),\n            \"Petal Width (cm)\": np.random.normal(2.0, 0.27),\n            \"Species\": \"Virginica\",\n        }\n    )\n\ndf = pd.DataFrame(data)\n\n# Variables for the scatter matrix\nvariables = [\"Sepal Length (cm)\", \"Sepal Width (cm)\", \"Petal Length (cm)\", \"Petal Width (cm)\"]\n\n# Color scale using Okabe-Ito palette\ncolor_scale = alt.Scale(domain=[\"Setosa\", \"Versicolor\", \"Virginica\"], range=IMPRINT)\n\n# Build scatter matrix grid with histograms on diagonal\ncharts = []\n\nfor row_var in variables:\n    row_charts = []\n    for col_var in variables:\n        if row_var == col_var:\n            # Diagonal: histogram\n            hist = (\n                alt.Chart(df)\n                .mark_bar(opacity=0.8)\n                .encode(\n                    alt.X(f\"{col_var}:Q\", bin=alt.Bin(maxbins=20), axis=alt.Axis(labelFontSize=14, titleFontSize=18)),\n                    alt.Y(\"count():Q\", axis=alt.Axis(labelFontSize=14, titleFontSize=18)),\n                    alt.Color(\"Species:N\", scale=color_scale, legend=None),\n                )\n                .properties(width=280, height=280)\n            )\n            row_charts.append(hist)\n        else:\n            # Off-diagonal: scatter plot\n            scatter = (\n                alt.Chart(df)\n                .mark_circle(size=120, opacity=0.7)\n                .encode(\n                    alt.X(f\"{col_var}:Q\", axis=alt.Axis(labelFontSize=14, titleFontSize=18)),\n                    alt.Y(f\"{row_var}:Q\", axis=alt.Axis(labelFontSize=14, titleFontSize=18)),\n                    alt.Color(\"Species:N\", scale=color_scale, legend=None),\n                    tooltip=[\n                        \"Species:N\",\n                        alt.Tooltip(f\"{col_var}:Q\", format=\".2f\"),\n                        alt.Tooltip(f\"{row_var}:Q\", format=\".2f\"),\n                    ],\n                )\n                .properties(width=280, height=280)\n            )\n            row_charts.append(scatter)\n\n    charts.append(alt.hconcat(*row_charts))\n\n# Combine rows\nscatter_matrix = alt.vconcat(*charts)\n\n# Add legend\nlegend = (\n    alt.Chart(df)\n    .mark_point(size=120)\n    .encode(\n        alt.Color(\n            \"Species:N\",\n            scale=color_scale,\n            legend=alt.Legend(\n                title=\"Species\",\n                titleFontSize=24,\n                labelFontSize=20,\n                symbolSize=350,\n                orient=\"right\",\n                titlePadding=15,\n                labelPadding=12,\n            ),\n        )\n    )\n    .properties(width=100, height=280)\n)\n\n# Combine scatter matrix with legend\nfinal_chart = alt.hconcat(scatter_matrix, legend)\n\n# Apply theme-adaptive styling and title\nchart = (\n    final_chart.properties(\n        title=alt.Title(text=\"scatter-matrix · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", offset=20),\n        background=PAGE_BG,\n    )\n    .configure_axis(\n        domainColor=INK_SOFT, tickColor=INK_SOFT, gridColor=INK, gridOpacity=0.10, labelColor=INK_SOFT, titleColor=INK\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT, strokeWidth=0)\n    .configure_title(color=INK, fontSize=28)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save outputs\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}