Jon Solow commited on
Commit
af23901
·
1 Parent(s): 197ebbf

Implement existing simulation in admin page

Browse files
src/analyze_yahoo.py CHANGED
@@ -1,5 +1,13 @@
 
1
  import pandas as pd
 
2
  from typing import Optional
 
 
 
 
 
 
3
 
4
 
5
  def calculate_luck(df: pd.DataFrame, as_of_week: Optional[int] = None, include_current: bool = False) -> pd.DataFrame:
@@ -70,3 +78,28 @@ def summarize_remaining_wins_from_matches_map(matches_map):
70
  team_remaining_map[n_wins] = prob_list
71
  remaining_map[team_name] = team_remaining_map
72
  return remaining_map
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
  import pandas as pd
3
+ from stqdm import stqdm
4
  from typing import Optional
5
+ from simulate import (
6
+ calculate_scenario_probabilities,
7
+ create_simulate_summary,
8
+ run_simulations,
9
+ )
10
+ from yahoo_client import YahooFantasyClient
11
 
12
 
13
  def calculate_luck(df: pd.DataFrame, as_of_week: Optional[int] = None, include_current: bool = False) -> pd.DataFrame:
 
78
  team_remaining_map[n_wins] = prob_list
79
  remaining_map[team_name] = team_remaining_map
80
  return remaining_map
81
+
82
+
83
+ def analyze_league(league_key: str, yahoo_client: YahooFantasyClient) -> None:
84
+ df_scores = yahoo_client.full_schedule_dataframe(league_key)
85
+ league_settings = yahoo_client.parse_league_settings(league_key)
86
+ name_str = league_settings.name.strip().replace(" ", "_").lower()
87
+ sim_completed_weeks = league_settings.current_week - 1
88
+ print(f"{sim_completed_weeks=}")
89
+ stqdm.pandas()
90
+ df_sims = run_simulations(
91
+ df_scores,
92
+ complete_weeks=sim_completed_weeks,
93
+ n_sims=10000,
94
+ n_playoff=league_settings.num_playoff_teams,
95
+ )
96
+ df_sim_sum = create_simulate_summary(df_sims)
97
+ df_sim_sum.to_csv(f"{name_str}_sim_sum.csv")
98
+
99
+ scenario_probs = calculate_scenario_probabilities(df_sims)
100
+ with open(f"{name_str}_scenario_probs.json", "w") as f:
101
+ json.dump(scenario_probs, f)
102
+
103
+ remaining_wins_to_probs_map = summarize_remaining_wins_from_matches_map(scenario_probs)
104
+ with open(f"{name_str}_remaining_wins_probs.json", "w") as f:
105
+ json.dump(remaining_wins_to_probs_map, f)
src/pages/99_Admin_Simulation.py CHANGED
@@ -1,5 +1,7 @@
1
  import streamlit as st
2
 
 
 
3
  from config import DEFAULT_ICON, SEASON
4
  from shared_page import common_page_config
5
 
@@ -27,6 +29,8 @@ def get_page():
27
  user_leagues = get_all_league_settings_with_cache(season=selected_season)
28
  selected_league = st.selectbox("Select league", user_leagues, format_func=lambda x: x.name)
29
  st.header(f"{selected_league.name} - {selected_league.season}")
 
 
30
 
31
 
32
  if __name__ == "__main__":
 
1
  import streamlit as st
2
 
3
+
4
+ from analyze_yahoo import analyze_league
5
  from config import DEFAULT_ICON, SEASON
6
  from shared_page import common_page_config
7
 
 
29
  user_leagues = get_all_league_settings_with_cache(season=selected_season)
30
  selected_league = st.selectbox("Select league", user_leagues, format_func=lambda x: x.name)
31
  st.header(f"{selected_league.name} - {selected_league.season}")
32
+ if st.button("Analyze League"):
33
+ analyze_league(selected_league.league_key, st.session_state.yahoo_client)
34
 
35
 
36
  if __name__ == "__main__":
src/simulate.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pandas as pd
3
+ from stqdm import stqdm
4
+ from typing import List, Mapping, MutableMapping, Tuple
5
+
6
+
7
+ def simulate_game(team_name: str, mean_points: float, std_points: float) -> float:
8
+ general_normal = np.round(np.random.normal(mean_points, std_points), 3)
9
+ return general_normal
10
+
11
+
12
+ def simulate_week_matchups(df_week: pd.DataFrame, mean_points: float, std_points: float) -> pd.DataFrame:
13
+ df_week.loc[:, "team_points"] = df_week.team_name.apply(lambda x: simulate_game(x, mean_points, std_points)).values
14
+ df_week.loc[:, "max_match"] = df_week.groupby("match_index").team_points.transform("max").values
15
+ df_week.loc[:, "win_probability"] = ((df_week["team_points"] == df_week["max_match"]) * 1.0).values
16
+ df_week.drop(columns=["max_match"], inplace=True)
17
+ return df_week
18
+
19
+
20
+ def simulate_remaining_season(df_completed_weeks: pd.DataFrame, df_incomplete_weeks: pd.DataFrame) -> pd.DataFrame:
21
+ df_comp = df_completed_weeks.copy()
22
+ df_inc = df_incomplete_weeks.copy()
23
+
24
+ mean_points = df_comp.team_points.mean()
25
+ std_points = df_comp.team_points.std()
26
+
27
+ sim_week_list = [
28
+ simulate_week_matchups(df_week, mean_points, std_points) for (_, df_week) in df_inc.groupby("week")
29
+ ]
30
+ df_full_sim = pd.concat([df_comp] + sim_week_list)
31
+ return df_full_sim
32
+
33
+
34
+ def summarize_season(df_sim: pd.DataFrame, n_bye: int, n_playoff: int) -> pd.DataFrame:
35
+ sim_sum = (
36
+ df_sim.groupby("team_name")[["win_probability", "team_points"]]
37
+ .sum()
38
+ .sort_values(["win_probability", "team_points"], ascending=False)
39
+ )
40
+ sim_sum["season_rank"] = range(1, 1 + len(sim_sum))
41
+ sim_sum["bye"] = (sim_sum["season_rank"] <= n_bye) * 1
42
+ sim_sum["playoff"] = (sim_sum["season_rank"] <= n_playoff) * 1
43
+ return sim_sum
44
+
45
+
46
+ def finalize_all(df: pd.DataFrame) -> None:
47
+ df["win_probability"] = (df.groupby(["week", "match_index"]).team_points.transform("max") == df.team_points) * 1
48
+
49
+
50
+ def run_simulations(df: pd.DataFrame, complete_weeks: int, n_sims: int, n_playoff: int):
51
+ if n_playoff == 6:
52
+ n_bye = 2
53
+ else:
54
+ n_bye = 0
55
+
56
+ df_comp = df[df.week <= complete_weeks]
57
+ finalize_all(df_comp)
58
+ df_inc = df[df.week > complete_weeks]
59
+ sim_list = []
60
+ for i in stqdm(range(n_sims)):
61
+ df_sim = simulate_remaining_season(df_comp, df_inc)
62
+ sim_sum = summarize_season(df_sim, n_bye, n_playoff)
63
+ df_simmed = df_sim[df_sim.week > complete_weeks]
64
+ win_dict = {
65
+ match_key: df_match.sort_values("team_points").team_name.iloc[-1]
66
+ for (match_key, df_match) in df_simmed.groupby(["week", "match_index"])
67
+ }
68
+ df_wins = pd.DataFrame(win_dict, index=[i])
69
+ df_melt = (
70
+ sim_sum.reset_index()[["team_name", "bye", "playoff", "season_rank", "team_points"]]
71
+ .melt(id_vars="team_name")
72
+ .sort_values(["variable", "team_name"])
73
+ )
74
+ df_team_sum = pd.DataFrame(
75
+ {x[0]: x[1] for x in df_melt.apply(lambda r: [(r.variable, r.team_name), r.value], axis=1).values},
76
+ index=[i],
77
+ )
78
+ df_sim_result = df_team_sum.join(df_wins)
79
+ sim_list.append(df_sim_result)
80
+ df_all_sims = pd.concat(sim_list)
81
+ return df_all_sims
82
+
83
+
84
+ def create_simulate_summary(sims: pd.DataFrame) -> pd.DataFrame:
85
+ df_sim_sum = pd.DataFrame()
86
+ df_sim_sum["bye"] = sims.bye.mean()
87
+ df_sim_sum["playoffs"] = sims.playoff.mean()
88
+
89
+ return (
90
+ df_sim_sum[["bye", "playoffs"]]
91
+ .sort_values(["playoffs", "bye"], ascending=False)
92
+ .map(lambda n: "{:,.2%}".format(n))
93
+ )
94
+
95
+
96
+ def get_matches_by_team_from_sims_df(sims: pd.DataFrame) -> Mapping[str, List[Tuple[int]]]:
97
+ team_matches: MutableMapping[str, List[Tuple[int]]] = {}
98
+ for col in sims.columns:
99
+ if isinstance(col[0], (int, float)):
100
+ teams_in_match = sims[col].unique()
101
+ for team in teams_in_match:
102
+ if team in team_matches:
103
+ team_matches[team].append(col)
104
+ else:
105
+ team_matches[team] = [col]
106
+
107
+ return team_matches
108
+
109
+
110
+ def calc_wins_on_scenario(team_name, match_cols_list, sims_df):
111
+ n_matches = len(match_cols_list)
112
+ scenario_bye_playoff_results = {}
113
+ for i in range(2**n_matches):
114
+ binary_scenario = format(i, f"0{n_matches}b")
115
+ filters = []
116
+ for scenario, match in zip(binary_scenario, match_cols_list):
117
+ match_filter = (sims_df[match] == team_name) == bool(int(scenario))
118
+ filters.append(match_filter)
119
+ filtered_sims = sims_df[pd.DataFrame(filters).all()]
120
+ playoff_odds = filtered_sims["playoff"][team_name].mean()
121
+ bye_odds = filtered_sims["bye"][team_name].mean()
122
+ scenario_bye_playoff_results[binary_scenario] = np.nan_to_num(
123
+ [len(filtered_sims), round(playoff_odds, 3), round(bye_odds, 3)]
124
+ ).tolist()
125
+
126
+ return scenario_bye_playoff_results
127
+
128
+
129
+ def calculate_scenario_probabilities(sims: pd.DataFrame) -> Mapping:
130
+ remaining_matches = get_matches_by_team_from_sims_df(sims)
131
+ team_scenario_probs = {
132
+ team: calc_wins_on_scenario(team, matches, sims) for team, matches in remaining_matches.items()
133
+ }
134
+ return team_scenario_probs