spforge 0.8.19__tar.gz → 0.8.23__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.19/spforge.egg-info → spforge-0.8.23}/PKG-INFO +1 -1
- {spforge-0.8.19 → spforge-0.8.23}/pyproject.toml +1 -1
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/_base.py +6 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/_player_rating.py +73 -45
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/_team_rating.py +23 -20
- {spforge-0.8.19 → spforge-0.8.23/spforge.egg-info}/PKG-INFO +1 -1
- {spforge-0.8.19 → spforge-0.8.23}/tests/ratings/test_player_rating_generator.py +341 -118
- {spforge-0.8.19 → spforge-0.8.23}/tests/ratings/test_team_rating_generator.py +153 -11
- {spforge-0.8.19 → spforge-0.8.23}/LICENSE +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/MANIFEST.in +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/README.md +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/game_level_example.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/lol/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/lol/data/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/lol/data/subsample_lol_data.parquet +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/lol/data/utils.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/lol/pipeline_transformer_example.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/cross_validation_example.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/data/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/data/game_player_subsample.parquet +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/data/utils.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/feature_engineering_example.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/game_winner_example.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/examples/nba/predictor_transformers_example.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/setup.cfg +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/autopipeline.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/base_feature_generator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/cross_validator/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/cross_validator/_base.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/cross_validator/cross_validator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/data_structures.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/distributions/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/distributions/_negative_binomial_estimator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/distributions/_normal_distribution_predictor.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/distributions/_student_t_distribution_estimator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/estimator/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/estimator/_conditional_estimator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/estimator/_frequency_bucketing_classifier.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/estimator/_granularity_estimator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/estimator/_group_by_estimator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/estimator/_ordinal_classifier.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/estimator/_sklearn_enhancer_estimator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_base.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_lag.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_net_over_predicted.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_regressor_feature_generator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_rolling_against_opponent.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_rolling_mean_binary.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_rolling_mean_days.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_rolling_window.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/feature_generator/_utils.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/features_generator_pipeline.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/hyperparameter_tuning/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/hyperparameter_tuning/_default_search_spaces.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/hyperparameter_tuning/_tuner.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/performance_transformers/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/performance_transformers/_performance_manager.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/performance_transformers/_performances_transformers.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/enums.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/league_identifier.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/league_start_rating_optimizer.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/player_performance_predictor.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/start_rating_generator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/team_performance_predictor.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/team_start_rating_generator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/ratings/utils.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/scorer/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/scorer/_score.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/__init__.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/_base.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/_net_over_predicted.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/_operator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/_other_transformer.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/_predictor.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/_simple_transformer.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/transformers/_team_ratio_predictor.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge/utils.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge.egg-info/SOURCES.txt +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge.egg-info/dependency_links.txt +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge.egg-info/requires.txt +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/spforge.egg-info/top_level.txt +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/cross_validator/test_cross_validator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/distributions/test_distribution.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/end_to_end/test_estimator_hyperparameter_tuning.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/end_to_end/test_league_start_rating_optimizer.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/end_to_end/test_lol_player_kills.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/end_to_end/test_nba_player_points.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/end_to_end/test_nba_player_ratings_hyperparameter_tuning.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/end_to_end/test_nba_prediction_consistency.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/estimator/test_sklearn_estimator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/feature_generator/test_lag.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/feature_generator/test_regressor_feature_generator.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/feature_generator/test_rolling_against_opponent.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/feature_generator/test_rolling_mean_binary.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/feature_generator/test_rolling_mean_days.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/feature_generator/test_rolling_window.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/hyperparameter_tuning/test_estimator_tuner.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/hyperparameter_tuning/test_rating_tuner.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/performance_transformers/test_performance_manager.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/performance_transformers/test_performances_transformers.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/ratings/test_player_rating_no_mutation.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/ratings/test_ratings_property.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/ratings/test_utils_scaled_weights.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/scorer/test_score.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/scorer/test_score_aggregation_granularity.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/test_autopipeline.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/test_autopipeline_context.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/test_feature_generator_pipeline.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/transformers/test_estimator_transformer_context.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/transformers/test_net_over_predicted.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/transformers/test_other_transformer.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/transformers/test_predictor_transformer.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/tests/transformers/test_simple_transformer.py +0 -0
- {spforge-0.8.19 → spforge-0.8.23}/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.23
|
|
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.23"
|
|
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"
|
|
@@ -176,6 +176,9 @@ class RatingGenerator(FeatureGenerator):
|
|
|
176
176
|
@to_polars
|
|
177
177
|
@nw.narwhalify
|
|
178
178
|
def transform(self, df: IntoFrameT) -> IntoFrameT:
|
|
179
|
+
if self.performance_manager and self.performance_manager.ori_performance_column in df.columns:
|
|
180
|
+
df = nw.from_native(self.performance_manager.transform(df))
|
|
181
|
+
|
|
179
182
|
pl_df: pl.DataFrame
|
|
180
183
|
pl_df = df.to_native() if df.implementation.is_polars() else df.to_polars().to_native()
|
|
181
184
|
return self._historical_transform(pl_df)
|
|
@@ -188,6 +191,9 @@ class RatingGenerator(FeatureGenerator):
|
|
|
188
191
|
- use existing ratings to compute pre-match ratings/features
|
|
189
192
|
- do NOT update ratings
|
|
190
193
|
"""
|
|
194
|
+
if self.performance_manager and self.performance_manager.ori_performance_column in df.columns:
|
|
195
|
+
df = nw.from_native(self.performance_manager.transform(df))
|
|
196
|
+
|
|
191
197
|
pl_df: pl.DataFrame
|
|
192
198
|
pl_df = df.to_native() if df.implementation.is_polars() else df.to_polars().to_native()
|
|
193
199
|
return self._future_transform(pl_df)
|
|
@@ -433,9 +433,16 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
433
433
|
team1_off_perf = self._team_off_perf_from_collection(c1)
|
|
434
434
|
team2_off_perf = self._team_off_perf_from_collection(c2)
|
|
435
435
|
|
|
436
|
+
team1_def_perf: float | None = None
|
|
437
|
+
team2_def_perf: float | None = None
|
|
438
|
+
|
|
436
439
|
if self.use_off_def_split:
|
|
437
|
-
team1_def_perf =
|
|
438
|
-
|
|
440
|
+
team1_def_perf = (
|
|
441
|
+
1.0 - team2_off_perf if team2_off_perf is not None else None
|
|
442
|
+
)
|
|
443
|
+
team2_def_perf = (
|
|
444
|
+
1.0 - team1_off_perf if team1_off_perf is not None else None
|
|
445
|
+
)
|
|
439
446
|
else:
|
|
440
447
|
team1_def_perf = team1_off_perf
|
|
441
448
|
team2_def_perf = team2_off_perf
|
|
@@ -484,26 +491,33 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
484
491
|
),
|
|
485
492
|
)
|
|
486
493
|
|
|
487
|
-
|
|
488
|
-
|
|
494
|
+
perf_value = pre_player.match_performance.performance_value
|
|
495
|
+
if perf_value is None:
|
|
496
|
+
off_change = 0.0
|
|
497
|
+
else:
|
|
498
|
+
off_perf = float(perf_value)
|
|
499
|
+
mult_off = self._applied_multiplier_off(off_state)
|
|
500
|
+
off_change = (
|
|
501
|
+
(off_perf - float(pred_off))
|
|
502
|
+
* mult_off
|
|
503
|
+
* float(pre_player.match_performance.participation_weight)
|
|
504
|
+
)
|
|
489
505
|
|
|
490
|
-
if
|
|
491
|
-
|
|
492
|
-
|
|
506
|
+
if perf_value is None or team1_def_perf is None:
|
|
507
|
+
def_change = 0.0
|
|
508
|
+
else:
|
|
509
|
+
def_perf = float(team1_def_perf)
|
|
493
510
|
|
|
494
|
-
|
|
495
|
-
|
|
511
|
+
if not self.use_off_def_split:
|
|
512
|
+
pred_def = pred_off
|
|
513
|
+
def_perf = float(perf_value)
|
|
496
514
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
(def_perf - float(pred_def))
|
|
504
|
-
* mult_def
|
|
505
|
-
* float(pre_player.match_performance.participation_weight)
|
|
506
|
-
)
|
|
515
|
+
mult_def = self._applied_multiplier_def(def_state)
|
|
516
|
+
def_change = (
|
|
517
|
+
(def_perf - float(pred_def))
|
|
518
|
+
* mult_def
|
|
519
|
+
* float(pre_player.match_performance.participation_weight)
|
|
520
|
+
)
|
|
507
521
|
|
|
508
522
|
if math.isnan(off_change) or math.isnan(def_change):
|
|
509
523
|
raise ValueError(
|
|
@@ -562,26 +576,33 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
562
576
|
),
|
|
563
577
|
)
|
|
564
578
|
|
|
565
|
-
|
|
566
|
-
|
|
579
|
+
perf_value = pre_player.match_performance.performance_value
|
|
580
|
+
if perf_value is None:
|
|
581
|
+
off_change = 0.0
|
|
582
|
+
else:
|
|
583
|
+
off_perf = float(perf_value)
|
|
584
|
+
mult_off = self._applied_multiplier_off(off_state)
|
|
585
|
+
off_change = (
|
|
586
|
+
(off_perf - float(pred_off))
|
|
587
|
+
* mult_off
|
|
588
|
+
* float(pre_player.match_performance.participation_weight)
|
|
589
|
+
)
|
|
567
590
|
|
|
568
|
-
if
|
|
569
|
-
|
|
570
|
-
|
|
591
|
+
if perf_value is None or team2_def_perf is None:
|
|
592
|
+
def_change = 0.0
|
|
593
|
+
else:
|
|
594
|
+
def_perf = float(team2_def_perf)
|
|
571
595
|
|
|
572
|
-
|
|
573
|
-
|
|
596
|
+
if not self.use_off_def_split:
|
|
597
|
+
pred_def = pred_off
|
|
598
|
+
def_perf = float(perf_value)
|
|
574
599
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
(def_perf - float(pred_def))
|
|
582
|
-
* mult_def
|
|
583
|
-
* float(pre_player.match_performance.participation_weight)
|
|
584
|
-
)
|
|
600
|
+
mult_def = self._applied_multiplier_def(def_state)
|
|
601
|
+
def_change = (
|
|
602
|
+
(def_perf - float(pred_def))
|
|
603
|
+
* mult_def
|
|
604
|
+
* float(pre_player.match_performance.participation_weight)
|
|
605
|
+
)
|
|
585
606
|
|
|
586
607
|
if math.isnan(off_change) or math.isnan(def_change):
|
|
587
608
|
raise ValueError(
|
|
@@ -933,7 +954,7 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
933
954
|
self.performance_column in team_player
|
|
934
955
|
and team_player[self.performance_column] is not None
|
|
935
956
|
)
|
|
936
|
-
else
|
|
957
|
+
else None
|
|
937
958
|
)
|
|
938
959
|
|
|
939
960
|
mp = MatchPerformance(
|
|
@@ -1021,22 +1042,28 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
1021
1042
|
|
|
1022
1043
|
return pre_match_player_ratings, pre_match_player_off_values
|
|
1023
1044
|
|
|
1024
|
-
def _team_off_perf_from_collection(
|
|
1045
|
+
def _team_off_perf_from_collection(
|
|
1046
|
+
self, c: PreMatchPlayersCollection
|
|
1047
|
+
) -> float | None:
|
|
1025
1048
|
# observed offense perf = weighted mean of player performance_value using participation_weight if present
|
|
1049
|
+
# skip players with null performance
|
|
1026
1050
|
cn = self.column_names
|
|
1027
1051
|
if not c.pre_match_player_ratings:
|
|
1028
|
-
return
|
|
1052
|
+
return None
|
|
1029
1053
|
wsum = 0.0
|
|
1030
1054
|
psum = 0.0
|
|
1031
1055
|
for pre in c.pre_match_player_ratings:
|
|
1056
|
+
perf_val = pre.match_performance.performance_value
|
|
1057
|
+
if perf_val is None:
|
|
1058
|
+
continue
|
|
1032
1059
|
w = (
|
|
1033
1060
|
float(pre.match_performance.participation_weight)
|
|
1034
1061
|
if cn.participation_weight
|
|
1035
1062
|
else 1.0
|
|
1036
1063
|
)
|
|
1037
|
-
psum += float(
|
|
1064
|
+
psum += float(perf_val) * w
|
|
1038
1065
|
wsum += w
|
|
1039
|
-
return psum / wsum if wsum else
|
|
1066
|
+
return psum / wsum if wsum else None
|
|
1040
1067
|
|
|
1041
1068
|
def _team_off_def_rating_from_collection(
|
|
1042
1069
|
self, c: PreMatchPlayersCollection
|
|
@@ -1101,13 +1128,13 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
1101
1128
|
self.PLAYER_PRED_PERF_COL: [],
|
|
1102
1129
|
}
|
|
1103
1130
|
|
|
1104
|
-
def get_perf_value(team_player: dict) -> float:
|
|
1131
|
+
def get_perf_value(team_player: dict) -> float | None:
|
|
1105
1132
|
if (
|
|
1106
1133
|
self.performance_column in team_player
|
|
1107
1134
|
and team_player[self.performance_column] is not None
|
|
1108
1135
|
):
|
|
1109
1136
|
return float(team_player[self.performance_column])
|
|
1110
|
-
return
|
|
1137
|
+
return None
|
|
1111
1138
|
|
|
1112
1139
|
def ensure_new_player(
|
|
1113
1140
|
pid: str,
|
|
@@ -1187,8 +1214,9 @@ class PlayerRatingGenerator(RatingGenerator):
|
|
|
1187
1214
|
)
|
|
1188
1215
|
off_vals.append(float(local_off[pid].rating_value))
|
|
1189
1216
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1217
|
+
if mp.performance_value is not None:
|
|
1218
|
+
psum += float(mp.performance_value) * float(pw)
|
|
1219
|
+
wsum += float(pw)
|
|
1192
1220
|
|
|
1193
1221
|
team_off_perf = psum / wsum if wsum else 0.0
|
|
1194
1222
|
return pre_list, player_ids, off_vals, proj_w, team_off_perf
|
|
@@ -326,16 +326,7 @@ class TeamRatingGenerator(RatingGenerator):
|
|
|
326
326
|
opp_off_pre = float(o_off.rating_value)
|
|
327
327
|
opp_def_pre = float(o_def.rating_value)
|
|
328
328
|
|
|
329
|
-
|
|
330
|
-
float(r[self.performance_column])
|
|
331
|
-
if r.get(self.performance_column) is not None
|
|
332
|
-
else 0.0
|
|
333
|
-
)
|
|
334
|
-
opp_off_perf = float(r[perf_opp_col]) if r.get(perf_opp_col) is not None else 0.0
|
|
335
|
-
if self.use_off_def_split:
|
|
336
|
-
def_perf = 1.0 - opp_off_perf
|
|
337
|
-
else:
|
|
338
|
-
def_perf = off_perf
|
|
329
|
+
off_perf_raw = r.get(self.performance_column)
|
|
339
330
|
|
|
340
331
|
pred_off = self._performance_predictor.predict_performance(
|
|
341
332
|
rating_value=s_off.rating_value, opponent_team_rating_value=o_def.rating_value
|
|
@@ -346,16 +337,28 @@ class TeamRatingGenerator(RatingGenerator):
|
|
|
346
337
|
if not self.use_off_def_split:
|
|
347
338
|
pred_def = pred_off
|
|
348
339
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
340
|
+
# Null performance means no rating change
|
|
341
|
+
if off_perf_raw is None:
|
|
342
|
+
off_change = 0.0
|
|
343
|
+
def_change = 0.0
|
|
344
|
+
else:
|
|
345
|
+
off_perf = float(off_perf_raw)
|
|
346
|
+
opp_off_perf = float(r[perf_opp_col]) if r.get(perf_opp_col) is not None else 0.0
|
|
347
|
+
if self.use_off_def_split:
|
|
348
|
+
def_perf = 1.0 - opp_off_perf
|
|
349
|
+
else:
|
|
350
|
+
def_perf = off_perf
|
|
351
|
+
|
|
352
|
+
mult_off = self._applied_multiplier(s_off, self.rating_change_multiplier_offense)
|
|
353
|
+
mult_def = self._applied_multiplier(s_def, self.rating_change_multiplier_defense)
|
|
354
|
+
|
|
355
|
+
off_change = (off_perf - pred_off) * mult_off
|
|
356
|
+
def_change = (def_perf - pred_def) * mult_def
|
|
357
|
+
|
|
358
|
+
if math.isnan(off_change) or math.isnan(def_change):
|
|
359
|
+
raise ValueError(
|
|
360
|
+
f"NaN rating change for team_id={team_id}, match_id={r[cn.match_id]}"
|
|
361
|
+
)
|
|
359
362
|
|
|
360
363
|
rows.append(
|
|
361
364
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spforge
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.23
|
|
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
|