Skip to content

Utilities API

The utils module provides utility functions for plotting and visualization of P-spline results.

Functions

Plot data and P-spline fit, subsampling for large datasets (inspired by Figure 2.9, Page 29).

Parameters: - pspline: PSpline object - title: Plot title - subsample: Number of points to plot (default: 1000)

Source code in src/psplines/utils.py
def plot_fit(pspline: PSpline, title: str = "P-spline Fit", subsample: int = 1000) -> None:
    """
    Plot data and P-spline fit, subsampling for large datasets (inspired by Figure 2.9, Page 29).

    Parameters:
    - pspline: PSpline object
    - title: Plot title
    - subsample: Number of points to plot (default: 1000)
    """
    n = len(pspline.x)
    if n > subsample:
        idx = np.random.choice(n, subsample, replace=False)
        idx = np.sort(idx)
        x_plot = pspline.x[idx]
        y_plot = pspline.y[idx]
        fit_plot = pspline.fitted_values[idx]
    else:
        idx = np.arange(n)
        x_plot = pspline.x
        y_plot = pspline.y
        fit_plot = pspline.fitted_values

    plt.scatter(x_plot, y_plot, c="grey", s=1, label="Data")
    plt.plot(x_plot, fit_plot, c="blue", lw=2, label="Fit")
    plt.xlabel("x")
    plt.ylabel("y")
    plt.title(title)
    plt.legend()
    plt.grid(True)
    plt.show()

Usage Examples

Basic Plotting

import numpy as np
import matplotlib.pyplot as plt
from psplines import PSpline
from psplines.utils import plot_fit

# Create and fit spline
x = np.linspace(0, 2*np.pi, 50)
y = np.sin(x) + 0.1 * np.random.randn(50)
spline = PSpline(x, y, nseg=15, lambda_=1.0)
spline.fit()

# Create basic plot
fig, ax = plt.subplots(figsize=(10, 6))
plot_fit(spline, ax=ax)
plt.show()

Customized Plotting

# Plot with uncertainty and custom styling
x_new = np.linspace(0, 2*np.pi, 200)
fig, ax = plt.subplots(figsize=(12, 8))

plot_fit(spline, x_new=x_new, show_se=True, ax=ax,
         data_kws={'alpha': 0.6, 'color': 'darkblue'},
         fit_kws={'color': 'red', 'linewidth': 2},
         se_kws={'alpha': 0.2, 'color': 'red'})

ax.set_title('P-Spline Fit with Uncertainty')
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()

Multiple Subplots

# Create comprehensive diagnostic plots
fig, axes = plt.subplots(2, 2, figsize=(15, 10))

# Main fit plot
plot_fit(spline, ax=axes[0, 0], show_se=True)
axes[0, 0].set_title('Data and Fit')

# Residuals
residuals = spline.y - spline.fitted_values
axes[0, 1].scatter(spline.fitted_values, residuals, alpha=0.6)
axes[0, 1].axhline(0, color='red', linestyle='--')
axes[0, 1].set_title('Residuals vs Fitted')
axes[0, 1].set_xlabel('Fitted Values')
axes[0, 1].set_ylabel('Residuals')

# First derivative
dy_dx = spline.derivative(x_new, deriv_order=1)
axes[1, 0].plot(x_new, dy_dx, 'g-', linewidth=2)
axes[1, 0].set_title('First Derivative')
axes[1, 0].set_xlabel('x')
axes[1, 0].set_ylabel('dy/dx')

# Second derivative
d2y_dx2 = spline.derivative(x_new, deriv_order=2)
axes[1, 1].plot(x_new, d2y_dx2, 'm-', linewidth=2)
axes[1, 1].set_title('Second Derivative')
axes[1, 1].set_xlabel('x')
axes[1, 1].set_ylabel('d²y/dx²')

plt.tight_layout()
plt.show()

Mathematical Visualization Utilities

While not part of the core API, here are some useful functions for understanding P-splines:

Basis Function Visualization

import numpy as np
import matplotlib.pyplot as plt
from psplines.basis import b_spline_basis

def plot_basis_functions(xl=0, xr=1, nseg=5, degree=3):
    """Plot individual B-spline basis functions."""
    x = np.linspace(xl, xr, 200)
    B, knots = b_spline_basis(x, xl, xr, nseg, degree)

    plt.figure(figsize=(12, 6))
    for i in range(B.shape[1]):
        plt.plot(x, B[:, i].toarray().flatten(), 
                 alpha=0.7, label=f'B_{i}')

    # Mark knots
    interior_knots = knots[degree:-degree]
    plt.vlines(interior_knots, 0, 1, colors='red', 
               linestyles='dashed', alpha=0.5)

    plt.title(f'B-spline Basis Functions (degree={degree}, nseg={nseg})')
    plt.xlabel('x')
    plt.ylabel('Basis Function Value')
    plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
    plt.tight_layout()
    plt.show()

# Example usage
plot_basis_functions(nseg=5, degree=3)

Penalty Matrix Visualization

import matplotlib.pyplot as plt
from psplines.penalty import difference_matrix

def plot_penalty_matrix(n=10, order=2):
    """Visualize the penalty matrix structure."""
    D = difference_matrix(n, order)

    fig, axes = plt.subplots(1, 2, figsize=(12, 5))

    # Penalty matrix D
    axes[0].spy(D, markersize=8)
    axes[0].set_title(f'Difference Matrix D_{order} ({D.shape[0]}×{D.shape[1]})')
    axes[0].set_xlabel('Column')
    axes[0].set_ylabel('Row')

    # Penalty matrix D^T D
    DtD = D.T @ D
    im = axes[1].imshow(DtD.toarray(), cmap='Blues')
    axes[1].set_title(f'Penalty Matrix D_{order}^T D_{order} ({DtD.shape[0]}×{DtD.shape[1]})')
    axes[1].set_xlabel('Column')
    axes[1].set_ylabel('Row')
    plt.colorbar(im, ax=axes[1])

    plt.tight_layout()
    plt.show()

# Example usage
plot_penalty_matrix(n=10, order=2)

Smoothing Parameter Effect Visualization

def plot_lambda_effect(spline, lambdas=None):
    """Show effect of different smoothing parameters."""
    if lambdas is None:
        lambdas = np.logspace(-2, 2, 5)

    x_plot = np.linspace(spline.x.min(), spline.x.max(), 200)

    plt.figure(figsize=(12, 8))
    colors = plt.cm.viridis(np.linspace(0, 1, len(lambdas)))

    for i, lam in enumerate(lambdas):
        # Create temporary spline with this lambda
        temp_spline = PSpline(spline.x, spline.y, 
                             nseg=spline.nseg, lambda_=lam)
        temp_spline.fit()
        y_fit = temp_spline.predict(x_plot)

        plt.plot(x_plot, y_fit, color=colors[i], 
                 linewidth=2, label=f'λ = {lam:.3f}')

    plt.scatter(spline.x, spline.y, alpha=0.6, 
                color='black', s=30, label='Data')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('Effect of Smoothing Parameter λ')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

# Example usage (assuming you have a fitted spline)
# plot_lambda_effect(spline)

Plotting Best Practices

Figure Styling

# Use consistent styling across plots
plt.rcParams.update({
    'font.size': 12,
    'axes.labelsize': 12,
    'axes.titlesize': 14,
    'legend.fontsize': 10,
    'figure.dpi': 100,
    'savefig.dpi': 300,
    'savefig.bbox': 'tight'
})

Color Schemes

  • Data points: Use muted colors with transparency
  • Fit line: Use bright, contrasting colors
  • Uncertainty bands: Use same color as fit with low alpha
  • Derivatives: Use distinct colors (green, magenta, etc.)

Layout Recommendations

  • Single plot: 10×6 inches for papers, 12×8 for presentations
  • Subplot grids: 15×10 inches for 2×2 grids
  • Always include axis labels and titles
  • Use tight_layout() to avoid overlapping elements
  • Save as PNG (300 DPI) for publications, PDF for vector graphics