spforge 0.8.27__py3-none-any.whl → 0.8.30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of spforge might be problematic. Click here for more details.
- spforge/data_structures.py +4 -0
- spforge/ratings/_player_rating.py +128 -5
- spforge/ratings/player_performance_predictor.py +11 -13
- spforge/scorer/_score.py +121 -0
- {spforge-0.8.27.dist-info → spforge-0.8.30.dist-info}/METADATA +1 -1
- {spforge-0.8.27.dist-info → spforge-0.8.30.dist-info}/RECORD +11 -10
- tests/ratings/test_player_rating_generator.py +470 -1
- tests/scorer/test_scorer_name.py +292 -0
- {spforge-0.8.27.dist-info → spforge-0.8.30.dist-info}/WHEEL +0 -0
- {spforge-0.8.27.dist-info → spforge-0.8.30.dist-info}/licenses/LICENSE +0 -0
- {spforge-0.8.27.dist-info → spforge-0.8.30.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from sklearn.metrics import mean_absolute_error, mean_squared_error
|
|
3
|
+
|
|
4
|
+
from spforge.scorer import (
|
|
5
|
+
Filter,
|
|
6
|
+
MeanBiasScorer,
|
|
7
|
+
Operator,
|
|
8
|
+
OrdinalLossScorer,
|
|
9
|
+
SklearnScorer,
|
|
10
|
+
)
|
|
11
|
+
from spforge.scorer._score import (
|
|
12
|
+
PWMSE,
|
|
13
|
+
ProbabilisticMeanBias,
|
|
14
|
+
ThresholdEventScorer,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestScorerNameProperty:
|
|
19
|
+
"""Test the auto-generated name property for all scorers."""
|
|
20
|
+
|
|
21
|
+
def test_simple_mean_bias_scorer(self):
|
|
22
|
+
scorer = MeanBiasScorer(target="points", pred_column="pred")
|
|
23
|
+
assert scorer.name == "mean_bias_scorer_points"
|
|
24
|
+
|
|
25
|
+
def test_simple_pwmse(self):
|
|
26
|
+
scorer = PWMSE(target="goals", pred_column="pred", labels=list(range(10)))
|
|
27
|
+
assert scorer.name == "pwmse_goals"
|
|
28
|
+
|
|
29
|
+
def test_simple_ordinal_loss(self):
|
|
30
|
+
scorer = OrdinalLossScorer(target="points", pred_column="pred", classes=list(range(0, 41)))
|
|
31
|
+
assert scorer.name == "ordinal_loss_scorer_points"
|
|
32
|
+
|
|
33
|
+
def test_simple_sklearn_scorer(self):
|
|
34
|
+
scorer = SklearnScorer(target="yards", pred_column="pred", scorer_function=mean_absolute_error)
|
|
35
|
+
assert scorer.name == "mean_absolute_error_yards"
|
|
36
|
+
|
|
37
|
+
def test_simple_probabilistic_mean_bias(self):
|
|
38
|
+
scorer = ProbabilisticMeanBias(target="points", pred_column="pred")
|
|
39
|
+
assert scorer.name == "probabilistic_mean_bias_points"
|
|
40
|
+
|
|
41
|
+
def test_simple_threshold_event_scorer(self):
|
|
42
|
+
scorer = ThresholdEventScorer(
|
|
43
|
+
dist_column="dist",
|
|
44
|
+
threshold_column="threshold",
|
|
45
|
+
outcome_column="outcome",
|
|
46
|
+
labels=list(range(10))
|
|
47
|
+
)
|
|
48
|
+
assert scorer.name == "threshold_event_scorer___event__"
|
|
49
|
+
|
|
50
|
+
def test_with_single_granularity(self):
|
|
51
|
+
scorer = MeanBiasScorer(target="points", pred_column="pred", granularity=["team_id"])
|
|
52
|
+
assert scorer.name == "mean_bias_scorer_points_gran:team_id"
|
|
53
|
+
|
|
54
|
+
def test_with_multiple_granularity(self):
|
|
55
|
+
scorer = MeanBiasScorer(
|
|
56
|
+
target="points",
|
|
57
|
+
pred_column="pred",
|
|
58
|
+
granularity=["game_id", "team_id"]
|
|
59
|
+
)
|
|
60
|
+
assert scorer.name == "mean_bias_scorer_points_gran:game_id+team_id"
|
|
61
|
+
|
|
62
|
+
def test_with_long_granularity_abbreviated(self):
|
|
63
|
+
scorer = MeanBiasScorer(
|
|
64
|
+
target="points",
|
|
65
|
+
pred_column="pred",
|
|
66
|
+
granularity=["col1", "col2", "col3", "col4", "col5"]
|
|
67
|
+
)
|
|
68
|
+
assert scorer.name == "mean_bias_scorer_points_gran:col1+col2+col3+2more"
|
|
69
|
+
|
|
70
|
+
def test_with_naive_comparison_no_granularity(self):
|
|
71
|
+
scorer = SklearnScorer(
|
|
72
|
+
target="goals",
|
|
73
|
+
pred_column="pred",
|
|
74
|
+
scorer_function=mean_absolute_error,
|
|
75
|
+
compare_to_naive=True
|
|
76
|
+
)
|
|
77
|
+
assert scorer.name == "mean_absolute_error_goals_naive"
|
|
78
|
+
|
|
79
|
+
def test_with_naive_comparison_with_naive_granularity(self):
|
|
80
|
+
scorer = MeanBiasScorer(
|
|
81
|
+
target="yards",
|
|
82
|
+
pred_column="pred",
|
|
83
|
+
compare_to_naive=True,
|
|
84
|
+
naive_granularity=["season"]
|
|
85
|
+
)
|
|
86
|
+
assert scorer.name == "mean_bias_scorer_yards_naive:season"
|
|
87
|
+
|
|
88
|
+
def test_with_aggregation_level(self):
|
|
89
|
+
scorer = MeanBiasScorer(
|
|
90
|
+
target="yards",
|
|
91
|
+
pred_column="pred",
|
|
92
|
+
aggregation_level=["game_id", "player_id"]
|
|
93
|
+
)
|
|
94
|
+
assert scorer.name == "mean_bias_scorer_yards_agg:game_id+player_id"
|
|
95
|
+
|
|
96
|
+
def test_with_user_filters_only(self):
|
|
97
|
+
scorer = MeanBiasScorer(
|
|
98
|
+
target="yards",
|
|
99
|
+
pred_column="pred",
|
|
100
|
+
filters=[
|
|
101
|
+
Filter("minutes", 0, Operator.GREATER_THAN),
|
|
102
|
+
Filter("position", "QB", Operator.EQUALS)
|
|
103
|
+
]
|
|
104
|
+
)
|
|
105
|
+
assert scorer.name == "mean_bias_scorer_yards_filters:2"
|
|
106
|
+
|
|
107
|
+
def test_validation_column_not_counted_in_filters(self):
|
|
108
|
+
scorer = MeanBiasScorer(
|
|
109
|
+
target="yards",
|
|
110
|
+
pred_column="pred",
|
|
111
|
+
validation_column="is_valid",
|
|
112
|
+
filters=[Filter("minutes", 0, Operator.GREATER_THAN)]
|
|
113
|
+
)
|
|
114
|
+
# Should only count the minutes filter, not the auto-added validation filter
|
|
115
|
+
assert scorer.name == "mean_bias_scorer_yards_filters:1"
|
|
116
|
+
|
|
117
|
+
def test_validation_column_alone_not_shown(self):
|
|
118
|
+
scorer = MeanBiasScorer(
|
|
119
|
+
target="yards",
|
|
120
|
+
pred_column="pred",
|
|
121
|
+
validation_column="is_valid"
|
|
122
|
+
)
|
|
123
|
+
# Validation filter auto-added but not counted
|
|
124
|
+
assert scorer.name == "mean_bias_scorer_yards"
|
|
125
|
+
|
|
126
|
+
def test_complex_configuration_all_components(self):
|
|
127
|
+
scorer = MeanBiasScorer(
|
|
128
|
+
target="yards",
|
|
129
|
+
pred_column="pred",
|
|
130
|
+
granularity=["game_id", "team_id"],
|
|
131
|
+
compare_to_naive=True,
|
|
132
|
+
naive_granularity=["season"],
|
|
133
|
+
aggregation_level=["game_id", "player_id"],
|
|
134
|
+
filters=[Filter("minutes", 0, Operator.GREATER_THAN)]
|
|
135
|
+
)
|
|
136
|
+
assert scorer.name == "mean_bias_scorer_yards_gran:game_id+team_id_naive:season_agg:game_id+player_id_filters:1"
|
|
137
|
+
|
|
138
|
+
def test_sklearn_with_different_function(self):
|
|
139
|
+
scorer = SklearnScorer(
|
|
140
|
+
target="points",
|
|
141
|
+
pred_column="pred",
|
|
142
|
+
scorer_function=mean_squared_error
|
|
143
|
+
)
|
|
144
|
+
assert scorer.name == "mean_squared_error_points"
|
|
145
|
+
|
|
146
|
+
def test_sklearn_with_lambda_fallback(self):
|
|
147
|
+
scorer = SklearnScorer(
|
|
148
|
+
target="points",
|
|
149
|
+
pred_column="pred",
|
|
150
|
+
scorer_function=lambda y_true, y_pred: 0.0
|
|
151
|
+
)
|
|
152
|
+
assert scorer.name == "custom_metric_points"
|
|
153
|
+
|
|
154
|
+
def test_special_characters_sanitized(self):
|
|
155
|
+
scorer = MeanBiasScorer(target="points-per-game", pred_column="pred")
|
|
156
|
+
assert scorer.name == "mean_bias_scorer_points_per_game"
|
|
157
|
+
|
|
158
|
+
def test_special_characters_in_target_sanitized(self):
|
|
159
|
+
scorer = MeanBiasScorer(target="pass/run_ratio", pred_column="pred")
|
|
160
|
+
assert scorer.name == "mean_bias_scorer_pass_run_ratio"
|
|
161
|
+
|
|
162
|
+
def test_name_override(self):
|
|
163
|
+
scorer = MeanBiasScorer(
|
|
164
|
+
target="points",
|
|
165
|
+
pred_column="pred",
|
|
166
|
+
granularity=["team_id"],
|
|
167
|
+
_name_override="custom_name"
|
|
168
|
+
)
|
|
169
|
+
assert scorer.name == "custom_name"
|
|
170
|
+
|
|
171
|
+
def test_consistency_across_repeated_calls(self):
|
|
172
|
+
scorer = MeanBiasScorer(
|
|
173
|
+
target="yards",
|
|
174
|
+
pred_column="pred",
|
|
175
|
+
granularity=["game_id"],
|
|
176
|
+
compare_to_naive=True
|
|
177
|
+
)
|
|
178
|
+
name1 = scorer.name
|
|
179
|
+
name2 = scorer.name
|
|
180
|
+
name3 = scorer.name
|
|
181
|
+
assert name1 == name2 == name3
|
|
182
|
+
|
|
183
|
+
def test_different_scorers_different_names(self):
|
|
184
|
+
scorer1 = MeanBiasScorer(target="points", pred_column="pred")
|
|
185
|
+
scorer2 = PWMSE(target="points", pred_column="pred", labels=list(range(10)))
|
|
186
|
+
assert scorer1.name != scorer2.name
|
|
187
|
+
|
|
188
|
+
def test_same_config_same_name(self):
|
|
189
|
+
scorer1 = MeanBiasScorer(
|
|
190
|
+
target="points",
|
|
191
|
+
pred_column="pred",
|
|
192
|
+
granularity=["team_id"]
|
|
193
|
+
)
|
|
194
|
+
scorer2 = MeanBiasScorer(
|
|
195
|
+
target="points",
|
|
196
|
+
pred_column="pred_2", # Different pred column shouldn't affect name
|
|
197
|
+
granularity=["team_id"]
|
|
198
|
+
)
|
|
199
|
+
assert scorer1.name == scorer2.name
|
|
200
|
+
|
|
201
|
+
def test_none_granularity_excluded(self):
|
|
202
|
+
scorer = MeanBiasScorer(
|
|
203
|
+
target="points",
|
|
204
|
+
pred_column="pred",
|
|
205
|
+
granularity=None
|
|
206
|
+
)
|
|
207
|
+
assert "gran:" not in scorer.name
|
|
208
|
+
assert scorer.name == "mean_bias_scorer_points"
|
|
209
|
+
|
|
210
|
+
def test_empty_filters_excluded(self):
|
|
211
|
+
scorer = MeanBiasScorer(
|
|
212
|
+
target="points",
|
|
213
|
+
pred_column="pred",
|
|
214
|
+
filters=[]
|
|
215
|
+
)
|
|
216
|
+
assert "filters:" not in scorer.name
|
|
217
|
+
assert scorer.name == "mean_bias_scorer_points"
|
|
218
|
+
|
|
219
|
+
def test_none_aggregation_level_excluded(self):
|
|
220
|
+
scorer = MeanBiasScorer(
|
|
221
|
+
target="points",
|
|
222
|
+
pred_column="pred",
|
|
223
|
+
aggregation_level=None
|
|
224
|
+
)
|
|
225
|
+
assert "agg:" not in scorer.name
|
|
226
|
+
assert scorer.name == "mean_bias_scorer_points"
|
|
227
|
+
|
|
228
|
+
def test_pwmse_with_all_components(self):
|
|
229
|
+
scorer = PWMSE(
|
|
230
|
+
target="goals",
|
|
231
|
+
pred_column="pred",
|
|
232
|
+
labels=list(range(10)),
|
|
233
|
+
granularity=["team_id"],
|
|
234
|
+
compare_to_naive=True,
|
|
235
|
+
naive_granularity=["season"],
|
|
236
|
+
aggregation_level=["game_id"],
|
|
237
|
+
filters=[Filter("minutes", 20, Operator.GREATER_THAN)]
|
|
238
|
+
)
|
|
239
|
+
assert scorer.name == "pwmse_goals_gran:team_id_naive:season_agg:game_id_filters:1"
|
|
240
|
+
|
|
241
|
+
def test_ordinal_loss_with_granularity(self):
|
|
242
|
+
scorer = OrdinalLossScorer(
|
|
243
|
+
target="points",
|
|
244
|
+
pred_column="pred",
|
|
245
|
+
classes=list(range(0, 41)),
|
|
246
|
+
granularity=["game_id"]
|
|
247
|
+
)
|
|
248
|
+
assert scorer.name == "ordinal_loss_scorer_points_gran:game_id"
|
|
249
|
+
|
|
250
|
+
def test_threshold_event_scorer_with_components(self):
|
|
251
|
+
scorer = ThresholdEventScorer(
|
|
252
|
+
dist_column="dist",
|
|
253
|
+
threshold_column="threshold",
|
|
254
|
+
outcome_column="outcome",
|
|
255
|
+
labels=list(range(10)),
|
|
256
|
+
granularity=["game_id"],
|
|
257
|
+
compare_to_naive=True
|
|
258
|
+
)
|
|
259
|
+
assert scorer.name == "threshold_event_scorer___event___gran:game_id_naive"
|
|
260
|
+
|
|
261
|
+
def test_long_aggregation_abbreviated(self):
|
|
262
|
+
scorer = MeanBiasScorer(
|
|
263
|
+
target="points",
|
|
264
|
+
pred_column="pred",
|
|
265
|
+
aggregation_level=["a", "b", "c", "d", "e"]
|
|
266
|
+
)
|
|
267
|
+
assert scorer.name == "mean_bias_scorer_points_agg:a+b+c+2more"
|
|
268
|
+
|
|
269
|
+
def test_long_naive_granularity_abbreviated(self):
|
|
270
|
+
scorer = MeanBiasScorer(
|
|
271
|
+
target="points",
|
|
272
|
+
pred_column="pred",
|
|
273
|
+
compare_to_naive=True,
|
|
274
|
+
naive_granularity=["a", "b", "c", "d"]
|
|
275
|
+
)
|
|
276
|
+
assert scorer.name == "mean_bias_scorer_points_naive:a+b+c+1more"
|
|
277
|
+
|
|
278
|
+
def test_exactly_three_columns_no_abbreviation(self):
|
|
279
|
+
scorer = MeanBiasScorer(
|
|
280
|
+
target="points",
|
|
281
|
+
pred_column="pred",
|
|
282
|
+
granularity=["a", "b", "c"]
|
|
283
|
+
)
|
|
284
|
+
assert scorer.name == "mean_bias_scorer_points_gran:a+b+c"
|
|
285
|
+
|
|
286
|
+
def test_four_columns_abbreviated(self):
|
|
287
|
+
scorer = MeanBiasScorer(
|
|
288
|
+
target="points",
|
|
289
|
+
pred_column="pred",
|
|
290
|
+
granularity=["a", "b", "c", "d"]
|
|
291
|
+
)
|
|
292
|
+
assert scorer.name == "mean_bias_scorer_points_gran:a+b+c+1more"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|