spforge 0.8.35__tar.gz → 0.8.36__tar.gz
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-0.8.35/spforge.egg-info → spforge-0.8.36}/PKG-INFO +1 -1
- {spforge-0.8.35 → spforge-0.8.36}/pyproject.toml +1 -1
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/_base.py +13 -10
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/_player_rating.py +14 -7
- {spforge-0.8.35 → spforge-0.8.36/spforge.egg-info}/PKG-INFO +1 -1
- {spforge-0.8.35 → spforge-0.8.36}/tests/ratings/test_player_rating_generator.py +138 -0
- {spforge-0.8.35 → spforge-0.8.36}/LICENSE +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/MANIFEST.in +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/README.md +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/game_level_example.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/lol/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/lol/data/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/lol/data/subsample_lol_data.parquet +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/lol/data/utils.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/lol/pipeline_transformer_example.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/cross_validation_example.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/data/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/data/game_player_subsample.parquet +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/data/utils.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/feature_engineering_example.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/game_winner_example.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/examples/nba/predictor_transformers_example.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/setup.cfg +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/autopipeline.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/base_feature_generator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/cross_validator/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/cross_validator/_base.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/cross_validator/cross_validator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/data_structures.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/distributions/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/distributions/_negative_binomial_estimator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/distributions/_normal_distribution_predictor.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/distributions/_student_t_distribution_estimator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/estimator/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/estimator/_conditional_estimator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/estimator/_frequency_bucketing_classifier.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/estimator/_granularity_estimator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/estimator/_group_by_estimator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/estimator/_ordinal_classifier.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/estimator/_sklearn_enhancer_estimator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_base.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_lag.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_net_over_predicted.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_regressor_feature_generator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_rolling_against_opponent.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_rolling_mean_binary.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_rolling_mean_days.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_rolling_window.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/feature_generator/_utils.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/features_generator_pipeline.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/hyperparameter_tuning/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/hyperparameter_tuning/_default_search_spaces.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/hyperparameter_tuning/_tuner.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/performance_transformers/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/performance_transformers/_performance_manager.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/performance_transformers/_performances_transformers.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/_team_rating.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/enums.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/league_identifier.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/league_start_rating_optimizer.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/player_performance_predictor.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/start_rating_generator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/team_performance_predictor.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/team_start_rating_generator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/ratings/utils.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/scorer/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/scorer/_score.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/__init__.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/_base.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/_net_over_predicted.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/_operator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/_other_transformer.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/_predictor.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/_simple_transformer.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/transformers/_team_ratio_predictor.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge/utils.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge.egg-info/SOURCES.txt +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge.egg-info/dependency_links.txt +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge.egg-info/requires.txt +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/spforge.egg-info/top_level.txt +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/cross_validator/test_cross_validator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/distributions/test_distribution.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/end_to_end/test_estimator_hyperparameter_tuning.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/end_to_end/test_league_start_rating_optimizer.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/end_to_end/test_lol_player_kills.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/end_to_end/test_nba_player_points.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/end_to_end/test_nba_player_ratings_hyperparameter_tuning.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/end_to_end/test_nba_prediction_consistency.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/estimator/test_sklearn_estimator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/feature_generator/test_lag.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/feature_generator/test_regressor_feature_generator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/feature_generator/test_rolling_against_opponent.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/feature_generator/test_rolling_mean_binary.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/feature_generator/test_rolling_mean_days.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/feature_generator/test_rolling_window.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/hyperparameter_tuning/test_estimator_tuner.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/hyperparameter_tuning/test_rating_tuner.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/performance_transformers/test_performance_manager.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/performance_transformers/test_performances_transformers.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/ratings/test_player_rating_no_mutation.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/ratings/test_ratings_property.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/ratings/test_team_rating_generator.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/ratings/test_utils_scaled_weights.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/scorer/test_score.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/scorer/test_score_aggregation_granularity.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/scorer/test_scorer_name.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/test_autopipeline.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/test_autopipeline_context.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/test_feature_generator_pipeline.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/transformers/test_estimator_transformer_context.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/transformers/test_net_over_predicted.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/transformers/test_other_transformer.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/transformers/test_predictor_transformer.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/transformers/test_simple_transformer.py +0 -0
- {spforge-0.8.35 → spforge-0.8.36}/tests/transformers/test_team_ratio_predictor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spforge
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.36
|
|
4
4
|
Summary: A flexible framework for generating features, ratings, and building machine learning or other models for training and inference on sports data.
|
|
5
5
|
Author-email: Mathias Holmstrøm <mathiasholmstom@gmail.com>
|
|
6
6
|
License: See LICENSE file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "spforge"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.36"
|
|
8
8
|
description = "A flexible framework for generating features, ratings, and building machine learning or other models for training and inference on sports data."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -156,17 +156,20 @@ class RatingGenerator(FeatureGenerator):
|
|
|
156
156
|
)
|
|
157
157
|
|
|
158
158
|
perf = df[self.performance_column]
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
# Filter to finite values for validation (NaN/inf are treated as missing data)
|
|
160
|
+
finite_perf = perf.filter(perf.is_finite())
|
|
161
|
+
if len(finite_perf) > 0:
|
|
162
|
+
if finite_perf.max() > 1.02 or finite_perf.min() < -0.02:
|
|
163
|
+
raise ValueError(
|
|
164
|
+
f"Max {self.performance_column} must be less than than 1.02 and min value larger than -0.02. "
|
|
165
|
+
"Either transform it manually or set auto_scale_performance to True"
|
|
166
|
+
)
|
|
164
167
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
if finite_perf.mean() < 0.42 or finite_perf.mean() > 0.58:
|
|
169
|
+
raise ValueError(
|
|
170
|
+
f"Mean {self.performance_column} must be between 0.42 and 0.58. "
|
|
171
|
+
"Either transform it manually or set auto_scale_performance to True"
|
|
172
|
+
)
|
|
170
173
|
|
|
171
174
|
pl_df: pl.DataFrame
|
|
172
175
|
pl_df = df.to_native() if df.implementation.is_polars() else df.to_polars().to_native()
|
|
@@ -587,8 +587,9 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
587
587
|
)
|
|
588
588
|
|
|
589
589
|
perf_value = pre_player.match_performance.performance_value
|
|
590
|
+
perf_is_valid = perf_value is not None and math.isfinite(float(perf_value))
|
|
590
591
|
|
|
591
|
-
if
|
|
592
|
+
if not perf_is_valid:
|
|
592
593
|
off_change = 0.0
|
|
593
594
|
else:
|
|
594
595
|
off_perf = float(perf_value)
|
|
@@ -599,7 +600,7 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
599
600
|
* float(pre_player.match_performance.participation_weight)
|
|
600
601
|
)
|
|
601
602
|
|
|
602
|
-
if team1_def_perf is None or (not self.use_off_def_split and
|
|
603
|
+
if team1_def_perf is None or (not self.use_off_def_split and not perf_is_valid):
|
|
603
604
|
def_change = 0.0
|
|
604
605
|
else:
|
|
605
606
|
def_perf = float(team1_def_perf)
|
|
@@ -681,8 +682,9 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
681
682
|
)
|
|
682
683
|
|
|
683
684
|
perf_value = pre_player.match_performance.performance_value
|
|
685
|
+
perf_is_valid = perf_value is not None and math.isfinite(float(perf_value))
|
|
684
686
|
|
|
685
|
-
if
|
|
687
|
+
if not perf_is_valid:
|
|
686
688
|
off_change = 0.0
|
|
687
689
|
else:
|
|
688
690
|
off_perf = float(perf_value)
|
|
@@ -693,7 +695,7 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
693
695
|
* float(pre_player.match_performance.participation_weight)
|
|
694
696
|
)
|
|
695
697
|
|
|
696
|
-
if team2_def_perf is None or (not self.use_off_def_split and
|
|
698
|
+
if team2_def_perf is None or (not self.use_off_def_split and not perf_is_valid):
|
|
697
699
|
def_change = 0.0
|
|
698
700
|
else:
|
|
699
701
|
def_perf = float(team2_def_perf)
|
|
@@ -1254,7 +1256,7 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
1254
1256
|
self, c: PreMatchPlayersCollection
|
|
1255
1257
|
) -> float | None:
|
|
1256
1258
|
# observed offense perf = weighted mean of player performance_value using participation_weight if present
|
|
1257
|
-
# skip players with null performance
|
|
1259
|
+
# skip players with null/non-finite performance
|
|
1258
1260
|
cn = self.column_names
|
|
1259
1261
|
if not c.pre_match_player_ratings:
|
|
1260
1262
|
return None
|
|
@@ -1264,12 +1266,15 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
1264
1266
|
perf_val = pre.match_performance.performance_value
|
|
1265
1267
|
if perf_val is None:
|
|
1266
1268
|
continue
|
|
1269
|
+
perf_float = float(perf_val)
|
|
1270
|
+
if not math.isfinite(perf_float):
|
|
1271
|
+
continue
|
|
1267
1272
|
w = (
|
|
1268
1273
|
float(pre.match_performance.participation_weight)
|
|
1269
1274
|
if cn.participation_weight
|
|
1270
1275
|
else 1.0
|
|
1271
1276
|
)
|
|
1272
|
-
psum +=
|
|
1277
|
+
psum += perf_float * w
|
|
1273
1278
|
wsum += w
|
|
1274
1279
|
return psum / wsum if wsum else None
|
|
1275
1280
|
|
|
@@ -1341,7 +1346,9 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
1341
1346
|
self.performance_column in team_player
|
|
1342
1347
|
and team_player[self.performance_column] is not None
|
|
1343
1348
|
):
|
|
1344
|
-
|
|
1349
|
+
val = float(team_player[self.performance_column])
|
|
1350
|
+
if math.isfinite(val):
|
|
1351
|
+
return val
|
|
1345
1352
|
return None
|
|
1346
1353
|
|
|
1347
1354
|
def ensure_new_player(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spforge
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.36
|
|
4
4
|
Summary: A flexible framework for generating features, ratings, and building machine learning or other models for training and inference on sports data.
|
|
5
5
|
Author-email: Mathias Holmstrøm <mathiasholmstom@gmail.com>
|
|
6
6
|
License: See LICENSE file
|
|
@@ -2938,3 +2938,141 @@ def test_player_opponent_mean_projected_feature(base_cn, sample_df):
|
|
|
2938
2938
|
(pl.col("player_opponent_mean_projected_perf") - expected).abs().max()
|
|
2939
2939
|
).item()
|
|
2940
2940
|
assert diff < 1e-6, f"Max difference from expected mean: {diff}"
|
|
2941
|
+
|
|
2942
|
+
|
|
2943
|
+
class TestNaNPerformanceHandling:
|
|
2944
|
+
"""Tests that PlayerRatingGenerator handles NaN performance values correctly."""
|
|
2945
|
+
|
|
2946
|
+
@pytest.fixture
|
|
2947
|
+
def nan_cn(self):
|
|
2948
|
+
return ColumnNames(
|
|
2949
|
+
player_id="player_id",
|
|
2950
|
+
team_id="team_id",
|
|
2951
|
+
match_id="match_id",
|
|
2952
|
+
start_date="start_date",
|
|
2953
|
+
participation_weight="participation_weight",
|
|
2954
|
+
)
|
|
2955
|
+
|
|
2956
|
+
def _create_test_df(self, performance_values: list) -> pl.DataFrame:
|
|
2957
|
+
"""Create minimal test DataFrame with 2 teams, 2 players each."""
|
|
2958
|
+
import numpy as np
|
|
2959
|
+
|
|
2960
|
+
return pl.DataFrame({
|
|
2961
|
+
"match_id": ["game1"] * 4,
|
|
2962
|
+
"player_id": ["p1", "p2", "p3", "p4"],
|
|
2963
|
+
"team_id": ["A", "A", "B", "B"],
|
|
2964
|
+
"start_date": ["2024-01-01"] * 4,
|
|
2965
|
+
"performance": performance_values,
|
|
2966
|
+
"participation_weight": [1.0] * 4,
|
|
2967
|
+
})
|
|
2968
|
+
|
|
2969
|
+
def test_nan_performance_does_not_raise(self, nan_cn):
|
|
2970
|
+
"""NaN performance values should not raise ValueError."""
|
|
2971
|
+
import numpy as np
|
|
2972
|
+
|
|
2973
|
+
# Use values that give mean ~0.5 when NaN is excluded
|
|
2974
|
+
df = self._create_test_df([0.6, np.nan, 0.4, 0.5])
|
|
2975
|
+
|
|
2976
|
+
gen = PlayerRatingGenerator(
|
|
2977
|
+
performance_column="performance",
|
|
2978
|
+
column_names=nan_cn,
|
|
2979
|
+
features_out=[RatingKnownFeatures.PLAYER_OFF_RATING],
|
|
2980
|
+
)
|
|
2981
|
+
|
|
2982
|
+
# Should not raise
|
|
2983
|
+
result = gen.fit_transform(df)
|
|
2984
|
+
assert len(result) == 4
|
|
2985
|
+
|
|
2986
|
+
def test_inf_performance_does_not_raise(self, nan_cn):
|
|
2987
|
+
"""Inf performance values should not raise ValueError."""
|
|
2988
|
+
# Use values that give mean ~0.5 when inf is excluded
|
|
2989
|
+
df = self._create_test_df([0.6, float('inf'), 0.4, 0.5])
|
|
2990
|
+
|
|
2991
|
+
gen = PlayerRatingGenerator(
|
|
2992
|
+
performance_column="performance",
|
|
2993
|
+
column_names=nan_cn,
|
|
2994
|
+
features_out=[RatingKnownFeatures.PLAYER_OFF_RATING],
|
|
2995
|
+
)
|
|
2996
|
+
|
|
2997
|
+
result = gen.fit_transform(df)
|
|
2998
|
+
assert len(result) == 4
|
|
2999
|
+
|
|
3000
|
+
def test_neg_inf_performance_does_not_raise(self, nan_cn):
|
|
3001
|
+
"""Negative inf performance values should not raise ValueError."""
|
|
3002
|
+
# Use values that give mean ~0.5 when -inf is excluded
|
|
3003
|
+
df = self._create_test_df([0.6, float('-inf'), 0.4, 0.5])
|
|
3004
|
+
|
|
3005
|
+
gen = PlayerRatingGenerator(
|
|
3006
|
+
performance_column="performance",
|
|
3007
|
+
column_names=nan_cn,
|
|
3008
|
+
features_out=[RatingKnownFeatures.PLAYER_OFF_RATING],
|
|
3009
|
+
)
|
|
3010
|
+
|
|
3011
|
+
result = gen.fit_transform(df)
|
|
3012
|
+
assert len(result) == 4
|
|
3013
|
+
|
|
3014
|
+
def test_nan_performance_treated_as_zero_rating_change(self, nan_cn):
|
|
3015
|
+
"""Players with NaN performance should have zero rating change."""
|
|
3016
|
+
import numpy as np
|
|
3017
|
+
|
|
3018
|
+
# Two games: first establishes ratings, second tests NaN handling
|
|
3019
|
+
df = pl.DataFrame({
|
|
3020
|
+
"match_id": ["game1"] * 4 + ["game2"] * 4,
|
|
3021
|
+
"player_id": ["p1", "p2", "p3", "p4"] * 2,
|
|
3022
|
+
"team_id": ["A", "A", "B", "B"] * 2,
|
|
3023
|
+
"start_date": ["2024-01-01"] * 4 + ["2024-01-02"] * 4,
|
|
3024
|
+
"performance": [0.5, 0.5, 0.5, 0.5, 0.6, np.nan, 0.4, 0.5],
|
|
3025
|
+
"participation_weight": [1.0] * 8,
|
|
3026
|
+
})
|
|
3027
|
+
|
|
3028
|
+
gen = PlayerRatingGenerator(
|
|
3029
|
+
performance_column="performance",
|
|
3030
|
+
column_names=nan_cn,
|
|
3031
|
+
features_out=[RatingKnownFeatures.PLAYER_OFF_RATING],
|
|
3032
|
+
)
|
|
3033
|
+
|
|
3034
|
+
result = gen.fit_transform(df)
|
|
3035
|
+
|
|
3036
|
+
# Get player p2's ratings for both games
|
|
3037
|
+
p2_game1 = result.filter(
|
|
3038
|
+
(pl.col("player_id") == "p2") & (pl.col("match_id") == "game1")
|
|
3039
|
+
)["player_off_rating_performance"][0]
|
|
3040
|
+
|
|
3041
|
+
p2_game2 = result.filter(
|
|
3042
|
+
(pl.col("player_id") == "p2") & (pl.col("match_id") == "game2")
|
|
3043
|
+
)["player_off_rating_performance"][0]
|
|
3044
|
+
|
|
3045
|
+
# Rating should not change when performance is NaN
|
|
3046
|
+
assert p2_game1 == p2_game2, "NaN performance should result in zero rating change"
|
|
3047
|
+
|
|
3048
|
+
def test_all_nan_performance_in_match_handled(self, nan_cn):
|
|
3049
|
+
"""Match where all players have NaN should not raise."""
|
|
3050
|
+
import numpy as np
|
|
3051
|
+
|
|
3052
|
+
# All NaN - validation is skipped when no finite values exist
|
|
3053
|
+
df = self._create_test_df([np.nan, np.nan, np.nan, np.nan])
|
|
3054
|
+
|
|
3055
|
+
gen = PlayerRatingGenerator(
|
|
3056
|
+
performance_column="performance",
|
|
3057
|
+
column_names=nan_cn,
|
|
3058
|
+
features_out=[RatingKnownFeatures.PLAYER_OFF_RATING],
|
|
3059
|
+
)
|
|
3060
|
+
|
|
3061
|
+
result = gen.fit_transform(df)
|
|
3062
|
+
assert len(result) == 4
|
|
3063
|
+
|
|
3064
|
+
def test_mixed_nan_none_performance(self, nan_cn):
|
|
3065
|
+
"""Mix of NaN and None performance values should both be handled."""
|
|
3066
|
+
import numpy as np
|
|
3067
|
+
|
|
3068
|
+
# Use values that give mean ~0.5 when NaN/None are excluded
|
|
3069
|
+
df = self._create_test_df([0.6, np.nan, None, 0.5])
|
|
3070
|
+
|
|
3071
|
+
gen = PlayerRatingGenerator(
|
|
3072
|
+
performance_column="performance",
|
|
3073
|
+
column_names=nan_cn,
|
|
3074
|
+
features_out=[RatingKnownFeatures.PLAYER_OFF_RATING],
|
|
3075
|
+
)
|
|
3076
|
+
|
|
3077
|
+
result = gen.fit_transform(df)
|
|
3078
|
+
assert len(result) == 4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spforge-0.8.35 → spforge-0.8.36}/spforge/distributions/_student_t_distribution_estimator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spforge-0.8.35 → spforge-0.8.36}/spforge/performance_transformers/_performances_transformers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spforge-0.8.35 → spforge-0.8.36}/tests/end_to_end/test_nba_player_ratings_hyperparameter_tuning.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spforge-0.8.35 → spforge-0.8.36}/tests/feature_generator/test_regressor_feature_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spforge-0.8.35 → spforge-0.8.36}/tests/performance_transformers/test_performance_manager.py
RENAMED
|
File without changes
|
{spforge-0.8.35 → spforge-0.8.36}/tests/performance_transformers/test_performances_transformers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|