spforge 0.8.15__tar.gz → 0.8.16__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.

Files changed (119) hide show
  1. {spforge-0.8.15/spforge.egg-info → spforge-0.8.16}/PKG-INFO +1 -1
  2. {spforge-0.8.15 → spforge-0.8.16}/pyproject.toml +1 -1
  3. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/_player_rating.py +30 -8
  4. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/start_rating_generator.py +1 -1
  5. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/team_start_rating_generator.py +1 -1
  6. {spforge-0.8.15 → spforge-0.8.16/spforge.egg-info}/PKG-INFO +1 -1
  7. {spforge-0.8.15 → spforge-0.8.16}/tests/ratings/test_player_rating_generator.py +70 -0
  8. {spforge-0.8.15 → spforge-0.8.16}/LICENSE +0 -0
  9. {spforge-0.8.15 → spforge-0.8.16}/MANIFEST.in +0 -0
  10. {spforge-0.8.15 → spforge-0.8.16}/README.md +0 -0
  11. {spforge-0.8.15 → spforge-0.8.16}/examples/__init__.py +0 -0
  12. {spforge-0.8.15 → spforge-0.8.16}/examples/game_level_example.py +0 -0
  13. {spforge-0.8.15 → spforge-0.8.16}/examples/lol/__init__.py +0 -0
  14. {spforge-0.8.15 → spforge-0.8.16}/examples/lol/data/__init__.py +0 -0
  15. {spforge-0.8.15 → spforge-0.8.16}/examples/lol/data/subsample_lol_data.parquet +0 -0
  16. {spforge-0.8.15 → spforge-0.8.16}/examples/lol/data/utils.py +0 -0
  17. {spforge-0.8.15 → spforge-0.8.16}/examples/lol/pipeline_transformer_example.py +0 -0
  18. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/__init__.py +0 -0
  19. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/cross_validation_example.py +0 -0
  20. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/data/__init__.py +0 -0
  21. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/data/game_player_subsample.parquet +0 -0
  22. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/data/utils.py +0 -0
  23. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/feature_engineering_example.py +0 -0
  24. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/game_winner_example.py +0 -0
  25. {spforge-0.8.15 → spforge-0.8.16}/examples/nba/predictor_transformers_example.py +0 -0
  26. {spforge-0.8.15 → spforge-0.8.16}/setup.cfg +0 -0
  27. {spforge-0.8.15 → spforge-0.8.16}/spforge/__init__.py +0 -0
  28. {spforge-0.8.15 → spforge-0.8.16}/spforge/autopipeline.py +0 -0
  29. {spforge-0.8.15 → spforge-0.8.16}/spforge/base_feature_generator.py +0 -0
  30. {spforge-0.8.15 → spforge-0.8.16}/spforge/cross_validator/__init__.py +0 -0
  31. {spforge-0.8.15 → spforge-0.8.16}/spforge/cross_validator/_base.py +0 -0
  32. {spforge-0.8.15 → spforge-0.8.16}/spforge/cross_validator/cross_validator.py +0 -0
  33. {spforge-0.8.15 → spforge-0.8.16}/spforge/data_structures.py +0 -0
  34. {spforge-0.8.15 → spforge-0.8.16}/spforge/distributions/__init__.py +0 -0
  35. {spforge-0.8.15 → spforge-0.8.16}/spforge/distributions/_negative_binomial_estimator.py +0 -0
  36. {spforge-0.8.15 → spforge-0.8.16}/spforge/distributions/_normal_distribution_predictor.py +0 -0
  37. {spforge-0.8.15 → spforge-0.8.16}/spforge/distributions/_student_t_distribution_estimator.py +0 -0
  38. {spforge-0.8.15 → spforge-0.8.16}/spforge/estimator/__init__.py +0 -0
  39. {spforge-0.8.15 → spforge-0.8.16}/spforge/estimator/_conditional_estimator.py +0 -0
  40. {spforge-0.8.15 → spforge-0.8.16}/spforge/estimator/_frequency_bucketing_classifier.py +0 -0
  41. {spforge-0.8.15 → spforge-0.8.16}/spforge/estimator/_granularity_estimator.py +0 -0
  42. {spforge-0.8.15 → spforge-0.8.16}/spforge/estimator/_group_by_estimator.py +0 -0
  43. {spforge-0.8.15 → spforge-0.8.16}/spforge/estimator/_ordinal_classifier.py +0 -0
  44. {spforge-0.8.15 → spforge-0.8.16}/spforge/estimator/_sklearn_enhancer_estimator.py +0 -0
  45. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/__init__.py +0 -0
  46. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_base.py +0 -0
  47. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_lag.py +0 -0
  48. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_net_over_predicted.py +0 -0
  49. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_regressor_feature_generator.py +0 -0
  50. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_rolling_against_opponent.py +0 -0
  51. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_rolling_mean_binary.py +0 -0
  52. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_rolling_mean_days.py +0 -0
  53. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_rolling_window.py +0 -0
  54. {spforge-0.8.15 → spforge-0.8.16}/spforge/feature_generator/_utils.py +0 -0
  55. {spforge-0.8.15 → spforge-0.8.16}/spforge/features_generator_pipeline.py +0 -0
  56. {spforge-0.8.15 → spforge-0.8.16}/spforge/hyperparameter_tuning/__init__.py +0 -0
  57. {spforge-0.8.15 → spforge-0.8.16}/spforge/hyperparameter_tuning/_default_search_spaces.py +0 -0
  58. {spforge-0.8.15 → spforge-0.8.16}/spforge/hyperparameter_tuning/_tuner.py +0 -0
  59. {spforge-0.8.15 → spforge-0.8.16}/spforge/performance_transformers/__init__.py +0 -0
  60. {spforge-0.8.15 → spforge-0.8.16}/spforge/performance_transformers/_performance_manager.py +0 -0
  61. {spforge-0.8.15 → spforge-0.8.16}/spforge/performance_transformers/_performances_transformers.py +0 -0
  62. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/__init__.py +0 -0
  63. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/_base.py +0 -0
  64. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/_team_rating.py +0 -0
  65. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/enums.py +0 -0
  66. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/league_identifier.py +0 -0
  67. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/league_start_rating_optimizer.py +0 -0
  68. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/player_performance_predictor.py +0 -0
  69. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/team_performance_predictor.py +0 -0
  70. {spforge-0.8.15 → spforge-0.8.16}/spforge/ratings/utils.py +0 -0
  71. {spforge-0.8.15 → spforge-0.8.16}/spforge/scorer/__init__.py +0 -0
  72. {spforge-0.8.15 → spforge-0.8.16}/spforge/scorer/_score.py +0 -0
  73. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/__init__.py +0 -0
  74. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/_base.py +0 -0
  75. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/_net_over_predicted.py +0 -0
  76. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/_operator.py +0 -0
  77. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/_other_transformer.py +0 -0
  78. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/_predictor.py +0 -0
  79. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/_simple_transformer.py +0 -0
  80. {spforge-0.8.15 → spforge-0.8.16}/spforge/transformers/_team_ratio_predictor.py +0 -0
  81. {spforge-0.8.15 → spforge-0.8.16}/spforge/utils.py +0 -0
  82. {spforge-0.8.15 → spforge-0.8.16}/spforge.egg-info/SOURCES.txt +0 -0
  83. {spforge-0.8.15 → spforge-0.8.16}/spforge.egg-info/dependency_links.txt +0 -0
  84. {spforge-0.8.15 → spforge-0.8.16}/spforge.egg-info/requires.txt +0 -0
  85. {spforge-0.8.15 → spforge-0.8.16}/spforge.egg-info/top_level.txt +0 -0
  86. {spforge-0.8.15 → spforge-0.8.16}/tests/cross_validator/test_cross_validator.py +0 -0
  87. {spforge-0.8.15 → spforge-0.8.16}/tests/distributions/test_distribution.py +0 -0
  88. {spforge-0.8.15 → spforge-0.8.16}/tests/end_to_end/test_estimator_hyperparameter_tuning.py +0 -0
  89. {spforge-0.8.15 → spforge-0.8.16}/tests/end_to_end/test_league_start_rating_optimizer.py +0 -0
  90. {spforge-0.8.15 → spforge-0.8.16}/tests/end_to_end/test_lol_player_kills.py +0 -0
  91. {spforge-0.8.15 → spforge-0.8.16}/tests/end_to_end/test_nba_player_points.py +0 -0
  92. {spforge-0.8.15 → spforge-0.8.16}/tests/end_to_end/test_nba_player_ratings_hyperparameter_tuning.py +0 -0
  93. {spforge-0.8.15 → spforge-0.8.16}/tests/end_to_end/test_nba_prediction_consistency.py +0 -0
  94. {spforge-0.8.15 → spforge-0.8.16}/tests/estimator/test_sklearn_estimator.py +0 -0
  95. {spforge-0.8.15 → spforge-0.8.16}/tests/feature_generator/test_lag.py +0 -0
  96. {spforge-0.8.15 → spforge-0.8.16}/tests/feature_generator/test_regressor_feature_generator.py +0 -0
  97. {spforge-0.8.15 → spforge-0.8.16}/tests/feature_generator/test_rolling_against_opponent.py +0 -0
  98. {spforge-0.8.15 → spforge-0.8.16}/tests/feature_generator/test_rolling_mean_binary.py +0 -0
  99. {spforge-0.8.15 → spforge-0.8.16}/tests/feature_generator/test_rolling_mean_days.py +0 -0
  100. {spforge-0.8.15 → spforge-0.8.16}/tests/feature_generator/test_rolling_window.py +0 -0
  101. {spforge-0.8.15 → spforge-0.8.16}/tests/hyperparameter_tuning/test_estimator_tuner.py +0 -0
  102. {spforge-0.8.15 → spforge-0.8.16}/tests/hyperparameter_tuning/test_rating_tuner.py +0 -0
  103. {spforge-0.8.15 → spforge-0.8.16}/tests/performance_transformers/test_performance_manager.py +0 -0
  104. {spforge-0.8.15 → spforge-0.8.16}/tests/performance_transformers/test_performances_transformers.py +0 -0
  105. {spforge-0.8.15 → spforge-0.8.16}/tests/ratings/test_player_rating_no_mutation.py +0 -0
  106. {spforge-0.8.15 → spforge-0.8.16}/tests/ratings/test_ratings_property.py +0 -0
  107. {spforge-0.8.15 → spforge-0.8.16}/tests/ratings/test_team_rating_generator.py +0 -0
  108. {spforge-0.8.15 → spforge-0.8.16}/tests/ratings/test_utils_scaled_weights.py +0 -0
  109. {spforge-0.8.15 → spforge-0.8.16}/tests/scorer/test_score.py +0 -0
  110. {spforge-0.8.15 → spforge-0.8.16}/tests/scorer/test_score_aggregation_granularity.py +0 -0
  111. {spforge-0.8.15 → spforge-0.8.16}/tests/test_autopipeline.py +0 -0
  112. {spforge-0.8.15 → spforge-0.8.16}/tests/test_autopipeline_context.py +0 -0
  113. {spforge-0.8.15 → spforge-0.8.16}/tests/test_feature_generator_pipeline.py +0 -0
  114. {spforge-0.8.15 → spforge-0.8.16}/tests/transformers/test_estimator_transformer_context.py +0 -0
  115. {spforge-0.8.15 → spforge-0.8.16}/tests/transformers/test_net_over_predicted.py +0 -0
  116. {spforge-0.8.15 → spforge-0.8.16}/tests/transformers/test_other_transformer.py +0 -0
  117. {spforge-0.8.15 → spforge-0.8.16}/tests/transformers/test_predictor_transformer.py +0 -0
  118. {spforge-0.8.15 → spforge-0.8.16}/tests/transformers/test_simple_transformer.py +0 -0
  119. {spforge-0.8.15 → spforge-0.8.16}/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.15
3
+ Version: 0.8.16
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.15"
7
+ version = "0.8.16"
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"
@@ -16,6 +16,7 @@ from spforge.data_structures import (
16
16
  MatchPerformance,
17
17
  MatchPlayer,
18
18
  PlayerRating,
19
+ PlayerRatingChange,
19
20
  PlayerRatingsResult,
20
21
  PreMatchPlayerRating,
21
22
  PreMatchPlayersCollection,
@@ -78,7 +79,7 @@ class PlayerRatingGenerator(RatingGenerator):
78
79
  start_min_count_for_percentiles: int = 50,
79
80
  start_team_rating_subtract: float = 80,
80
81
  start_team_weight: float = 0,
81
- start_max_days_ago_league_entities: int = 120,
82
+ start_max_days_ago_league_entities: int = 600,
82
83
  start_min_match_count_team_rating: int = 2,
83
84
  start_harcoded_start_rating: float | None = None,
84
85
  column_names: ColumnNames | None = None,
@@ -442,9 +443,9 @@ class PlayerRatingGenerator(RatingGenerator):
442
443
  team1_off_rating, team1_def_rating = self._team_off_def_rating_from_collection(c1)
443
444
  team2_off_rating, team2_def_rating = self._team_off_def_rating_from_collection(c2)
444
445
 
445
- player_updates: list[tuple[str, str, float, float, float, float, float, float, int]] = (
446
- []
447
- )
446
+ player_updates: list[
447
+ tuple[str, str, float, float, float, float, float, float, int, str | None]
448
+ ] = []
448
449
 
449
450
  for pre_player in c1.pre_match_player_ratings:
450
451
  pid = pre_player.id
@@ -520,6 +521,7 @@ class PlayerRatingGenerator(RatingGenerator):
520
521
  float(off_change),
521
522
  float(def_change),
522
523
  day_number,
524
+ pre_player.league,
523
525
  )
524
526
  )
525
527
 
@@ -597,6 +599,7 @@ class PlayerRatingGenerator(RatingGenerator):
597
599
  float(off_change),
598
600
  float(def_change),
599
601
  day_number,
602
+ pre_player.league,
600
603
  )
601
604
  )
602
605
 
@@ -611,6 +614,7 @@ class PlayerRatingGenerator(RatingGenerator):
611
614
  _off_change,
612
615
  _def_change,
613
616
  _dn,
617
+ _league,
614
618
  ) in player_updates:
615
619
  out[cn.player_id].append(pid)
616
620
  out[cn.match_id].append(match_id)
@@ -627,15 +631,18 @@ class PlayerRatingGenerator(RatingGenerator):
627
631
  for (
628
632
  pid,
629
633
  team_id,
630
- _off_pre,
634
+ off_pre,
631
635
  _def_pre,
632
636
  _pred_off,
633
637
  _pred_def,
634
638
  off_change,
635
639
  def_change,
636
640
  dn,
641
+ league,
637
642
  ) in player_updates:
638
- pending_team_updates.append((pid, team_id, off_change, def_change, dn))
643
+ pending_team_updates.append(
644
+ (pid, team_id, off_pre, off_change, def_change, dn, league)
645
+ )
639
646
 
640
647
  if last_update_id is None:
641
648
  last_update_id = update_id
@@ -645,9 +652,11 @@ class PlayerRatingGenerator(RatingGenerator):
645
652
 
646
653
  return pl.DataFrame(out, strict=False)
647
654
 
648
- def _apply_player_updates(self, updates: list[tuple[str, str, float, float, int]]) -> None:
655
+ def _apply_player_updates(
656
+ self, updates: list[tuple[str, str, float, float, float, int, str | None]]
657
+ ) -> None:
649
658
 
650
- for player_id, team_id, off_change, def_change, day_number in updates:
659
+ for player_id, team_id, pre_rating, off_change, def_change, day_number, league in updates:
651
660
  off_state = self._player_off_ratings[player_id]
652
661
  off_state.confidence_sum = self._calculate_post_match_confidence_sum(
653
662
  entity_rating=off_state,
@@ -670,6 +679,19 @@ class PlayerRatingGenerator(RatingGenerator):
670
679
  def_state.last_match_day_number = int(day_number)
671
680
  def_state.most_recent_team_id = team_id
672
681
 
682
+ self.start_rating_generator.update_players_to_leagues(
683
+ PlayerRatingChange(
684
+ id=player_id,
685
+ day_number=day_number,
686
+ league=league,
687
+ participation_weight=1.0,
688
+ predicted_performance=0.0,
689
+ performance=0.0,
690
+ pre_match_rating_value=pre_rating,
691
+ rating_change_value=off_change,
692
+ )
693
+ )
694
+
673
695
  def _add_rating_features(self, df: pl.DataFrame) -> pl.DataFrame:
674
696
  cols_to_add = set((self._features_out or []) + (self.non_predictor_features_out or []))
675
697
 
@@ -28,7 +28,7 @@ class StartRatingGenerator:
28
28
  min_count_for_percentiles: int = 50,
29
29
  team_rating_subtract: float = 80,
30
30
  team_weight: float = 0,
31
- max_days_ago_league_entities: int = 120,
31
+ max_days_ago_league_entities: int = 600,
32
32
  min_match_count_team_rating: int = 2,
33
33
  harcoded_start_rating: float | None = None,
34
34
  ):
@@ -24,7 +24,7 @@ class TeamStartRatingGenerator:
24
24
  league_ratings: dict[str, float] | None = None,
25
25
  league_quantile: float = 0.2,
26
26
  min_count_for_percentiles: int = 50,
27
- max_days_ago_league_entities: int = 120,
27
+ max_days_ago_league_entities: int = 600,
28
28
  min_match_count_team_rating: int = 2,
29
29
  harcoded_start_rating: float | None = None,
30
30
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spforge
3
- Version: 0.8.15
3
+ Version: 0.8.16
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
@@ -1746,3 +1746,73 @@ def test_fit_transform__player_rating_difference_from_team_projected_feature(bas
1746
1746
  for row in result.iter_rows(named=True):
1747
1747
  expected = row[player_col] - row[team_col]
1748
1748
  assert row[diff_col] == pytest.approx(expected, rel=1e-9)
1749
+
1750
+
1751
+ def test_fit_transform__start_league_quantile_uses_existing_player_ratings(base_cn):
1752
+ """
1753
+ Bug reproduction: start_league_quantile should use percentile of existing player
1754
+ ratings for new players, but update_players_to_leagues is never called so
1755
+ _league_player_ratings stays empty and all new players get default rating.
1756
+
1757
+ Expected: New player P_NEW should start at 5th percentile of existing ratings (~920)
1758
+ Actual: New player starts at default 1000 because _league_player_ratings is empty
1759
+ """
1760
+ import numpy as np
1761
+
1762
+ num_existing_players = 60
1763
+ player_ids = [f"P{i}" for i in range(num_existing_players)]
1764
+ team_ids = [f"T{i % 2 + 1}" for i in range(num_existing_players)]
1765
+
1766
+ df1 = pl.DataFrame(
1767
+ {
1768
+ "pid": player_ids,
1769
+ "tid": team_ids,
1770
+ "mid": ["M1"] * num_existing_players,
1771
+ "dt": ["2024-01-01"] * num_existing_players,
1772
+ "perf": [0.3 + (i % 10) * 0.07 for i in range(num_existing_players)],
1773
+ "pw": [1.0] * num_existing_players,
1774
+ }
1775
+ )
1776
+
1777
+ gen = PlayerRatingGenerator(
1778
+ performance_column="perf",
1779
+ column_names=base_cn,
1780
+ auto_scale_performance=True,
1781
+ start_league_quantile=0.05,
1782
+ start_min_count_for_percentiles=50,
1783
+ features_out=[RatingKnownFeatures.PLAYER_OFF_RATING],
1784
+ )
1785
+ gen.fit_transform(df1)
1786
+
1787
+ existing_ratings = [
1788
+ gen._player_off_ratings[pid].rating_value for pid in player_ids
1789
+ ]
1790
+ expected_quantile_rating = np.percentile(existing_ratings, 5)
1791
+
1792
+ srg = gen.start_rating_generator
1793
+ assert len(srg._league_player_ratings.get(None, [])) >= 50, (
1794
+ f"Expected _league_player_ratings to have >=50 entries but got "
1795
+ f"{len(srg._league_player_ratings.get(None, []))}. "
1796
+ "update_players_to_leagues is never called."
1797
+ )
1798
+
1799
+ df2 = pl.DataFrame(
1800
+ {
1801
+ "pid": ["P_NEW", "P0"],
1802
+ "tid": ["T1", "T2"],
1803
+ "mid": ["M2", "M2"],
1804
+ "dt": ["2024-01-02", "2024-01-02"],
1805
+ "pw": [1.0, 1.0],
1806
+ }
1807
+ )
1808
+ result = gen.future_transform(df2)
1809
+
1810
+ new_player_start_rating = result.filter(pl.col("pid") == "P_NEW")[
1811
+ "player_off_rating_perf"
1812
+ ][0]
1813
+
1814
+ assert new_player_start_rating == pytest.approx(expected_quantile_rating, rel=0.1), (
1815
+ f"New player should start at 5th percentile ({expected_quantile_rating:.1f}) "
1816
+ f"but got {new_player_start_rating:.1f}. "
1817
+ "start_league_quantile has no effect because update_players_to_leagues is never called."
1818
+ )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes