Throughout the user guide, formulas are written as strings like "y ~ s(x1) + s(x2)". This is convenient for interactive work, but sometimes you need to build formulas programmatically (for example, when the set of covariates is determined at runtime, or when you want to manipulate individual terms in a loop).
Whittaker’s formula system has two layers:
- String formulas: parsed by parse_formula() into structured objects.
- Term objects: LinearTerm, SmoothTerm, InteractionTerm, OffsetTerm, and Formula (which can be constructed directly and passed to GAM()).
Term types
SmoothTerm
SmoothTerm represents s(), te(), ti(), and t2() terms:
from whittaker import SmoothTerm
# Equivalent to s(x, k=15, bs='cr')
s1 = SmoothTerm(variables=("x",), k=15, bs="cr")
print(s1)
# Equivalent to te(x1, x2)
te = SmoothTerm(variables=("x1", "x2"), smooth_type="te")
print(te)
# Equivalent to s(x, by=group)
s_by = SmoothTerm(variables=("x",), by="group")
print(s_by)
# Equivalent to ti(x1, x2, k=5)
ti = SmoothTerm(variables=("x1", "x2"), smooth_type="ti", k=5)
print(ti)
s(x, bs='cr', k=15)
te(x1, x2)
s(x, by='group')
ti(x1, x2, k=5)
The extra dictionary passes additional keyword arguments to the basis constructor:
# Equivalent to s(x, bs='ps', m=2): P-spline with second-order penalty
s_ps = SmoothTerm(variables=("x",), bs="ps", extra={"m": 2})
print(s_ps)
LinearTerm
LinearTerm represents a bare covariate entered linearly (unpenalized):
from whittaker import LinearTerm
lt = LinearTerm(variable="age")
print(lt)
For categorical columns, LinearTerm automatically expands to dummy indicators (one per non-reference level) when the model matrix is built.
InteractionTerm
InteractionTerm represents a parametric interaction between two covariates:
from whittaker import InteractionTerm
# Full interaction (x1 * x2): includes both main effects + interaction
full = InteractionTerm(left="x1", right="x2", full=True)
print(full)
# Interaction only (x1 : x2): no main effects
interaction_only = InteractionTerm(left="x1", right="x2", full=False)
print(interaction_only)
For smooth interactions between continuous variables, use a tensor-product SmoothTerm instead.
OffsetTerm
OffsetTerm represents a covariate with a fixed coefficient of 1, commonly used for exposure terms in rate models:
from whittaker import OffsetTerm
offset = OffsetTerm(expression="log_exposure")
print(offset)
Suppressing the intercept
Set intercept=False to drop the intercept (equivalent to y ~ 0 + ...):
f_no_intercept = Formula(
response="y",
terms=[SmoothTerm(variables=("x",))],
intercept=False,
)
print(f_no_intercept)
Inspecting required columns
Formula.required_columns() returns every data column the formula needs, in first-seen order:
f = parse_formula("y ~ s(x1, by=group) + x2 + te(x3, x4)")
print(f.required_columns())
['y', 'x1', 'group', 'x2', 'x3', 'x4']
This is useful for validating that a dataset has all the columns a formula expects before fitting.
Mixing string and object APIs
You can use strings for interactive exploration and switch to the object API when you need programmatic control. Both produce the same Formula objects and both are accepted by GAM():
# These are equivalent:
m1 = wk.GAM("y ~ s(x1, k=10) + x2")
m2 = wk.GAM(Formula(
response="y",
terms=[SmoothTerm(variables=("x1",), k=10), LinearTerm(variable="x2")],
))
print(m1.formula)
print(m2.formula)
y ~ s(x1, k=10) + x2
y ~ s(x1, k=10) + x2
Where to go next
- Smooth terms: details on basis types, knot placement, and the
k parameter.
- Model fitting: fitting methods and smoothing parameter selection.
- Data input: supported data formats and column types.