{"spec_id":"residual-plot","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nresidual-plot: Residual Plot\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 86/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_point,\n    geom_smooth,\n    ggplot,\n    labs,\n    scale_color_manual,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme tokens (see prompts/default-style-guide.md)\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\"\nRULE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Okabe-Ito palette\nIMPRINT = [\n    \"#009E73\",  # Brand green (normal points)\n    \"#C475FD\",  # Vermillion (outliers)\n    \"#4467A3\",  # Blue (LOWESS line)\n]\n\n# Data - Generate realistic regression data with better labels\nnp.random.seed(42)\nn_points = 150\n\n# House prices regression example\nhouse_size = np.linspace(1000, 5000, n_points)  # Square feet\nprice_true = 150 * house_size + 50000 + np.random.normal(0, 30000, n_points)\n\n# Add a few outliers (priced unusually high or low for their size)\noutlier_indices = [20, 75, 130]\nprice_true[outlier_indices] += np.array([150000, -120000, 100000])\n\n# Fit linear regression using OLS\nsize_mean = np.mean(house_size)\nprice_mean = np.mean(price_true)\nslope = np.sum((house_size - size_mean) * (price_true - price_mean)) / np.sum((house_size - size_mean) ** 2)\nintercept = price_mean - slope * size_mean\nprice_pred = slope * house_size + intercept\n\n# Calculate residuals\nresiduals = price_true - price_pred\n\n# Identify outliers (beyond 2 standard deviations)\nstd_resid = np.std(residuals)\nis_outlier = np.abs(residuals) > 2 * std_resid\npoint_type = np.where(is_outlier, \"Outlier\", \"Normal\")\n\n# Create DataFrame\ndf = pd.DataFrame({\"fitted\": price_pred, \"residuals\": residuals, \"point_type\": point_type})\n\n# Create theme customization\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n    panel_border=element_rect(color=INK_SOFT, fill=None),\n    axis_title=element_text(color=INK, size=20),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    axis_line=element_line(color=INK_SOFT),\n    plot_title=element_text(color=INK, size=24),\n    legend_background=element_rect(fill=PAGE_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=16),\n    legend_title=element_text(color=INK, size=18),\n    figure_size=(16, 9),\n)\n\n# Create residual plot\nplot = (\n    ggplot(df, aes(x=\"fitted\", y=\"residuals\", color=\"point_type\"))\n    + geom_hline(yintercept=0, color=INK_SOFT, size=1.2, linetype=\"solid\", alpha=0.8)\n    + geom_hline(yintercept=2 * std_resid, color=INK_SOFT, size=0.8, linetype=\"dashed\", alpha=0.5)\n    + geom_hline(yintercept=-2 * std_resid, color=INK_SOFT, size=0.8, linetype=\"dashed\", alpha=0.5)\n    + geom_point(size=4, alpha=0.7)\n    + geom_smooth(aes(group=1), method=\"lowess\", color=IMPRINT[2], size=1.5, se=False, span=0.5)\n    + scale_color_manual(values={\"Normal\": IMPRINT[0], \"Outlier\": IMPRINT[1]}, name=\"Point Type\")\n    + labs(x=\"Fitted Values ($)\", y=\"Residuals ($)\", title=\"residual-plot · plotnine · anyplot.ai\")\n    + theme_minimal()\n    + anyplot_theme\n)\n\n# Save\noutput_path = os.path.join(os.path.dirname(__file__), f\"plot-{THEME}.png\")\nplot.save(output_path, dpi=300)\n"}