spforge 0.8.30__py3-none-any.whl → 0.8.31__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.

@@ -595,7 +595,7 @@ class PlayerRatingGenerator(RatingGenerator):
595
595
  * float(pre_player.match_performance.participation_weight)
596
596
  )
597
597
 
598
- if perf_value is None or team1_def_perf is None:
598
+ if team1_def_perf is None:
599
599
  def_change = 0.0
600
600
  else:
601
601
  def_perf = float(team1_def_perf)
@@ -689,7 +689,7 @@ class PlayerRatingGenerator(RatingGenerator):
689
689
  * float(pre_player.match_performance.participation_weight)
690
690
  )
691
691
 
692
- if perf_value is None or team2_def_perf is None:
692
+ if team2_def_perf is None:
693
693
  def_change = 0.0
694
694
  else:
695
695
  def_perf = float(team2_def_perf)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spforge
3
- Version: 0.8.30
3
+ Version: 0.8.31
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
@@ -51,7 +51,7 @@ spforge/performance_transformers/_performance_manager.py,sha256=WmjmlMEnq7y75MiI
51
51
  spforge/performance_transformers/_performances_transformers.py,sha256=0lxuWjAfWBRXRgQsNJHjw3P-nlTtHBu4_bOVdoy7hq4,15536
52
52
  spforge/ratings/__init__.py,sha256=OZVH2Lo6END3n1X8qi4QcyAPlThIwAYwVKCiIuOQSQU,576
53
53
  spforge/ratings/_base.py,sha256=ne4BRrYFPqMirdFPVnyDN44wjFQwOQgWoUXu_59xgWE,14687
54
- spforge/ratings/_player_rating.py,sha256=0VZYTWdoZoxPpw1UhTsRxlwMJjBAGNr2EdGOQkT2BpE,67097
54
+ spforge/ratings/_player_rating.py,sha256=Dx_X1gl8_D_k2PZhMc-zuZ6wvX_YPgXibIfflNwT14g,67053
55
55
  spforge/ratings/_team_rating.py,sha256=3m90-R2zW0k5EHwjw-83Hacz91fGmxW1LQ8ZUGHlgt4,24970
56
56
  spforge/ratings/enums.py,sha256=s7z_RcZS6Nlgfa_6tasO8_IABZJwywexe7sep9DJBgo,1739
57
57
  spforge/ratings/league_identifier.py,sha256=_KDUKOwoNU6RNFKE5jju4eYFGVNGBdJsv5mhNvMakfc,6019
@@ -71,7 +71,7 @@ spforge/transformers/_other_transformer.py,sha256=w2a7Wnki3vJe4GAkSa4kealw0GILIo
71
71
  spforge/transformers/_predictor.py,sha256=2sE6gfVrilXzPVcBurSrtqHw33v2ljygQcEYXt9LhZc,3119
72
72
  spforge/transformers/_simple_transformer.py,sha256=zGUFNQYMeoDSa2CoQejQNiNmKCBN5amWTvyOchiUHj0,5660
73
73
  spforge/transformers/_team_ratio_predictor.py,sha256=g8_bR53Yyv0iNCtol1O9bgJSeZcIco_AfbQuUxQJkeY,6884
74
- spforge-0.8.30.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
74
+ spforge-0.8.31.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
75
75
  tests/test_autopipeline.py,sha256=7cNAn-nmGolfyfk3THh9IKcHZfRA-pLYC_xAyMg-No4,26863
76
76
  tests/test_autopipeline_context.py,sha256=IuRUY4IA6uMObvbl2pXSaXO2_tl3qX6wEbTZY0dkTMI,1240
77
77
  tests/test_feature_generator_pipeline.py,sha256=CK0zVL8PfTncy3RmG9i-YpgwjOIV7yJhV7Q44tbetI8,19020
@@ -94,7 +94,7 @@ tests/hyperparameter_tuning/test_estimator_tuner.py,sha256=iewME41d6LR2aQ0OtohGF
94
94
  tests/hyperparameter_tuning/test_rating_tuner.py,sha256=usjC2ioO_yWRjjNAlRTyMVYheOrCi0kKocmHQHdTmpM,18699
95
95
  tests/performance_transformers/test_performance_manager.py,sha256=gjuuV_hb27kCo_kUecPKG3Cbot2Gqis1W3kw2A4ovS4,10690
96
96
  tests/performance_transformers/test_performances_transformers.py,sha256=A-tGiCx7kXrj1cVj03Bc7prOeZ1_Ryz8YFx9uj3eK6w,11064
97
- tests/ratings/test_player_rating_generator.py,sha256=F4mW7J4djkFpt0GgORIfVz0jKegfGNwPqGtXp44VOSc,100762
97
+ tests/ratings/test_player_rating_generator.py,sha256=clakS4RZxTWbSrb3gQiIKg4UQLI-g-K_cdvfMgJR7uw,103176
98
98
  tests/ratings/test_player_rating_no_mutation.py,sha256=GzO3Hl__5K68DS3uRLefwnbcTJOvBM7cZqww4M21UZM,8493
99
99
  tests/ratings/test_ratings_property.py,sha256=ckyfGILXa4tfQvsgyXEzBDNr2DUmHwFRV13N60w66iE,6561
100
100
  tests/ratings/test_team_rating_generator.py,sha256=SqQcfckNmJJc99feCdnmkNYDape-p69e92Dp8Vzpu2w,101156
@@ -108,7 +108,7 @@ tests/transformers/test_other_transformer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
108
108
  tests/transformers/test_predictor_transformer.py,sha256=N1aBYLjN3ldpYZLwjih_gTFYSMitrZu-PNK78W6RHaQ,6877
109
109
  tests/transformers/test_simple_transformer.py,sha256=wWR0qjLb_uS4HXrJgGdiqugOY1X7kwd1_OPS02IT2b8,4676
110
110
  tests/transformers/test_team_ratio_predictor.py,sha256=fOUP_JvNJi-3kom3ZOs1EdG0I6Z8hpLpYKNHu1eWtOw,8562
111
- spforge-0.8.30.dist-info/METADATA,sha256=DHqd51r8ONs36cHM0-CaWQJW_4QIKmX5MDNvl-2xTfo,20048
112
- spforge-0.8.30.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
113
- spforge-0.8.30.dist-info/top_level.txt,sha256=6UW2M5a7WKOeaAi900qQmRKNj5-HZzE8-eUD9Y9LTq0,23
114
- spforge-0.8.30.dist-info/RECORD,,
111
+ spforge-0.8.31.dist-info/METADATA,sha256=6K4WvRIGPNqP4DLegmzk95oc7rJd8yS6nZQgCoLG_yc,20048
112
+ spforge-0.8.31.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
113
+ spforge-0.8.31.dist-info/top_level.txt,sha256=6UW2M5a7WKOeaAi900qQmRKNj5-HZzE8-eUD9Y9LTq0,23
114
+ spforge-0.8.31.dist-info/RECORD,,
@@ -2133,6 +2133,59 @@ def test_fit_transform_when_all_players_have_null_performance_then_no_rating_cha
2133
2133
  )
2134
2134
 
2135
2135
 
2136
+ def test_null_individual_perf_still_updates_def_rating(base_cn):
2137
+ """
2138
+ Regression test: Players with null individual performance should still get DEF updates.
2139
+
2140
+ Bug: Line 598 had `if perf_value is None or team1_def_perf is None: def_change = 0.0`
2141
+ This incorrectly skipped DEF updates when player had null individual performance.
2142
+
2143
+ Fix: Changed to `if team1_def_perf is None: def_change = 0.0`
2144
+ Defense is team-level, so null individual perf should NOT block DEF updates.
2145
+
2146
+ Test creates scenario where P1 has null perf but team defense is known (poor).
2147
+ Verifies P1's DEF rating decreases (proving defensive update logic ran).
2148
+ """
2149
+ # Match 1: Balanced to establish baseline ratings
2150
+ # Match 2: P1 null perf, but T2 dominates offense (0.9) so T1 defense is poor (0.1)
2151
+ df = pl.DataFrame(
2152
+ {
2153
+ "pid": ["P1", "P2", "P3", "P4", "P1", "P2", "P3", "P4"],
2154
+ "tid": ["T1", "T1", "T2", "T2", "T1", "T1", "T2", "T2"],
2155
+ "mid": ["M1", "M1", "M1", "M1", "M2", "M2", "M2", "M2"],
2156
+ "dt": ["2024-01-01"] * 4 + ["2024-01-02"] * 4,
2157
+ "perf": [0.5, 0.5, 0.5, 0.5, None, 0.1, 0.9, 0.9],
2158
+ "pw": [1.0] * 8,
2159
+ }
2160
+ )
2161
+
2162
+ gen = PlayerRatingGenerator(
2163
+ performance_column="perf",
2164
+ column_names=base_cn,
2165
+ use_off_def_split=True,
2166
+ rating_change_multiplier_offense=50.0, # High multipliers to ensure visible changes
2167
+ rating_change_multiplier_defense=50.0,
2168
+ start_rating_value=1000.0,
2169
+ )
2170
+ gen.fit_transform(df)
2171
+
2172
+ # P1 had null perf in M2, so OFF rating should be unchanged from baseline
2173
+ p1_off = gen._player_off_ratings["P1"].rating_value
2174
+ assert p1_off == 1000.0, f"P1 OFF should be 1000 (null perf), got {p1_off}"
2175
+
2176
+ # P1's DEF rating MUST decrease because T1's defense was poor (0.1) in M2
2177
+ # Team defense = 1.0 - opponent offense = 1.0 - 0.9 = 0.1 (much worse than expected 0.5)
2178
+ p1_def = gen._player_def_ratings["P1"].rating_value
2179
+ assert p1_def < 1000.0, (
2180
+ f"P1 DEF should decrease (team defended poorly), but got {p1_def}. "
2181
+ f"Bug: defensive update was incorrectly skipped for null individual performance."
2182
+ )
2183
+
2184
+ # Sanity check: P2 had valid perf (0.1) so OFF should change too
2185
+ p2_off = gen._player_off_ratings["P2"].rating_value
2186
+ assert p2_off != 1000.0, f"P2 OFF should change (valid perf 0.1), got {p2_off}"
2187
+
2188
+
2136
2189
  # --- team_players_playing_time Tests ---
2137
2190
 
2138
2191