Blocked and mixed analysis¶
From doekit 0.4, analysis is no longer limited to plain OLS. Blocking,
heteroscedasticity-robust standard errors, lack-of-fit tests and linear mixed
models (REML) are first-class, with statsmodels as a core dependency.
Fixed blocks¶
When a known nuisance factor (day, batch, operator) is controlled by the design, treat it as a fixed block:
import doekit as ed
import numpy as np
d = ed.full_factorial({"A": [-1, 1], "B": [-1, 1]})
# replicate the factorial across two blocks
import pandas as pd
mat = pd.concat([d.matrix, d.matrix], ignore_index=True)
design = ed.Design(matrix=mat, factors=d.factors,
model=ed.Model.main_effects(["A", "B"]))
design = ed.attach_blocks(design, blocks=[0, 0, 0, 0, 1, 1, 1, 1])
y = ... # measured responses
fit = ed.fit_linear_model(design, y, blocks="block") # or rely on metadata
ed.anova_table(fit)
attach_blocks writes the column and metadata["blocking"]. If that metadata
is present, fit_linear_model(..., blocks=None) uses it automatically. Block
columns are excluded from the default factor model and enter as drop-first
dummies (block[...]).
Robust standard errors¶
Use HC when residual variance is suspected to change with the factor settings. Point estimates stay OLS; only the covariance (hence SE / p-values) changes.
Lack of fit¶
When the design has replicate rows (identical factor levels), pure error can be separated from lack of fit:
Raises if there are no replicates.
Mixed models (random groups)¶
Hard-to-change factors / batches that were not designed as fixed blocks are better modelled as random effects:
fit = ed.fit_mixed_model(design, y, groups="batch") # REML by default
fit = ed.fit_mixed_model(design, y, groups=batch_ids, method="ml")
print(fit.re_var, fit.summary_frame())
Typical use: analyse already-collected split-plot / multi-batch data. To
generate a split-plot layout and shortlist it in the advisor, see
Mixture and split-plot (split_plot_design,
hard_to_change=).
Agent-friendly serialization¶
fit.to_dict() # schema: doekit.FitResult/1
mix.to_dict() # schema: doekit.MixedFitResult/1
ed.evaluate(d).to_dict()
ed.recommend_design("screening", 5).to_dict()
Stable JSON schemas so notebooks, agents, and any future MCP bridge share the same contract.