Residplot Examples

publiplots.residplot() wraps seaborn.residplot() — the scatter of regression residuals — and extends it with a hue= dimension that seaborn doesn’t natively support. Each hue level gets its own residual computation and a palette-mapped color; a categorical hue legend is auto-rendered via the publiplots legend reactor.

Use residplot to diagnose fit quality: curvature in the residual cloud (or a noticeable LOWESS bow) suggests the current regression order is too low; obvious heteroscedasticity hints that variance isn’t constant across the predictor.

import publiplots as pp
from publiplots.plot.residplot import residplot
import numpy as np
import pandas as pd

Basic Residuals (Linear Fit)

The simplest case: residuals of a linear regression of y on x. The dotted horizontal is the zero reference line (a well-fit model should have residuals centered on it with no visible structure).

rng = np.random.default_rng(0)
n = 180
x = rng.normal(0.0, 1.0, n)
y = 1.2 * x + rng.normal(0.0, 0.5, n)
df = pd.DataFrame({"x": x, "y": y})

ax = residplot(
    data=df, x="x", y="y",
    title="Linear Residuals",
    xlabel="x", ylabel="residual",
)
pp.show()
Linear Residuals

Polynomial Order (order=2)

When the underlying relationship is non-linear, a linear fit leaves systematic curvature in the residuals. Bumping order= to 2 absorbs quadratic structure — the residual cloud should look more centered on zero with no obvious bow.

rng = np.random.default_rng(0)
n = 180
x = rng.uniform(-2.0, 2.0, n)
y = 0.8 * x**2 + 0.3 * x + rng.normal(0.0, 0.4, n)
df_poly = pd.DataFrame({"x": x, "y": y})

ax = residplot(
    data=df_poly, x="x", y="y", order=1,
    title="Order=1 (quadratic signal remains)",
    xlabel="x", ylabel="residual",
)
pp.show()

ax = residplot(
    data=df_poly, x="x", y="y", order=2,
    title="Order=2 (quadratic absorbed)",
    xlabel="x", ylabel="residual",
)
pp.show()
  • Order=1 (quadratic signal remains)
  • Order=2 (quadratic absorbed)

LOWESS Smoother on Residuals

lowess=True fits a nonparametric smoother on top of the residual cloud. A flat LOWESS curve is the goal; a persistent bow is evidence of unmodeled structure. Requires statsmodels (install with pip install publiplots[regression]).

try:
    import statsmodels  # noqa: F401

    ax = residplot(
        data=df_poly, x="x", y="y", order=1, lowess=True,
        title="LOWESS Reveals Missing Quadratic Term",
        xlabel="x", ylabel="residual",
    )
    pp.show()
except ImportError:
    print("statsmodels not installed; skipping LOWESS section")
LOWESS Reveals Missing Quadratic Term

Residuals by Hue (the novelty over sns.residplot)

When hue= is passed, the data is split by level and a separate residual plot is drawn per group, each tinted by the palette. This makes it easy to compare fit quality across conditions side-by-side.

rng = np.random.default_rng(0)
n_per = 60
slopes = {"A": 1.0, "B": 0.5, "C": -0.8}
frames = []
for group, slope in slopes.items():
    xg = rng.normal(0.0, 1.0, n_per)
    yg = slope * xg + rng.normal(0.0, 0.3, n_per)
    frames.append(pd.DataFrame({"x": xg, "y": yg, "treatment": group}))
hue_df = pd.concat(frames, ignore_index=True)

ax = residplot(
    data=hue_df, x="x", y="y", hue="treatment",
    title="Residuals by Treatment",
    xlabel="x", ylabel="residual",
)
pp.show()
Residuals by Treatment

Total running time of the script: (0 minutes 2.113 seconds)

Gallery generated by Sphinx-Gallery