{"spec_id":"scatter-regression-polynomial","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nscatter-regression-polynomial: Scatter Plot with Polynomial Regression\nLibrary: plotly 6.9.0 | Python 3.13.14\nQuality: 91/100 | Updated: 2026-08-11\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\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\"\nGRID = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Imprint palette - first series is always #009E73\nBRAND = \"#009E73\"\nACCENT = \"#C475FD\"\n\n# Data - Temperature vs Energy Consumption (environmental/building efficiency)\nnp.random.seed(42)\n# Simulate heating/cooling season data where energy consumption follows a U-shaped curve\n# (more energy needed for both heating in winter and cooling in summer)\noutdoor_temp = np.linspace(-10, 40, 100)\nbase_consumption = 30\nenergy_consumption = 0.12 * (outdoor_temp - 15) ** 2 + base_consumption + np.random.normal(0, 5, len(outdoor_temp))\n\n# Polynomial regression (degree 2 - quadratic, capturing the U-shaped curve)\ncoeffs = np.polyfit(outdoor_temp, energy_consumption, 2)\npoly = np.poly1d(coeffs)\nx_fit = np.linspace(outdoor_temp.min(), outdoor_temp.max(), 200)\ny_fit = poly(x_fit)\n\n# Calculate R²\ny_pred = poly(outdoor_temp)\nresiduals = energy_consumption - y_pred\nss_res = np.sum(residuals**2)\nss_tot = np.sum((energy_consumption - np.mean(energy_consumption)) ** 2)\nr_squared = 1 - (ss_res / ss_tot)\n\n# 95% confidence band around the fit, from the residual spread\nresidual_std = np.std(residuals)\ny_upper = y_fit + 1.96 * residual_std\ny_lower = y_fit - 1.96 * residual_std\n\n# Format polynomial equation with explicit sign handling (avoids \"+ -3.63x\")\na, b, c = coeffs\nsign_b = \"-\" if b < 0 else \"+\"\nsign_c = \"-\" if c < 0 else \"+\"\nequation = f\"y = {a:.4f}x² {sign_b} {abs(b):.2f}x {sign_c} {abs(c):.1f}\"\n\n# Curve vertex - the \"balance point\" temperature where energy use is minimized\nvertex_x = -b / (2 * a)\nvertex_y = poly(vertex_x)\n\n# Create figure\nfig = go.Figure()\n\n# Confidence band (drawn first so it sits behind the scatter and fit line)\nfig.add_trace(\n    go.Scatter(\n        x=np.concatenate([x_fit, x_fit[::-1]]),\n        y=np.concatenate([y_upper, y_lower[::-1]]),\n        fill=\"toself\",\n        fillcolor=\"rgba(196, 117, 253, 0.15)\",\n        line={\"width\": 0},\n        hoverinfo=\"skip\",\n        showlegend=False,\n        name=\"95% Confidence Band\",\n    )\n)\n\n# Scatter points with brand color\nfig.add_trace(\n    go.Scatter(\n        x=outdoor_temp,\n        y=energy_consumption,\n        mode=\"markers\",\n        name=\"Measured Data\",\n        marker={\"size\": 10, \"color\": BRAND, \"opacity\": 0.6, \"line\": {\"width\": 1, \"color\": PAGE_BG}},\n        hovertemplate=\"%{x:.1f}°C, %{y:.1f} kWh/day<extra></extra>\",\n    )\n)\n\n# Polynomial regression curve\nfig.add_trace(\n    go.Scatter(\n        x=x_fit,\n        y=y_fit,\n        mode=\"lines\",\n        name=\"Polynomial Fit (degree 2)\",\n        line={\"color\": ACCENT, \"width\": 3.5},\n        hovertemplate=\"Fit: %{x:.1f}°C, %{y:.1f} kWh/day<extra></extra>\",\n    )\n)\n\n# Highlight the curve's minimum - the real-world \"balance point\" insight\nfig.add_trace(\n    go.Scatter(\n        x=[vertex_x],\n        y=[vertex_y],\n        mode=\"markers\",\n        name=\"Balance Point\",\n        showlegend=False,\n        marker={\"size\": 13, \"symbol\": \"diamond\", \"color\": INK, \"line\": {\"width\": 2, \"color\": ACCENT}},\n        hovertemplate=f\"Balance point: {vertex_x:.1f}°C, {vertex_y:.1f} kWh/day<extra></extra>\",\n    )\n)\n\n# Title fontsize scaled from the 16px/67-char baseline\ntitle_text = \"Energy vs. Temperature: Quadratic Regression · scatter-regression-polynomial · plotly · anyplot.ai\"\ntitle_fontsize = max(round(16 * 67 / len(title_text)), 11)\n\n# Layout with theme-adaptive chrome\nfig.update_layout(\n    autosize=False,\n    title={\"text\": title_text, \"font\": {\"size\": title_fontsize, \"color\": INK}, \"x\": 0.5, \"xanchor\": \"center\"},\n    xaxis={\n        \"title\": {\"text\": \"Outdoor Temperature (°C)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": True,\n        \"gridwidth\": 1,\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Energy Consumption (kWh/day)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": True,\n        \"gridwidth\": 1,\n        \"gridcolor\": GRID,\n        \"linecolor\": INK_SOFT,\n        \"zeroline\": False,\n    },\n    legend={\"font\": {\"size\": 10, \"color\": INK_SOFT}, \"x\": 0.02, \"y\": 0.98, \"bgcolor\": ELEVATED_BG, \"borderwidth\": 0},\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    margin={\"l\": 80, \"r\": 40, \"t\": 80, \"b\": 60},\n    annotations=[\n        {\n            \"x\": vertex_x,\n            \"y\": vertex_y,\n            \"xref\": \"x\",\n            \"yref\": \"y\",\n            \"text\": \"Balance point\",\n            \"showarrow\": True,\n            \"arrowhead\": 2,\n            \"arrowcolor\": INK_SOFT,\n            \"ax\": 0,\n            \"ay\": -36,\n            \"font\": {\"size\": 10, \"color\": INK},\n            \"bgcolor\": ELEVATED_BG,\n            \"borderwidth\": 0,\n            \"borderpad\": 4,\n        },\n        {\n            \"x\": 0.98,\n            \"y\": 0.05,\n            \"xref\": \"paper\",\n            \"yref\": \"paper\",\n            \"text\": f\"R² = {r_squared:.4f}<br>{equation}\",\n            \"showarrow\": False,\n            \"font\": {\"size\": 11, \"color\": INK},\n            \"bgcolor\": ELEVATED_BG,\n            \"borderwidth\": 0,\n            \"borderpad\": 10,\n            \"xanchor\": \"right\",\n            \"yanchor\": \"bottom\",\n        },\n    ],\n)\n\n# Save as PNG and HTML with theme-suffixed filenames — canonical 3200×1800 canvas\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}