# Predict on the Link Scale

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.


``` python
# 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.


``` python
# 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.


``` python
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.


# Manual back-transform

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


``` python
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.


# 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).


``` python
# 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.


``` python
# 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


``` python
# 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)
```


    array([0.2839, 0.6631])


Negative log-odds correspond to probabilities below 0.5. Positive log-odds correspond to probabilities above 0.5.


# Response scale gives probabilities


``` python
probs = bin_model.predict(new_obs)
probs.values.round(4)
```


    array([0.5705, 0.66  ])


Apply the logistic function manually to confirm the relationship.


``` python
(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(-η))`.


# 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"`.
