Spaces:
Sleeping
Sleeping
Jon Solow
commited on
Commit
·
af23901
1
Parent(s):
197ebbf
Implement existing simulation in admin page
Browse files- src/analyze_yahoo.py +33 -0
- src/pages/99_Admin_Simulation.py +4 -0
- src/simulate.py +134 -0
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
|