Matching coefficients and robust covariance with explicit callbacks
This example fits the same unpenalized Poisson mean model with a native Poisson estimator and a callback-driven MEstimator. It compares sandwich covariance in both implementations. Comparing a score sandwich to Poisson’s default Fisher covariance would compare different inference assumptions.
Objective and scores
The objective and gradient must be derivatives of the same function. Clipping the linear predictor while retaining the unclipped gradient breaks that contract. This moderate-scale example uses the exact exponential throughout.
Code
import numpy as npimport crabbymetrics as cmrng = np.random.default_rng(42)n =700x = rng.normal(size=(n, 3))design = np.column_stack([np.ones(n), x])theta_true = np.array([0.15, 0.2, 0.4, -0.6])y = rng.poisson(np.exp(design @ theta_true)).astype(float)data = {"X": design, "y": y, "n": n}def sample(data): indices = data.get("indices", np.arange(data["n"]))return data["X"][indices], data["y"][indices]def objective(theta, data): X, y = sample(data) eta = X @ theta mean = np.exp(eta)returnfloat(np.mean(mean - y * eta)), X.T @ (mean - y) /len(y)def scores(theta, data): X, y = sample(data)return X * (np.exp(X @ theta) - y)[:, None]
MEstimator does not add an intercept: it is the first column of design and the first element of theta. Both callbacks honor bootstrap indices. A score returns one row per observation, not a pre-aggregated gradient.
In v0.9, fit() computes and stores the covariance before returning. It numerically differentiates the mean scores to obtain the bread; the meat is their uncentered outer product. Invalid scores or a singular bread reject the fit and clear its state. Repeated summaries use that stored covariance.
Bootstrap interface
Code
draws = custom.bootstrap(20, seed=123)print("Draw shape:", draws.shape)print("Bootstrap coefficient mean:", draws.mean(axis=0))
Twenty draws illustrate the interface, not a precise confidence interval. Keep callback data immutable for subsequent bootstrap or explicit covariance recomputation. Prefer the native estimator for this built-in model; callbacks are useful when the objective itself needs to change.