Shot value (experimental)

Warning

Read wa_setpieces.ml.shot_value’s module docstring in full before trusting this output for anything real. The five bundled models were trained elsewhere against a feature schema this package has to reconstruct from Opta qualifiers – some inputs are confidently derived, several are experimental best-effort defaults (documented in the module docstring), not verified ground truth.

Five pre-trained gradient-boosted models, bundled with the ml extra, score every shot in a match: P(on target), xG On Target (P(goal) given on target), Post-Shot xG (P(goal) from actual placement), a situational signal, and a 4-class outcome distribution. shot_value() runs all five and blends psxg and on_target_prob * xgot – two independent P(goal) estimates – into one shot_value column.

from pathlib import Path

from wa_setpieces import load_matches
from wa_setpieces.ml.shot_value import ShotValueModels, shot_value

try:
    _here = Path(__file__).resolve().parent
except NameError:
    _here = Path.cwd()
DATA = _here.parent / "tests" / "data" / "sample_match.json"

events = load_matches(DATA)

Load once, reuse across matches (the .pkl files are a few hundred KB each and slower to load than to run inference with):

models = ShotValueModels.load()
shots = shot_value(events, models)
shots[["eventId", "playerName", "is_goal", "set_piece_type", "on_target_prob", "xgot", "psxg", "shot_value"]]
eventId playerName is_goal set_piece_type on_target_prob xgot psxg shot_value
0 21 P. Velasco False None 0.316310 0.104364 0.000361 0.016686
1 22 G. Rostagno False None 0.280220 0.505882 0.000361 0.071060
2 88 S. Vasquez False None 0.394737 0.104364 0.018957 0.030077
3 161 J. Corozo True None 0.362346 0.104364 0.044335 0.041075
4 249 M. Estrada False None 0.406040 0.505882 0.375000 0.290204
5 281 A. Tobar True None 0.362346 0.156584 0.044335 0.050536
6 318 J. Pretell False None 0.355072 0.369231 0.000361 0.065732
7 337 D. Armas False None 0.277270 0.505882 0.000000 0.070133
8 361 R. Jaramillo True None 0.362346 0.104364 0.044335 0.041075
9 480 A. Herrera False None 0.323395 0.135593 0.000361 0.022105
10 504 P. Charpentier False None 0.401198 0.356061 0.000361 0.071606
11 483 A. Tobar False None 0.362346 0.264151 0.000361 0.048037
12 586 R. Jaramillo False None 0.401198 0.746269 0.000361 0.149881
13 594 N. Molina False None 0.362346 0.264151 0.000361 0.048037
14 599 P. Velasco False None 0.468034 0.554217 0.000000 0.129696
15 608 P. Charpentier False None 0.468034 0.264151 0.000361 0.061996
16 611 N. Molina False None 0.435115 0.264151 0.000361 0.057648
17 564 J. Corozo False None 0.362346 0.156584 0.000361 0.028549
18 641 A. Herrera False None 0.360161 0.302326 0.000361 0.054623
19 696 M. Bermúdez False None 0.468034 0.826590 0.001458 0.194165
20 721 M. Bermúdez False None 0.394737 0.264151 0.000361 0.052315
21 734 A. Herrera False None 0.360161 0.746269 0.000361 0.134569
22 766 V. Burgoa False None 0.277270 0.264151 0.000361 0.036801
23 674 G. Villamíl False None 0.323395 0.078261 0.018957 0.022133
24 678 K. Minda False None 0.362346 0.104364 0.000361 0.019088
25 855 S. Vasquez False None 0.468034 0.223214 0.000000 0.052236


Set-piece shots specifically, ranked by blended shot value:

set_piece_shots = shots[shots["set_piece_type"].notna()].sort_values("shot_value", ascending=False)
set_piece_shots[["playerName", "set_piece_type", "on_target_prob", "psxg", "shot_value"]]
playerName set_piece_type on_target_prob psxg shot_value


Gallery generated by Sphinx-Gallery