# Fit a Monotone Decreasing Smooth

Shape-constrained smoothing is most useful when domain knowledge dictates the direction of an effect. Heavy metals in river floodplains dilute with distance from the source so zinc concentration can only decrease (or stay flat) as you move further from the river, never increase. The `bs="mpd"` basis encodes this prior directly in the model, preventing the smooth from producing implausible upward turns in regions with sparse data.


# Fit

Load the `meuse` dataset, which records zinc concentrations at locations across a Rhine floodplain. The predictor `dist` is the standardized distance from the river bank and `zinc` is the response.


``` python
import whittaker as wk

# Load data and fit monotone decreasing smooth
data = wk.load_dataset("meuse")

model = wk.GAM("zinc ~ s(dist, bs='mpd')").fit(data)
```


# Summarize

The summary confirms the smooth's complexity via its effective degrees of freedom. A monotone constraint restricts the function space, so EDF is often lower than an unconstrained equivalent.


``` python
model.summary()
```


    GAM fit summary
    ============================================================
    Formula:    zinc ~ s(dist, bs='mpd')
    Family:     Gaussian(link='identity')
    Inference:  GCV
    Observations: 155
    Coefficients: 20

    Parametric coefficients:
      Term                       Estimate    Std.Err    t value    p-value
      ------------------------ ---------- ---------- ---------- ----------
      (Intercept)                504.6421    48.4040     10.426    < 1e-16

    Approximate significance of smooth terms:
      Term                        EDF Ref.df     Chi.sq    p-value
      ------------------------ ------ ------ ---------- ----------
      s(dist, bs='mpd')          1.45      2     57.222  3.753e-13

    Total EDF:  2.45
    Scale est:  97965.885389
    Deviance:   14944836.6468
    Null dev:   20607458.7006
    Dev. expl:  27.5%
    GCV score:  99538.309696
    AIC:        2223.64
    BIC:        2231.09


# Visualize the partial effect

`model.plot()` draws the estimated smooth against the predictor. The curve should decrease monotonically from left to right. Near the river bank, zinc is high but further away, it can only be equal or lower.


``` python
model.plot()
```


<style>
  #altair-viz-9ce2c18df33343a2a76f5ae233f2b3c3.vega-embed {
    width: 100%;
    display: flex;
  }

  #altair-viz-9ce2c18df33343a2a76f5ae233f2b3c3.vega-embed details,
  #altair-viz-9ce2c18df33343a2a76f5ae233f2b3c3.vega-embed details summary {
    position: relative;
  }
</style>


# Check diagnostics

Inspect the four diagnostic panels to assess whether residuals are well-behaved. Systematic structure in the residuals vs fitted plot would suggest the smooth is too rigid and an unconstrained basis might fit better.


``` python
wk.check(model)
```


<style>
  #altair-viz-89b0270ba1c84c8faecfdad61d878381.vega-embed {
    width: 100%;
    display: flex;
  }

  #altair-viz-89b0270ba1c84c8faecfdad61d878381.vega-embed details,
  #altair-viz-89b0270ba1c84c8faecfdad61d878381.vega-embed details summary {
    position: relative;
  }
</style>


# Choosing between constrained and unconstrained

Fit an unconstrained model to see whether the data actually support the monotone assumption, or whether the smooth would prefer a different shape when given the freedom to do so.


``` python
# Fit unconstrained model to verify monotone assumption
m_unconstrained = wk.GAM("zinc ~ s(dist, bs='tp')").fit(data)
m_unconstrained.plot()
```


<style>
  #altair-viz-0f1e765652034f80a5cb05f6aaa7968d.vega-embed {
    width: 100%;
    display: flex;
  }

  #altair-viz-0f1e765652034f80a5cb05f6aaa7968d.vega-embed details,
  #altair-viz-0f1e765652034f80a5cb05f6aaa7968d.vega-embed details summary {
    position: relative;
  }
</style>


If the unconstrained smooth is also monotonically decreasing, the constraint and the data agree. Use `bs="mpd"` to enforce the shape explicitly. If the unconstrained smooth turns upward somewhere, investigate the cause before committing to the constraint.
