The scoring interface remains estimator.predict(raw_frame). No scoring service or persistence layer is required. The frame below contains only rating features and exposure. Keep the policy ID alongside the results so each price can be traced back to its inputs; it is not a predictor. We retain raw rates and training O/P adjusted rates, following the prediction-scale convention.
You now have a rate for each policy without supplying its outcomes. Multiply a rate by that policy’s exposure to get expected claim cost for its insured period. Columns ending in __raw_rate and __adjusted_rate are EUR per policy-year; __raw_expected_period_cost and __adjusted_expected_period_cost are EUR for that policy’s insured period. “Adjusted” always means the frozen training O/P multiplier, not a commercial premium loading. The scoring frame reuses known policies to demonstrate the interface; this is not a second performance evaluation.
If a multiplicative workbook is the deliverable, the direct GLM already provides one. This export uses its raw rates, so the training multiplier in scored is not embedded in the workbook:
Direct GLM workbook — raw rates (EUR per policy-year)
0
base_rate
48.05387
recalibrated
False
feature
level
multiplicative_factor
application
0
driver_age
(-inf, 21.5)
1.000000
multiply_by_factor_matching_level
1
driver_age
[21.5, 24.5)
0.446789
multiply_by_factor_matching_level
2
driver_age
[24.5, 27.5)
0.387670
multiply_by_factor_matching_level
3
driver_age
[27.5, 54.5)
0.489958
multiply_by_factor_matching_level
4
driver_age
[54.5, 71.5)
0.473482
multiply_by_factor_matching_level
5
driver_age
[71.5, inf)
0.749925
multiply_by_factor_matching_level
6
driver_age
Missing
0.557837
multiply_by_factor_matching_level
7
vehicle_age
(-inf, 2.5)
1.000000
multiply_by_factor_matching_level
8
vehicle_age
[2.5, 5.5)
0.909299
multiply_by_factor_matching_level
9
vehicle_age
[5.5, 6.5)
0.977541
multiply_by_factor_matching_level
5.2 Work through one policy price
Select the held-out policy with the lowest policy ID. This fixed rule uses neither claims nor predicted cost. Display its original rating inputs beside the levels assigned by the direct GLM’s fitted training bins and groups. Exposure is the insured fraction of a year; it converts a rate into period cost and does not receive a tariff factor.
The workbook application below reuses the same factor-reading recipe as the optional distillation example. It still needs the fitted preprocessing pipeline: this is not standalone reconstruction from the Excel mappings sheet. Retain that fitted pipeline with the workbook.
worked_policy = test_frame[["policy_id", *raw_predict_columns]].nsmallest(1, "policy_id")worked_inputs = worked_policy[raw_predict_columns]direct_pipeline = glm_estimators["direct-tweedie-glm"]worked_levels = direct_pipeline[:-1].transform(worked_inputs)direct_tariff = tariff_from_sheets(direct_sheets)# All nine predictors in this configuration become categorical bins/groups.assertnot direct_tariff["numeric"]worked_factors = pd.DataFrame({"Rating input": FEATURES,"Raw value": [worked_inputs.iloc[0][feature] for feature in FEATURES],"Fitted level": [str(worked_levels.iloc[0][feature]) for feature in FEATURES],"Selected factor": [ direct_tariff["categorical"][feature][str(worked_levels.iloc[0][feature])]for feature in FEATURES ],})display(worked_policy[["policy_id", "exposure"]].T)display(worked_factors)
7
policy_id
811.00
exposure
0.76
Rating input
Raw value
Fitted level
Selected factor
0
driver_age
44
[27.5, 54.5)
0.489958
1
vehicle_age
0
(-inf, 2.5)
1.000000
2
bonus_malus
50
(-inf, 51.5)
1.000000
3
density
3317
[2676.5, inf)
2.509551
4
region
R93
group_6
1.989918
5
vehicle_brand
B12
group_1
1.066475
6
veh_power
5
group_1
1.036693
7
veh_gas
'Regular'
'Regular'
1.000000
8
area
E
E
0.938247
Multiply the workbook base by every selected factor, including reference levels whose factor is one:
Read the last two rows as expected claim costs in EUR for this exposure, before and after the training adjustment. The base and both rates are EUR per policy-year; the factor product and training O/P are unitless. The workbook remains on the raw scale: the training multiplier is applied externally, exactly as in scored. It balances training totals, without guaranteeing this policy’s cost or held-out calibration. These are expected claims on the tutorial’s capped loss layer, before expenses, profit, taxes, or other commercial loadings.
For a tariff review, check the base rate, factor levels, mappings, loss layer, and raw prediction scale against the GLM diagnostics. The full-portfolio and segment limitations remain. You can stop here with a scored portfolio and a direct GLM workbook for review. Use reporting to record the run, or continue below if a GBM must be represented as a tariff.
5.3 Optional extension: distillation and workbook evaluation
The remaining sections evaluate a GBM-derived workbook, including its agreement with its student model and its performance against held-out claims.
5.4 Distil the GBM into an executable tariff
A tree model is not itself a multiplicative tariff. distill_gbm fits a log-link GLM student to a GBM teacher’s predictions on the experiment’s training rows, then measures fidelity on the untouched test rows. It copies the already-fitted preprocessing, so bins and group mappings are not relearned from the holdout. The teacher here is the raw, unscaled tariff-structured Tweedie LightGBM (tweedie-lightgbm-tariff), before the chart adjustment: the student lives in the same binned/grouped feature space. Fidelity measures agreement with this teacher; it does not establish performance against claims.
Construct this extra candidate here: copy the raw GBM’s fixed tree settings into a separate experiment with the GLM’s preprocessing and split. This is a new fit, with the same training rows and learned rating structure as the GLM; it does not change either main-path model.
The workbook contains the same base_rate, factors, and mappings sheets as a direct GLM export. Here recalibrate=False is deliberate: applying the workbook reproduces the student exactly. The student approximates the GBM teacher; the workbook does not reproduce the teacher exactly. Export receives only the training frame for feature metadata. The held-out fidelity metadata still measures agreement with the teacher, not with claims.
pd.testing.assert_frame_equal(fit_X, tariff_X.iloc[list(glm_run.train_indices)])pd.testing.assert_frame_equal(validation_X, tariff_X.iloc[list(glm_run.test_indices)])assertlist(sheets) == ["base_rate", "factors", "mappings"]assert sheets["base_rate"].iloc[0]["distilled_from"] =="RiskGBM"assertnot sheets["base_rate"].iloc[0]["recalibrated"]print("Distilled workbook — raw student rates (EUR per policy-year)")display(sheets["base_rate"].T)display(sheets["factors"].head(20))display(sheets["mappings"].head(20))
Distilled workbook — raw student rates (EUR per policy-year)
0
base_rate
51.015377
family
tweedie
link
log
intercept_
5.225353
recalibrated
False
distilled_from
RiskGBM
teacher_objective
tweedie
teacher_student_deviance
0.661562
student_teacher_total_ratio
1.003165
feature
level
multiplicative_factor
application
0
driver_age
(-inf, 21.5)
1.000000
multiply_by_factor_matching_level
1
driver_age
[21.5, 24.5)
0.561933
multiply_by_factor_matching_level
2
driver_age
[24.5, 27.5)
0.493566
multiply_by_factor_matching_level
3
driver_age
[27.5, 54.5)
0.618848
multiply_by_factor_matching_level
4
driver_age
[54.5, 71.5)
0.624068
multiply_by_factor_matching_level
5
driver_age
[71.5, inf)
0.761103
multiply_by_factor_matching_level
6
driver_age
Missing
0.658489
multiply_by_factor_matching_level
7
vehicle_age
(-inf, 2.5)
1.000000
multiply_by_factor_matching_level
8
vehicle_age
[2.5, 5.5)
0.980187
multiply_by_factor_matching_level
9
vehicle_age
[5.5, 6.5)
0.974645
multiply_by_factor_matching_level
10
vehicle_age
[6.5, 7.5)
0.999024
multiply_by_factor_matching_level
11
vehicle_age
[7.5, 12.5)
0.939047
multiply_by_factor_matching_level
12
vehicle_age
[12.5, 15.5)
0.736423
multiply_by_factor_matching_level
13
vehicle_age
[15.5, 18.5)
0.708853
multiply_by_factor_matching_level
14
vehicle_age
[18.5, inf)
0.464310
multiply_by_factor_matching_level
15
vehicle_age
Missing
0.826254
multiply_by_factor_matching_level
16
bonus_malus
(-inf, 51.5)
1.000000
multiply_by_factor_matching_level
17
bonus_malus
[51.5, 57.5)
1.403540
multiply_by_factor_matching_level
18
bonus_malus
[57.5, 63.5)
2.322182
multiply_by_factor_matching_level
19
bonus_malus
[63.5, 90.5)
2.034211
multiply_by_factor_matching_level
feature
role
dtype
levels
n_levels
reference_level
mapping
0
driver_age
categorical
category
(-inf, 21.5), [21.5, 24.5), [24.5, 27.5), [27....
7
(-inf, 21.5)
NaN
1
vehicle_age
categorical
category
(-inf, 2.5), [2.5, 5.5), [5.5, 6.5), [6.5, 7.5...
9
(-inf, 2.5)
NaN
2
bonus_malus
categorical
category
(-inf, 51.5), [51.5, 57.5), [57.5, 63.5), [63....
7
(-inf, 51.5)
NaN
3
density
categorical
category
(-inf, 18.5), [18.5, 375.5), [375.5, 2351.0), ...
6
(-inf, 18.5)
NaN
4
region
categorical
category
group_0, group_1, group_2, group_3, group_4, g...
9
group_0
NaN
5
vehicle_brand
categorical
category
group_0, group_1, B1, B4, B5, B13, group_6, B1...
9
group_0
NaN
6
veh_power
categorical
category
group_0, group_1, 7, group_3, 11, 10, 12, grou...
9
group_0
NaN
7
veh_gas
categorical
category
'Regular', 'Diesel', Other
3
'Regular'
NaN
8
area
categorical
category
B, A, C, D, F, E, Other
7
B
NaN
9
driver_age
binned
category
(-inf, 21.5), [21.5, 24.5), [24.5, 27.5), [27....
7
NaN
21.5, 24.5, 27.5, 54.5, 71.5
10
vehicle_age
binned
category
(-inf, 2.5), [2.5, 5.5), [5.5, 6.5), [6.5, 7.5...
9
NaN
2.5, 5.5, 6.5, 7.5, 12.5, 15.5, 18.5
11
bonus_malus
binned
category
(-inf, 51.5), [51.5, 57.5), [57.5, 63.5), [63....
7
NaN
51.5, 57.5, 63.5, 90.5, 95.5
12
density
binned
category
(-inf, 18.5), [18.5, 375.5), [375.5, 2351.0), ...
6
NaN
18.5, 375.5, 2351.0, 2676.5
13
region
grouped
category
group_0, group_1, group_2, group_3, group_4, g...
9
NaN
'R11' -> 'group_4'; 'R21' -> 'group_7'; 'R22' ...
14
vehicle_brand
grouped
category
group_0, group_1, B1, B4, B5, B13, group_6, B1...
9
NaN
'B1' -> 'B1'; 'B10' -> 'group_6'; 'B11' -> 'B1...
15
veh_power
grouped
category
group_0, group_1, 7, group_3, 11, 10, 12, grou...
9
NaN
'10' -> '10'; '11' -> '11'; '12' -> '12'; '13'...
16
veh_gas
grouped
category
'Regular', 'Diesel', Other
3
NaN
"'Diesel'" -> "'Diesel'"; "'Regular'" -> "'Reg...
17
area
grouped
category
B, A, C, D, F, E, Other
7
NaN
'A' -> 'A'; 'B' -> 'B'; 'C' -> 'C'; 'D' -> 'D'...
The following application still uses the student’s fitted preprocessing pipeline. It verifies workbook factors after transformation; it does not reconstruct standalone scoring from the Excel mappings sheet. Retain that pipeline with the workbook for this recipe.
Code
workbook_tariff = tariff_from_sheets(sheets)transformed_validation = distilled_pipeline[:-1].transform(validation_X)teacher_rate = np.asarray(teacher_pipeline.predict(validation_X), dtype=float)raw_test_predictions = { name: np.asarray(estimator.predict(test_frame[raw_predict_columns]), dtype=float)for name, estimator in estimators.items()}np.testing.assert_allclose(teacher_rate, teacher_pipeline.predict(test_frame[raw_predict_columns]))student_rate = np.asarray(distilled_pipeline.predict(validation_X), dtype=float)workbook_rate = apply_tariff(workbook_tariff, transformed_validation)for frame in [fit_X, validation_X]: applied = apply_tariff(workbook_tariff, distilled_pipeline[:-1].transform(frame)) student = np.asarray(distilled_pipeline.predict(frame), dtype=float)assert np.isfinite(applied).all() and (applied >0).all() np.testing.assert_allclose(applied, student, rtol=1e-6, atol=1e-8)for step in ["binner", "grouper"]: original = teacher_pipeline.named_steps[step].mapping_ copied = distilled_pipeline.named_steps[step].mapping_assert original.keys() == copied.keys()for name in original:if step =="binner": np.testing.assert_array_equal(original[name], copied[name])else:assert original[name] == copied[name]
5.5 Teacher–student fidelity, separately from claims performance
Here the target is the teacher’s predicted rate, not observed claims. Weighted Tweedie deviance at power 1.5 is zero for exact reproduction; the student/teacher predicted-total ratio is one for equal totals. Neither establishes accuracy against losses or segment calibration.
Reconcile fidelity with student and workbook metadata
5.6 Four candidates against the same held-out claims
The applied workbook is the fourth candidate. Its rates come from the workbook factors applied after the student’s fitted preprocessing, not from a teacher prediction. All four rows use exactly the same test claims and exposure. Deviance is exposure-weighted Tweedie at power 1.5; \(D^2\) uses the same constant held-out mean reference as above. Concentration Gini measures ranking only.
The second table applies each candidate’s own frozen training O/P. For the workbook, compute that factor from applied workbook predictions on training rows; it is an external multiplier, not a change to the exported base or factors. It balances training totals and can improve or worsen held-out diagnostics.
Shared test claims: 10,033,849.49; exposure: 70,511.87
5.7 Calibration and ranking of the applied tariff
Each calibration panel compares observed and predicted rates in that candidate’s exposure-weighted prediction deciles. The diagonal means agreement in level. The Lorenz overlay uses raw rates; positive training multipliers preserve every curve, as checked above. Crossing curves do not establish ranking dominance.
5.8 Compare candidates at each raw age, then inside tariff bins
The first figure compares all four candidates at every observed driver and vehicle age. Use it to locate differences that could be hidden by a fitted bin. The second is a supporting tariff view using the direct GLM’s frozen training bins. This also puts the raw-feature GBM in those bins for reporting; it does not alter its inputs or fit new cuts.
Both views reuse the same held-out predictions. Observed rates and exposure are shared across candidates within each view. Raw and training O/P adjusted rates appear in separate columns; “raw” in those column titles means no level adjustment, independently of whether the x-axis uses raw ages or fitted bins. These are portfolio averages, subject to composition and claim variation. Read the sparse tails alongside exposure before interpreting a jagged line.
The workbook gains a multiplicative factor representation. It preserves the student’s rates to the checked numerical tolerance, while approximating the teacher’s predictions. The table below reports workbook minus comparator on claims: negative deviance differences mean lower loss; positive \(D^2\) or Gini differences mean improvement in that statistic. An O/P change must be read against one, not maximized. Predicted-total changes are monetary level changes.
Calculate workbook differences on each prediction scale
Distillation may improve or worsen claims performance: approximation error against a teacher is not the same as prediction error against claims. Read the differences alongside calibration and age panels; equal portfolio totals do not imply equal segment rates. This single held-out split gives descriptive evidence, not statistical superiority, stability under refitting, or an automatic choice of production tariff. Adjusted workbook results describe the workbook plus its external training multiplier; the exported workbook itself remains raw.