import whittaker as wk
data = wk.load_dataset("wages", as_frame=True)
data.columns.tolist()['age', 'experience', 'wage']
Use the Gamma family whenever your response is strictly positive and right-skewed (income, insurance claims, reaction times, or wages are classic examples). The log link means each covariate has a multiplicative effect on the expected response. Unlike a log-transformed Gaussian, the Gamma model preserves the original scale and gives well-calibrated prediction intervals. The smooth terms s(age) and s(experience) let the data determine the shape of each relationship.
Load the built-in wages dataset and inspect its columns.
['age', 'experience', 'wage']
The wages dataset has three columns: age, experience, and wage.
| age | experience | wage | |
|---|---|---|---|
| 0 | 62.323637 | 9.216817 | 80.231223 |
| 1 | 42.032395 | 2.487841 | 40.024189 |
| 2 | 63.883454 | 7.251912 | 60.292852 |
| 3 | 21.799293 | 3.799293 | 20.232940 |
| 4 | 46.545724 | 1.738119 | 62.383214 |
Look at the distribution of wage. It is bounded below at zero and has a long right tail, which is exactly the shape the Gamma family is designed for.
Fit a Gamma GAM with REML smoothness selection. REML is generally preferred for Gamma models because it is less prone to under-smoothing than GCV.
# Fit Gamma GAM with REML selection
model = wk.GAM(
"wage ~ s(age) + s(experience)",
family=wk.Gamma(),
).fit(data, method="REML")
model.summary()GAM fit summary
============================================================
Formula: wage ~ s(age) + s(experience)
Family: Gamma(link='log')
Inference: REML
Observations: 800
Coefficients: 19
Parametric coefficients:
Term Estimate Std.Err t value p-value
------------------------ ---------- ---------- ---------- ----------
(Intercept) 3.7108 0.0084 440.791 < 1e-16
Approximate significance of smooth terms:
Term EDF Ref.df Chi.sq p-value
------------------------ ------ ------ ---------- ----------
s(age) 4.41 5 2481.673 < 1e-16
s(experience) 3.44 4 176.897 < 1e-16
Total EDF: 8.85
Scale est: 0.056696
Deviance: 44.8550
Null dev: 205.9584
Dev. expl: 78.2%
GCV score: 0.057331
AIC: 5883.06
BIC: 5924.54
Check the deviance explained and the EDF for each smooth. A high EDF for s(experience) would indicate a strongly non-linear wage-experience profile.
Each panel shows how wage changes with that covariate on the log (link) scale, holding the other covariate at its mean. The shaded band is the 95% confidence interval. A roughly linear partial effect means a log-linear relationship; curvature indicates genuine non-linearity.
For a well-fitting Gamma model, the Q-Q plot should follow the diagonal and the residual-vs-fitted plot should show no obvious funnel pattern.
Construct a small grid of new covariate values and obtain response-scale predictions with confidence intervals.
import pandas as pd
# Build new-data over age-experience pairs
new_data = pd.DataFrame({
"age": [25, 35, 45],
"experience": [2, 10, 20],
})
# Predict wages with confidence intervals
preds = model.predict(new_data, interval="confidence")
# Assemble results with prediction bounds
pd.DataFrame({
"age": new_data["age"],
"experience": new_data["experience"],
"predicted_wage": preds.values,
"lower": preds.lower,
"upper": preds.upper,
})| age | experience | predicted_wage | lower | upper | |
|---|---|---|---|---|---|
| 0 | 25 | 2 | 19.962330 | 19.201127 | 20.753709 |
| 1 | 35 | 10 | 39.925695 | 38.243158 | 41.682256 |
| 2 | 45 | 20 | 64.009630 | 59.996830 | 68.290819 |
Predictions are automatically back-transformed from the log scale to the original wage scale, so they are directly interpretable as expected wages in the same units as the response.