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).
Link-scale predictions
Pass type="link" to get the log-scale linear predictor.
link = model.predict(grid, type="link")
link.values.round(4)
array([1.475 , 2.4236, 2.3321])
These are log expected counts. Negative values are valid and simply represent counts below 1.
Confidence intervals on the link scale
Requesting interval="confidence" with type="link" gives symmetric intervals on the log scale. Back-transforming these produces asymmetric intervals on the count scale, which is preferable to computing symmetric intervals directly on counts (which can yield negative lower bounds).
# Predict with link-scale confidence intervals
link_ci = model.predict(grid, type="link", interval="confidence")
# Back-transform bounds to count scale
count_lower = np.exp(link_ci.lower).round(4)
count_upper = np.exp(link_ci.upper).round(4)
np.column_stack([count_lower, count_upper])
array([[ 3.702 , 5.1611],
[10.1946, 12.4943],
[ 9.2949, 11.4117]])
Both bounds are positive, even when the fitted count is small.
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
Link scale gives log-odds
# Define new observations and predict on log-odds scale
new_obs = {"income": np.array([40.0, 80.0]), "debt_ratio": np.array([0.3, 0.6]), "age": np.array([30.0, 50.0])}
log_odds = bin_model.predict(new_obs, type="link")
log_odds.values.round(4)
Negative log-odds correspond to probabilities below 0.5. Positive log-odds correspond to probabilities above 0.5.
Response scale gives probabilities
probs = bin_model.predict(new_obs)
probs.values.round(4)
Apply the logistic function manually to confirm the relationship.
(1 / (1 + np.exp(-log_odds.values))).round(4)
The values match probs.values. The logit link means μ = 1 / (1 + exp(-η)).
When to use link-scale predictions
- Custom back-transforms: apply a non-standard transformation after retrieving η.
- Interval construction: compute symmetric intervals on the link scale, then back-transform to avoid impossible values (negative counts, probabilities outside [0, 1]).
- Model combination: average or stack linear predictors from multiple models before back-transforming.
- Additive interpretation: report the contribution of each smooth on a common additive scale using
type="terms".