Predict on the Link Scale

Obtain predictions on the linear predictor scale instead of the response scale, and manually back-transform to confirm the relationship.

By default, predict() returns fitted values on the response scale (counts for Poisson, probabilities for Binomial). Passing type="link" returns predictions on the linear predictor scale instead (log for Poisson, log-odds for Binomial). Link-scale predictions are useful when building custom intervals, combining outputs from multiple models, or communicating on an additive scale where smooth contributions are simply summed.

Poisson example: fish counts

Fit a Poisson GAM on fish count data with two predictors.

# Import libraries
import whittaker as wk
import numpy as np

# Load data and fit Poisson GAM
data = wk.load_dataset("fish")
model = wk.GAM("count ~ s(temperature) + s(depth)", family=wk.Poisson()).fit(data)
model.summary()
GAM fit summary
============================================================
Formula:    count ~ s(temperature) + s(depth)
Family:     Poisson(link='log')
Inference:  GCV
Observations: 300
Coefficients: 19

Parametric coefficients:
  Term                       Estimate    Std.Err    z value    p-value
  ------------------------ ---------- ---------- ---------- ----------
  (Intercept)                  1.5349     0.0289     53.128    < 1e-16

Approximate significance of smooth terms:
  Term                        EDF Ref.df     Chi.sq    p-value
  ------------------------ ------ ------ ---------- ----------
  s(temperature)             4.24      5    294.642    < 1e-16
  s(depth)                   1.74      2    194.880    < 1e-16

Total EDF:  6.98
Scale est:  1.000000
Deviance:   320.5707
Null dev:   843.4578
Dev. expl:  62.0%
GCV score:  1.120093
AIC:        1305.79
BIC:        1331.65

Response-scale predictions (default)

The default predict() returns expected counts.

# Define prediction grid
grid = {"temperature": np.array([10.0, 15.0, 20.0]), "depth": np.array([5.0, 10.0, 15.0])}
resp = model.predict(grid)
resp.values.round(4)
array([ 4.3711, 11.286 , 10.2991])

These are expected fish counts (positive real numbers on the original scale).

Manual back-transform

Apply np.exp() to the link-scale values and confirm they match the response-scale predictions.

np.exp(link.values).round(4)
array([ 4.3711, 11.286 , 10.2991])

The values match resp.values exactly. For a Poisson model with a log link, exp(η) == μ where η is the linear predictor and μ is the expected count.

Binomial example: default probability

Fit a Binomial GAM on the credit dataset.

# Load credit data and fit Binomial GAM
credit = wk.load_dataset("credit")
bin_model = wk.GAM("default ~ s(income) + s(debt_ratio) + s(age)", family=wk.Binomial()).fit(credit)
bin_model.summary()
GAM fit summary
============================================================
Formula:    default ~ s(income) + s(debt_ratio) + s(age)
Family:     Binomial(link='logit')
Inference:  GCV
Observations: 1000
Coefficients: 28

Parametric coefficients:
  Term                       Estimate    Std.Err    z value    p-value
  ------------------------ ---------- ---------- ---------- ----------
  (Intercept)                 -0.3581     0.0723     -4.953  7.309e-07

Approximate significance of smooth terms:
  Term                        EDF Ref.df     Chi.sq    p-value
  ------------------------ ------ ------ ---------- ----------
  s(income)                  3.00      4    114.205    < 1e-16
  s(debt_ratio)              1.82      2     98.659    < 1e-16
  s(age)                     2.26      3      5.508     0.1381

Total EDF:  8.08
Scale est:  1.000000
Deviance:   1137.1597
Null dev:   1364.9020
Dev. expl:  16.7%
GCV score:  1.155769
AIC:        1153.33
BIC:        1193.00

Response scale gives probabilities

probs = bin_model.predict(new_obs)
probs.values.round(4)
array([0.5705, 0.66  ])

Apply the logistic function manually to confirm the relationship.

(1 / (1 + np.exp(-log_odds.values))).round(4)
array([0.5705, 0.66  ])

The values match probs.values. The logit link means μ = 1 / (1 + exp(-η)).