export_rating_tables#

superglm.export_rating_tables(
model: SuperGLM,
file_path: str | Path,
X: FrameLike,
y: NDArray,
sample_weight: NDArray | None = None,
*,
offset: NDArray | None = None,
offset_source=None,
offset_name: str | None = None,
offset_kind: str = 'auto',
offset_max_exact_levels: int = 20,
offset_mapping_rtol: float = 1e-10,
n_bins: int = 150,
impact_bins: tuple[int, ...] = (20, 50, 100, 200, 250),
bin_strategy: str = 'exposure_quantile',
band_se: float = 1.0,
band_max_error: float = 0.1,
format: str | None = None,
sheet_name: str = 'Rating Tables',
summary_sheet_name: str = 'Model Summary',
impact_sheet_name: str = 'Discretization Impact',
centering: str = 'native',
continuous_kind: str = 'binned',
allow_unbounded_extrapolation: bool = False,
) → Path#

Render the rating-table payload to a workbook and return the path.

A thin renderer over build_rating_table_payload, which is where the payload’s contract lives: what the exported product reproduces, which term types are exact and which are binned, what centering= does and does not change, and which errors the export raises. Read that docstring before relying on an exported workbook; this function adds only the file format, the sheet names, and the extra rounding a renderer imposes.

continuous_kind and allow_unbounded_extrapolation are forwarded verbatim and are not interpreted here. They select how a continuous term is represented and whether an unbounded extrapolation may be exported, both of which are properties of the payload rather than of the rendering, so the mode’s validation and its refusals happen once, where the blocks are built.

The mode is documented here as well as there, and deliberately: this is the entry point most callers reach it through, and its sheet – not the payload – is what the downstream consumer parses.

continuous_kind : {"binned", "ppform"}, default "binned" selects how a continuous main-effect term is represented.

"binned" writes a key/relativity/weight block a consumer applies by pure lookup, with no arithmetic. It is an APPROXIMATION: the fitted curve is chopped into intervals carrying one exposure-weighted average each, so a row inside an interval receives a factor that is not its own. Measured on a motor book with 81 distinct ages at n_bins=150, the worst row was mis-rated by 60%, concentrated in the wide intervals the quantile strategy opens in the sparse tails. n_bins is a budget rather than a target, and staying under it is not a route to an exact block – see build_rating_table_payload, where that is measured. bin_strategy="exact" bounds that error instead: every band average stays within min(band_se * SE, log(1 + band_max_error)) of the curve, unless n_bins is too few, in which case the limit widens with a warning.

"ppform" writes the exact piecewise-polynomial form of the fitted curve: one row per knot interval carrying four coefficients. A consumer reads both bounds back out of the interval key and evaluates exp(a + b*u + c*u**2 + d*u**3) with u = (x - lower) / (upper - lower), normalised onto [0, 1] rather than the raw x - lower. It reproduces the fitted model to machine precision – 2.4e-15 against 6.0e-01 for the block it replaces – in usually an order of magnitude fewer rows, and costs a consumer that can evaluate a polynomial.

Its first and last rows are unbounded and are READ rather than evaluated: the factor is Relativity, because u on an infinite width is nan and zero coefficients do not absorb it. The workbook states this beside each such block.

On the sheet that block is SEVEN columns rather than three, and a superset rather than a new shape: <feature>, Relativity and Weight stay in front unchanged, with a, b, c, d appended behind them. An un-upgraded loader still reads it as a step function – it locates the block by the same header signature and slices the same three columns positionally – while an upgraded one reads the coefficients and is exact. A consumer that STORES the coefficients must include them in any content digest it fingerprints a published package with, or two models differing only in their coefficients fingerprint identically and the second is silently deduplicated into the first.

Blocks are laid out at their own widths, so a seven-column block moves every block to its right; a reader keyed on the old fixed three-column stride reads the header row instead.

The bounds live in the KEY, which is text, and that is what lets the unbounded tail rows survive the workbook: they read [-inf, 18.0) and [99.0, inf) exactly as they left the payload, and every numeric cell in the block is a real number. A spreadsheet cell cannot hold an infinity, so a float bound column could not have carried those rows at all. Their coefficients are b = c = d = 0, so a consumer that recognises an infinite bound and skips u there and one that clamps u to [0, 1] arrive at the same factor.

Extrapolation is otherwise carried in the table rather than described beside it: constant leading and trailing rows under the default extrapolation="clip", no unbounded rows at all under extrapolation="error", and a refusal under extrapolation="extend" unless allow_unbounded_extrapolation=True acknowledges that the model prices beyond its training range with an unbounded cubic – which the block’s tails cannot carry and therefore clip.

Terms carrying a Constraint.postfit repair are refused under "ppform", naming the term; Constraint.fit constraints convert unchanged. Polynomial terms stay binned under "ppform", as does the continuous-by-continuous interaction grid, so one workbook can carry both kinds of block at once.