{"spec_id":"residual-plot","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nresidual-plot: Residual Plot\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\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\"\nBRAND = \"#009E73\"  # Okabe-Ito position 1\nACCENT_RED = \"#C475FD\"  # Okabe-Ito position 2 for trend/outliers\n\n# Data - manufacturing quality control (wafer defect prediction)\nnp.random.seed(42)\nn_samples = 150\n\n# Feature: process temperature (°C)\ntemperature = np.random.uniform(700, 1100, n_samples)\n\n# True relationship: exponential relationship (will show non-linearity in residuals)\ny_true = 1000 + 0.5 * temperature + 0.0005 * temperature**2 + np.random.normal(0, 150, n_samples)\n\n# Fit linear model (will miss the quadratic component)\nslope, intercept = np.polyfit(temperature, y_true, 1)\ny_pred = slope * temperature + intercept\n\n# Calculate residuals\nresiduals = y_true - y_pred\n\n# Identify outliers (beyond 2 standard deviations)\nresidual_std = np.std(residuals)\noutlier_mask = np.abs(residuals) > 2 * residual_std\n\n# Create DataFrame for seaborn\ndf = pd.DataFrame(\n    {\"Fitted Values\": y_pred, \"Residuals\": residuals, \"Type\": np.where(outlier_mask, \"Outlier (|z| > 2)\", \"Normal\")}\n)\n\n# Configure seaborn theme\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Create plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Plot using seaborn scatterplot with hue for outliers\nsns.scatterplot(\n    data=df,\n    x=\"Fitted Values\",\n    y=\"Residuals\",\n    hue=\"Type\",\n    palette={\"Normal\": BRAND, \"Outlier (|z| > 2)\": ACCENT_RED},\n    s=180,\n    alpha=0.75,\n    ax=ax,\n)\n\n# Add polynomial trend line (order=2) to show non-linear pattern\nsns.regplot(\n    data=df,\n    x=\"Fitted Values\",\n    y=\"Residuals\",\n    scatter=False,\n    order=2,\n    line_kws={\"color\": INK_SOFT, \"linewidth\": 2.5, \"linestyle\": \"--\", \"alpha\": 0.6, \"label\": \"Trend (2nd order)\"},\n    ax=ax,\n)\n\n# Reference line at y=0 (perfect prediction)\nax.axhline(y=0, color=INK, linestyle=\"-\", linewidth=2, zorder=2, alpha=0.8)\n\n# Add ±2 standard deviation bands\nax.axhline(y=2 * residual_std, color=INK_SOFT, linestyle=\"--\", linewidth=1.5, alpha=0.5, label=\"±2 SD\")\nax.axhline(y=-2 * residual_std, color=INK_SOFT, linestyle=\"--\", linewidth=1.5, alpha=0.5)\n\n# Styling\nax.set_xlabel(\"Fitted Values (Predicted Defect Count)\", fontsize=20, color=INK)\nax.set_ylabel(\"Residuals (Actual - Predicted)\", fontsize=20, color=INK)\nax.set_title(\"residual-plot · seaborn · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT)\n\n# Remove top and right spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Custom legend with better positioning\nhandles, labels = ax.get_legend_handles_labels()\nax.legend(handles, labels, fontsize=14, loc=\"upper right\", framealpha=0.95, edgecolor=INK_SOFT)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}