{"spec_id":"contour-density","library":"altair","language":"python","code":"\"\"\" anyplot.ai\ncontour-density: Density Contour Plot\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-16\n\"\"\"\n\nimport os\nimport sys\nfrom pathlib import Path\n\n\nscript_dir = str(Path(__file__).parent)\nwhile script_dir in sys.path:\n    sys.path.remove(script_dir)\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom scipy.stats import gaussian_kde\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# Data - climate measurements showing natural clusters\nnp.random.seed(42)\n\n# Create three distinct clusters representing different climate conditions\nn1 = 150\ntemp1 = np.random.normal(15, 4, n1)\nhumidity1 = np.random.normal(30, 8, n1)\n\nn2 = 200\ntemp2 = np.random.normal(25, 5, n2)\nhumidity2 = np.random.normal(55, 10, n2)\n\nn3 = 150\ntemp3 = np.random.normal(38, 4, n3)\nhumidity3 = np.random.normal(75, 8, n3)\n\n# Combine data\ntemperature = np.concatenate([temp1, temp2, temp3])\nhumidity = np.concatenate([humidity1, humidity2, humidity3])\n\n# Compute 2D KDE for density estimation\nxy = np.vstack([temperature, humidity])\nkde = gaussian_kde(xy)\n\n# Create grid for density estimation\nn_grid = 80\nx_grid = np.linspace(temperature.min() - 5, temperature.max() + 5, n_grid)\ny_grid = np.linspace(humidity.min() - 5, humidity.max() + 5, n_grid)\nxx, yy = np.meshgrid(x_grid, y_grid)\npositions = np.vstack([xx.ravel(), yy.ravel()])\nz = kde(positions).reshape(xx.shape)\n\n# Prepare grid data for heatmap\ngrid_data = pd.DataFrame({\"x\": xx.ravel(), \"y\": yy.ravel(), \"density\": z.ravel()})\n\n# Create filled contour visualization using heatmap\nx_domain = [float(temperature.min() - 6), float(temperature.max() + 6)]\ny_domain = [float(humidity.min() - 6), float(humidity.max() + 6)]\n\nchart = (\n    alt.Chart(grid_data)\n    .mark_rect()\n    .encode(\n        x=alt.X(\n            \"x:Q\",\n            bin=alt.Bin(step=(x_grid[1] - x_grid[0])),\n            scale=alt.Scale(domain=x_domain),\n            title=\"Temperature (°C)\",\n            axis=alt.Axis(labelFontSize=18, titleFontSize=22, grid=False, labelColor=INK_SOFT, titleColor=INK),\n        ),\n        y=alt.Y(\n            \"y:Q\",\n            bin=alt.Bin(step=(y_grid[1] - y_grid[0])),\n            scale=alt.Scale(domain=y_domain),\n            title=\"Humidity (%)\",\n            axis=alt.Axis(labelFontSize=18, titleFontSize=22, grid=False, labelColor=INK_SOFT, titleColor=INK),\n        ),\n        color=alt.Color(\n            \"mean(density):Q\",\n            scale=alt.Scale(scheme=\"viridis\"),\n            title=\"Density\",\n            legend=alt.Legend(\n                titleFontSize=20,\n                labelFontSize=16,\n                gradientLength=400,\n                gradientThickness=25,\n                titleColor=INK,\n                labelColor=INK_SOFT,\n                fillColor=PAGE_BG,\n                strokeColor=INK_SOFT,\n            ),\n        ),\n    )\n    .properties(\n        width=1600,\n        height=900,\n        title=alt.Title(text=\"contour-density · altair · anyplot.ai\", fontSize=28, anchor=\"middle\", color=INK),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None, strokeWidth=0)\n    .configure_axis(domainColor=INK_SOFT, gridOpacity=0.0)\n)\n\n# Save outputs\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}