Spaces:
Running
Running
James McCool
commited on
Commit
·
efd84aa
1
Parent(s):
ba2158d
Add MMA support to ROO simulation with site-specific functions and dynamic projections
Browse files- app.py +17 -5
- function_hold/MMA_functions.py +448 -0
app.py
CHANGED
@@ -18,6 +18,7 @@ from pandas import DataFrame
|
|
18 |
|
19 |
#bring in functions
|
20 |
from function_hold.NBA_functions import DK_NBA_ROO_Build, FD_NBA_ROO_Build
|
|
|
21 |
|
22 |
def load_file(upload):
|
23 |
if upload is not None:
|
@@ -38,11 +39,11 @@ def load_file(upload):
|
|
38 |
return None
|
39 |
return None
|
40 |
|
41 |
-
tab1, tab2 = st.tabs(["Data Load", "
|
42 |
with tab1:
|
43 |
if st.button('Clear data', key='reset1'):
|
44 |
st.session_state.clear()
|
45 |
-
sport_var = st.selectbox("Select Sport", ["NBA", "NFL", "MLB"])
|
46 |
st.subheader("Projections File")
|
47 |
if sport_var == "NBA":
|
48 |
st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
|
@@ -50,7 +51,8 @@ with tab1:
|
|
50 |
st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
|
51 |
elif sport_var == "MLB":
|
52 |
st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
|
53 |
-
|
|
|
54 |
# Create two columns for the uploader and template button
|
55 |
upload_col, template_col = st.columns([3, 1])
|
56 |
|
@@ -64,6 +66,8 @@ with tab1:
|
|
64 |
template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
|
65 |
elif sport_var == "MLB":
|
66 |
template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
|
|
|
|
|
67 |
# Add download button for template
|
68 |
st.download_button(
|
69 |
label="Template",
|
@@ -86,8 +90,11 @@ with tab2:
|
|
86 |
site_var_sb = st.selectbox("Select Site", ["Draftkings", "Fanduel"])
|
87 |
distribution_type_sb = st.selectbox("Select Distribution Type", ['normal', 'poisson', 'bimodal'])
|
88 |
st.info("The distribution type will determine the shape of the distribution of the ROO values. The normal distribution is for more linear projections, the poisson distribution is for stats like HRs and other counting stats, and the bimodal distribution is useful for event oriented outcomes frequent in MMA.")
|
89 |
-
|
90 |
-
|
|
|
|
|
|
|
91 |
std_var_sb = st.number_input("Standard Deviation (variance within distribution)", min_value=1.00, max_value=5.00, value=4.00, step=.01)
|
92 |
|
93 |
if projections_file:
|
@@ -107,6 +114,11 @@ with tab2:
|
|
107 |
disp_file = DK_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
|
108 |
elif site_var_sb == "Fanduel":
|
109 |
disp_file = FD_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
|
|
|
|
|
|
|
|
|
|
|
110 |
|
111 |
try:
|
112 |
if 'disp_file' in locals():
|
|
|
18 |
|
19 |
#bring in functions
|
20 |
from function_hold.NBA_functions import DK_NBA_ROO_Build, FD_NBA_ROO_Build
|
21 |
+
from function_hold.MMA_functions import DK_MMA_ROO_Build, FD_MMA_ROO_Build
|
22 |
|
23 |
def load_file(upload):
|
24 |
if upload is not None:
|
|
|
39 |
return None
|
40 |
return None
|
41 |
|
42 |
+
tab1, tab2 = st.tabs(["Data Load", "Create ROO"])
|
43 |
with tab1:
|
44 |
if st.button('Clear data', key='reset1'):
|
45 |
st.session_state.clear()
|
46 |
+
sport_var = st.selectbox("Select Sport", ["NBA", "NFL", "MLB", "MMA"])
|
47 |
st.subheader("Projections File")
|
48 |
if sport_var == "NBA":
|
49 |
st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
|
|
|
51 |
st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
|
52 |
elif sport_var == "MLB":
|
53 |
st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'")
|
54 |
+
elif sport_var == "MMA":
|
55 |
+
st.info("upload a projections file that has Data oriented in the following format: 'Player', 'Salary', 'Median', 'KO Potential', 'Own'")
|
56 |
# Create two columns for the uploader and template button
|
57 |
upload_col, template_col = st.columns([3, 1])
|
58 |
|
|
|
66 |
template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
|
67 |
elif sport_var == "MLB":
|
68 |
template_df = pd.DataFrame(columns=['Player', 'Team', 'Opp', 'Position', 'Salary', 'Median', 'Minutes', 'Own'])
|
69 |
+
elif sport_var == "MMA":
|
70 |
+
template_df = pd.DataFrame(columns=['Player', 'Salary', 'Median', 'KO_var', 'Own'])
|
71 |
# Add download button for template
|
72 |
st.download_button(
|
73 |
label="Template",
|
|
|
90 |
site_var_sb = st.selectbox("Select Site", ["Draftkings", "Fanduel"])
|
91 |
distribution_type_sb = st.selectbox("Select Distribution Type", ['normal', 'poisson', 'bimodal'])
|
92 |
st.info("The distribution type will determine the shape of the distribution of the ROO values. The normal distribution is for more linear projections, the poisson distribution is for stats like HRs and other counting stats, and the bimodal distribution is useful for event oriented outcomes frequent in MMA.")
|
93 |
+
if sport_var == "MMA":
|
94 |
+
st.info("MMA utilizes imputs from the projections file to determine distribution ranges, floor and ceiling are determined by Knockout potential (KO_var).")
|
95 |
+
elif sport_var != "MMA":
|
96 |
+
floor_var_sb = st.number_input("Floor (low end multiplier)", min_value=0.00, max_value=.50, value=.25, step=.01)
|
97 |
+
ceiling_var_sb = st.number_input("Ceiling (high end multiplier)", min_value=1.50, max_value=3.00, value=2.00, step=.01)
|
98 |
std_var_sb = st.number_input("Standard Deviation (variance within distribution)", min_value=1.00, max_value=5.00, value=4.00, step=.01)
|
99 |
|
100 |
if projections_file:
|
|
|
114 |
disp_file = DK_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
|
115 |
elif site_var_sb == "Fanduel":
|
116 |
disp_file = FD_MLB_ROO_Build(projections, floor_var_sb, ceiling_var_sb, std_var_sb, distribution_type_sb)
|
117 |
+
elif sport_var == "MMA":
|
118 |
+
if site_var_sb == "Draftkings":
|
119 |
+
disp_file = DK_MMA_ROO_Build(projections, std_var_sb, distribution_type_sb)
|
120 |
+
elif site_var_sb == "Fanduel":
|
121 |
+
disp_file = FD_MMA_ROO_Build(projections, std_var_sb, distribution_type_sb)
|
122 |
|
123 |
try:
|
124 |
if 'disp_file' in locals():
|
function_hold/MMA_functions.py
ADDED
@@ -0,0 +1,448 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from numpy import nan as np_nan
|
2 |
+
from numpy import where as np_where
|
3 |
+
from numpy import random as np_random
|
4 |
+
from numpy import zeros as np_zeros
|
5 |
+
from numpy import array as np_array
|
6 |
+
from pandas import concat as pd_concat
|
7 |
+
from pandas import merge as pd_merge
|
8 |
+
from pandas import DataFrame
|
9 |
+
|
10 |
+
def DK_MMA_ROO_Build(projections_file, std_var, distribution_type):
|
11 |
+
total_sims = 1000
|
12 |
+
|
13 |
+
projects_raw = projections_file.copy()
|
14 |
+
projects_raw = projects_raw.replace("", np_nan)
|
15 |
+
dk_df = projects_raw.sort_values(by='Median', ascending=False)
|
16 |
+
|
17 |
+
basic_own_df = dk_df.copy()
|
18 |
+
|
19 |
+
def calculate_ownership(df):
|
20 |
+
# Filter the dataframe based on the position
|
21 |
+
frame = df.copy()
|
22 |
+
|
23 |
+
# Calculate Small Field Own%
|
24 |
+
frame['Base Own%'] = np_where(
|
25 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
26 |
+
frame['Own'] * (5 * (frame['Own'] - (frame['Own'].mean() / 1.5)) / 100) + frame['Own'].mean(),
|
27 |
+
frame['Own']
|
28 |
+
)
|
29 |
+
frame['Base Own%'] = np_where(
|
30 |
+
frame['Base Own%'] > 85,
|
31 |
+
85,
|
32 |
+
frame['Base Own%']
|
33 |
+
)
|
34 |
+
|
35 |
+
# Calculate Small Field Own%
|
36 |
+
frame['Small Field Own%'] = np_where(
|
37 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
38 |
+
frame['Own'] * (6 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
|
39 |
+
frame['Own']
|
40 |
+
)
|
41 |
+
frame['Small Field Own%'] = np_where(
|
42 |
+
frame['Small Field Own%'] > 85,
|
43 |
+
85,
|
44 |
+
frame['Small Field Own%']
|
45 |
+
)
|
46 |
+
|
47 |
+
# Calculate Large Field Own%
|
48 |
+
frame['Large Field Own%'] = np_where(
|
49 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
50 |
+
frame['Own'] * (2.5 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
|
51 |
+
frame['Own']
|
52 |
+
)
|
53 |
+
frame['Large Field Own%'] = np_where(
|
54 |
+
frame['Large Field Own%'] > 85,
|
55 |
+
85,
|
56 |
+
frame['Large Field Own%']
|
57 |
+
)
|
58 |
+
|
59 |
+
# Calculate Cash Own%
|
60 |
+
frame['Cash Own%'] = np_where(
|
61 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
62 |
+
frame['Own'] * (8 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
|
63 |
+
frame['Own']
|
64 |
+
)
|
65 |
+
frame['Cash Own%'] = np_where(
|
66 |
+
frame['Cash Own%'] > 85,
|
67 |
+
85,
|
68 |
+
frame['Cash Own%']
|
69 |
+
)
|
70 |
+
|
71 |
+
return frame
|
72 |
+
|
73 |
+
# Apply the function to each dataframe
|
74 |
+
basic_own_df = calculate_ownership(basic_own_df)
|
75 |
+
|
76 |
+
own_norm_var_reg = 600 / basic_own_df['Own'].sum()
|
77 |
+
own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
|
78 |
+
own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
|
79 |
+
own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
|
80 |
+
basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
|
81 |
+
basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
|
82 |
+
basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
|
83 |
+
basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
|
84 |
+
|
85 |
+
basic_own_df['Own'] = np_where(basic_own_df['Own'] > 90, 90, basic_own_df['Own'])
|
86 |
+
|
87 |
+
# Apply the function to each dataframe
|
88 |
+
basic_own_df = calculate_ownership(basic_own_df)
|
89 |
+
|
90 |
+
own_norm_var_reg = 600 / basic_own_df['Own'].sum()
|
91 |
+
own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
|
92 |
+
own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
|
93 |
+
own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
|
94 |
+
basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
|
95 |
+
basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
|
96 |
+
basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
|
97 |
+
basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
|
98 |
+
|
99 |
+
own_dict = dict(zip(basic_own_df.Player, basic_own_df.Own))
|
100 |
+
small_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Small Field Own%']))
|
101 |
+
large_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Large Field Own%']))
|
102 |
+
cash_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Cash Own%']))
|
103 |
+
min_dict = dict(zip(basic_own_df.Player, basic_own_df.KO_var))
|
104 |
+
|
105 |
+
flex_file = basic_own_df[['Player', 'Salary', 'Median', 'KO_var']]
|
106 |
+
flex_file = flex_file.rename(columns={"Agg": "Median"})
|
107 |
+
flex_file['Floor'] = flex_file['Median'] * (1-flex_file['KO_var'])
|
108 |
+
flex_file['Ceiling'] = flex_file['Median'] * (1+flex_file['KO_var'])
|
109 |
+
flex_file['STD'] = (flex_file['Median'] / std_var)
|
110 |
+
flex_file = flex_file[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD']]
|
111 |
+
flex_file = flex_file.reset_index(drop=True)
|
112 |
+
hold_file = flex_file.copy()
|
113 |
+
overall_file = flex_file.copy()
|
114 |
+
salary_file = flex_file.copy()
|
115 |
+
|
116 |
+
try:
|
117 |
+
overall_floor_gpu = np_array(overall_file['Floor'])
|
118 |
+
overall_ceiling_gpu = np_array(overall_file['Ceiling'])
|
119 |
+
overall_median_gpu = np_array(overall_file['Median'])
|
120 |
+
overall_std_gpu = np_array(overall_file['STD'])
|
121 |
+
overall_salary_gpu = np_array(overall_file['Salary'])
|
122 |
+
|
123 |
+
data_shape = (len(overall_file['Player']), total_sims) # Example: 1000 rows
|
124 |
+
salary_array = np_zeros(data_shape)
|
125 |
+
sim_array = np_zeros(data_shape)
|
126 |
+
|
127 |
+
for x in range(0, total_sims):
|
128 |
+
result_gpu = overall_salary_gpu
|
129 |
+
salary_array[:, x] = result_gpu
|
130 |
+
cupy_array = salary_array
|
131 |
+
|
132 |
+
salary_file = salary_file.reset_index(drop=True)
|
133 |
+
salary_cupy = DataFrame(cupy_array, columns=list(range(0, total_sims)))
|
134 |
+
salary_check_file = pd_concat([salary_file, salary_cupy], axis=1)
|
135 |
+
except:
|
136 |
+
for x in range(0,total_sims):
|
137 |
+
salary_file[x] = salary_file['Salary']
|
138 |
+
salary_check_file = salary_file.copy()
|
139 |
+
|
140 |
+
salary_file=salary_check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
|
141 |
+
|
142 |
+
salary_file = salary_file.div(1000)
|
143 |
+
|
144 |
+
try:
|
145 |
+
for x in range(0, total_sims):
|
146 |
+
if distribution_type == 'normal':
|
147 |
+
# Normal distribution (existing logic)
|
148 |
+
result_gpu = np_random.normal(overall_median_gpu, overall_std_gpu)
|
149 |
+
elif distribution_type == 'poisson':
|
150 |
+
# Poisson distribution - using median as lambda
|
151 |
+
result_gpu = np_random.poisson(overall_median_gpu)
|
152 |
+
elif distribution_type == 'bimodal':
|
153 |
+
# Bimodal distribution - mixture of two normal distributions
|
154 |
+
# First peak centered at 80% of median, second at 120% of median
|
155 |
+
if np_random.random() < 0.5:
|
156 |
+
result_gpu = np_random.normal(overall_floor_gpu, overall_std_gpu)
|
157 |
+
else:
|
158 |
+
result_gpu = np_random.normal(overall_ceiling_gpu, overall_std_gpu)
|
159 |
+
else:
|
160 |
+
raise ValueError("Invalid distribution type. Must be 'normal', 'poisson', or 'bimodal'")
|
161 |
+
|
162 |
+
sim_array[:, x] = result_gpu
|
163 |
+
add_array = sim_array
|
164 |
+
|
165 |
+
overall_file = overall_file.reset_index(drop=True)
|
166 |
+
df2 = DataFrame(add_array, columns=list(range(0, total_sims)))
|
167 |
+
check_file = pd_concat([overall_file, df2], axis=1)
|
168 |
+
except:
|
169 |
+
for x in range(0,total_sims):
|
170 |
+
if distribution_type == 'normal':
|
171 |
+
overall_file[x] = np_random.normal(overall_file['Median'], overall_file['STD'])
|
172 |
+
elif distribution_type == 'poisson':
|
173 |
+
overall_file[x] = np_random.poisson(overall_file['Median'])
|
174 |
+
elif distribution_type == 'bimodal':
|
175 |
+
# Bimodal distribution fallback
|
176 |
+
if np_random.random() < 0.5:
|
177 |
+
overall_file[x] = np_random.normal(overall_file['Floor'], overall_file['STD'])
|
178 |
+
else:
|
179 |
+
overall_file[x] = np_random.normal(overall_file['Ceiling'], overall_file['STD'])
|
180 |
+
check_file = overall_file.copy()
|
181 |
+
|
182 |
+
overall_file=check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
|
183 |
+
|
184 |
+
players_only = hold_file[['Player']]
|
185 |
+
raw_lineups_file = players_only
|
186 |
+
|
187 |
+
for x in range(0,total_sims):
|
188 |
+
maps_dict = {'proj_map':dict(zip(hold_file.Player,overall_file[x]))}
|
189 |
+
raw_lineups_file[x] = sum([raw_lineups_file['Player'].map(maps_dict['proj_map'])])
|
190 |
+
players_only[x] = raw_lineups_file[x].rank(ascending=False)
|
191 |
+
|
192 |
+
players_only=players_only.drop(['Player'], axis=1)
|
193 |
+
|
194 |
+
salary_4x_check = (overall_file - (salary_file*4))
|
195 |
+
salary_5x_check = (overall_file - (salary_file*5))
|
196 |
+
salary_6x_check = (overall_file - (salary_file*6))
|
197 |
+
gpp_check = (overall_file - ((salary_file*5)+10))
|
198 |
+
|
199 |
+
players_only['Average_Rank'] = players_only.mean(axis=1)
|
200 |
+
players_only['Top_finish'] = players_only[players_only == 1].count(axis=1)/total_sims
|
201 |
+
players_only['Top_5_finish'] = players_only[players_only <= 5].count(axis=1)/total_sims
|
202 |
+
players_only['Top_10_finish'] = players_only[players_only <= 10].count(axis=1)/total_sims
|
203 |
+
players_only['20+%'] = overall_file[overall_file >= 20].count(axis=1)/float(total_sims)
|
204 |
+
players_only['4x%'] = salary_4x_check[salary_4x_check >= 1].count(axis=1)/float(total_sims)
|
205 |
+
players_only['5x%'] = salary_5x_check[salary_5x_check >= 1].count(axis=1)/float(total_sims)
|
206 |
+
players_only['6x%'] = salary_6x_check[salary_6x_check >= 1].count(axis=1)/float(total_sims)
|
207 |
+
players_only['GPP%'] = gpp_check[gpp_check >= 1].count(axis=1)/float(total_sims)
|
208 |
+
|
209 |
+
players_only['Player'] = hold_file[['Player']]
|
210 |
+
|
211 |
+
final_outcomes = players_only[['Player', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
|
212 |
+
|
213 |
+
final_Proj = pd_merge(hold_file, final_outcomes, on="Player")
|
214 |
+
final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
|
215 |
+
|
216 |
+
final_Proj['name_team'] = final_Proj['Player'] + final_Proj['Position']
|
217 |
+
final_Proj['Own'] = final_Proj['Player'].map(own_dict)
|
218 |
+
final_Proj['Small_Own'] = final_Proj['Player'].map(small_own_dict)
|
219 |
+
final_Proj['Large_Own'] = final_Proj['Player'].map(large_own_dict)
|
220 |
+
final_Proj['Cash_Own'] = final_Proj['Player'].map(cash_own_dict)
|
221 |
+
final_Proj['CPT_Own'] = final_Proj['Own'] / 6
|
222 |
+
final_Proj['LevX'] = ((final_Proj[['Top_finish', '6x%', 'Top_5_finish']].mean(axis=1))*100) - final_Proj['Own']
|
223 |
+
final_Proj['ValX'] = ((final_Proj[['5x%', '6x%']].mean(axis=1))*100) + final_Proj['LevX']
|
224 |
+
|
225 |
+
final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%', 'Own', 'Small_Own', 'Large_Own', 'Cash_Own', 'CPT_Own', 'LevX', 'ValX']]
|
226 |
+
final_Proj = final_Proj.sort_values(by='Median', ascending=False)
|
227 |
+
|
228 |
+
return final_Proj.copy()
|
229 |
+
|
230 |
+
def FD_MMA_ROO_Build(projections_file, std_var, distribution_type):
|
231 |
+
total_sims = 1000
|
232 |
+
|
233 |
+
projects_raw = projections_file.copy()
|
234 |
+
fd_df = projects_raw.sort_values(by='Median', ascending=False)
|
235 |
+
|
236 |
+
basic_own_df = fd_df.copy()
|
237 |
+
|
238 |
+
def calculate_ownership(df):
|
239 |
+
# Filter the dataframe based on the position
|
240 |
+
frame = df.copy()
|
241 |
+
|
242 |
+
# Calculate Small Field Own%
|
243 |
+
frame['Base Own%'] = np_where(
|
244 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
245 |
+
frame['Own'] * (5 * (frame['Own'] - (frame['Own'].mean() / 1.5)) / 100) + frame['Own'].mean(),
|
246 |
+
frame['Own']
|
247 |
+
)
|
248 |
+
frame['Base Own%'] = np_where(
|
249 |
+
frame['Base Own%'] > 85,
|
250 |
+
85,
|
251 |
+
frame['Base Own%']
|
252 |
+
)
|
253 |
+
|
254 |
+
# Calculate Small Field Own%
|
255 |
+
frame['Small Field Own%'] = np_where(
|
256 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
257 |
+
frame['Own'] * (6 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
|
258 |
+
frame['Own']
|
259 |
+
)
|
260 |
+
frame['Small Field Own%'] = np_where(
|
261 |
+
frame['Small Field Own%'] > 85,
|
262 |
+
85,
|
263 |
+
frame['Small Field Own%']
|
264 |
+
)
|
265 |
+
|
266 |
+
# Calculate Large Field Own%
|
267 |
+
frame['Large Field Own%'] = np_where(
|
268 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
269 |
+
frame['Own'] * (2.5 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
|
270 |
+
frame['Own']
|
271 |
+
)
|
272 |
+
frame['Large Field Own%'] = np_where(
|
273 |
+
frame['Large Field Own%'] > 85,
|
274 |
+
85,
|
275 |
+
frame['Large Field Own%']
|
276 |
+
)
|
277 |
+
|
278 |
+
# Calculate Cash Own%
|
279 |
+
frame['Cash Own%'] = np_where(
|
280 |
+
(frame['Own'] - frame['Own'].mean() >= 0),
|
281 |
+
frame['Own'] * (8 * (frame['Own'] - frame['Own'].mean()) / 100) + frame['Own'].mean(),
|
282 |
+
frame['Own']
|
283 |
+
)
|
284 |
+
frame['Cash Own%'] = np_where(
|
285 |
+
frame['Cash Own%'] > 85,
|
286 |
+
85,
|
287 |
+
frame['Cash Own%']
|
288 |
+
)
|
289 |
+
|
290 |
+
return frame
|
291 |
+
|
292 |
+
# Apply the function to each dataframe
|
293 |
+
basic_own_df = calculate_ownership(basic_own_df)
|
294 |
+
|
295 |
+
own_norm_var_reg = 600 / basic_own_df['Own'].sum()
|
296 |
+
own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
|
297 |
+
own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
|
298 |
+
own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
|
299 |
+
basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
|
300 |
+
basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
|
301 |
+
basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
|
302 |
+
basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
|
303 |
+
|
304 |
+
basic_own_df['Own'] = np_where(basic_own_df['Own'] > 90, 90, basic_own_df['Own'])
|
305 |
+
|
306 |
+
# Apply the function to each dataframe
|
307 |
+
basic_own_df = calculate_ownership(basic_own_df)
|
308 |
+
|
309 |
+
own_norm_var_reg = 600 / basic_own_df['Own'].sum()
|
310 |
+
own_norm_var_small = 600 / basic_own_df['Small Field Own%'].sum()
|
311 |
+
own_norm_var_large = 600 / basic_own_df['Large Field Own%'].sum()
|
312 |
+
own_norm_var_cash = 600 / basic_own_df['Cash Own%'].sum()
|
313 |
+
basic_own_df['Own'] = basic_own_df['Own'] * own_norm_var_reg
|
314 |
+
basic_own_df['Small_Own'] = basic_own_df['Small Field Own%'] * own_norm_var_small
|
315 |
+
basic_own_df['Large_Own'] = basic_own_df['Large Field Own%'] * own_norm_var_large
|
316 |
+
basic_own_df['Cash_Own'] = basic_own_df['Cash Own%'] * own_norm_var_cash
|
317 |
+
|
318 |
+
own_dict = dict(zip(basic_own_df.Player, basic_own_df.Own))
|
319 |
+
small_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Small Field Own%']))
|
320 |
+
large_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Large Field Own%']))
|
321 |
+
cash_own_dict = dict(zip(basic_own_df.Player, basic_own_df['Cash Own%']))
|
322 |
+
min_dict = dict(zip(basic_own_df.Player, basic_own_df.KO_var))
|
323 |
+
|
324 |
+
flex_file = basic_own_df[['Player', 'Salary', 'Median', 'KO_var']]
|
325 |
+
flex_file = flex_file.rename(columns={"Agg": "Median"})
|
326 |
+
flex_file['Floor'] = flex_file['Median'] * (1-flex_file['KO_var'])
|
327 |
+
flex_file['Ceiling'] = flex_file['Median'] * (1+flex_file['KO_var'])
|
328 |
+
flex_file['STD'] = (flex_file['Median'] / std_var)
|
329 |
+
flex_file = flex_file[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD']]
|
330 |
+
flex_file = flex_file.reset_index(drop=True)
|
331 |
+
hold_file = flex_file.copy()
|
332 |
+
overall_file = flex_file.copy()
|
333 |
+
salary_file = flex_file.copy()
|
334 |
+
|
335 |
+
try:
|
336 |
+
overall_floor_gpu = np_array(overall_file['Floor'])
|
337 |
+
overall_ceiling_gpu = np_array(overall_file['Ceiling'])
|
338 |
+
overall_median_gpu = np_array(overall_file['Median'])
|
339 |
+
overall_std_gpu = np_array(overall_file['STD'])
|
340 |
+
overall_salary_gpu = np_array(overall_file['Salary'])
|
341 |
+
|
342 |
+
data_shape = (len(overall_file['Player']), total_sims) # Example: 1000 rows
|
343 |
+
salary_array = np_zeros(data_shape)
|
344 |
+
sim_array = np_zeros(data_shape)
|
345 |
+
|
346 |
+
for x in range(0, total_sims):
|
347 |
+
result_gpu = overall_salary_gpu
|
348 |
+
salary_array[:, x] = result_gpu
|
349 |
+
cupy_array = salary_array
|
350 |
+
|
351 |
+
salary_file = salary_file.reset_index(drop=True)
|
352 |
+
salary_cupy = DataFrame(cupy_array, columns=list(range(0, total_sims)))
|
353 |
+
salary_check_file = pd_concat([salary_file, salary_cupy], axis=1)
|
354 |
+
except:
|
355 |
+
for x in range(0,total_sims):
|
356 |
+
salary_file[x] = salary_file['Salary']
|
357 |
+
salary_check_file = salary_file.copy()
|
358 |
+
|
359 |
+
salary_file=salary_check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
|
360 |
+
|
361 |
+
salary_file = salary_file.div(1000)
|
362 |
+
|
363 |
+
try:
|
364 |
+
for x in range(0, total_sims):
|
365 |
+
if distribution_type == 'normal':
|
366 |
+
# Normal distribution (existing logic)
|
367 |
+
result_gpu = np_random.normal(overall_median_gpu, overall_std_gpu)
|
368 |
+
elif distribution_type == 'poisson':
|
369 |
+
# Poisson distribution - using median as lambda
|
370 |
+
result_gpu = np_random.poisson(overall_median_gpu)
|
371 |
+
elif distribution_type == 'bimodal':
|
372 |
+
# Bimodal distribution - mixture of two normal distributions
|
373 |
+
# First peak centered at 80% of median, second at 120% of median
|
374 |
+
if np_random.random() < 0.5:
|
375 |
+
result_gpu = np_random.normal(overall_floor_gpu, overall_std_gpu)
|
376 |
+
else:
|
377 |
+
result_gpu = np_random.normal(overall_ceiling_gpu, overall_std_gpu)
|
378 |
+
else:
|
379 |
+
raise ValueError("Invalid distribution type. Must be 'normal', 'poisson', or 'bimodal'")
|
380 |
+
|
381 |
+
sim_array[:, x] = result_gpu
|
382 |
+
add_array = sim_array
|
383 |
+
|
384 |
+
overall_file = overall_file.reset_index(drop=True)
|
385 |
+
df2 = DataFrame(add_array, columns=list(range(0, total_sims)))
|
386 |
+
check_file = pd_concat([overall_file, df2], axis=1)
|
387 |
+
except:
|
388 |
+
for x in range(0,total_sims):
|
389 |
+
if distribution_type == 'normal':
|
390 |
+
overall_file[x] = np_random.normal(overall_file['Median'], overall_file['STD'])
|
391 |
+
elif distribution_type == 'poisson':
|
392 |
+
overall_file[x] = np_random.poisson(overall_file['Median'])
|
393 |
+
elif distribution_type == 'bimodal':
|
394 |
+
# Bimodal distribution fallback
|
395 |
+
if np_random.random() < 0.5:
|
396 |
+
overall_file[x] = np_random.normal(overall_file['Floor'], overall_file['STD'])
|
397 |
+
else:
|
398 |
+
overall_file[x] = np_random.normal(overall_file['Ceiling'], overall_file['STD'])
|
399 |
+
check_file = overall_file.copy()
|
400 |
+
|
401 |
+
overall_file=check_file.drop(['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
|
402 |
+
|
403 |
+
players_only = hold_file[['Player']]
|
404 |
+
raw_lineups_file = players_only
|
405 |
+
|
406 |
+
for x in range(0,total_sims):
|
407 |
+
maps_dict = {'proj_map':dict(zip(hold_file.Player,overall_file[x]))}
|
408 |
+
raw_lineups_file[x] = sum([raw_lineups_file['Player'].map(maps_dict['proj_map'])])
|
409 |
+
players_only[x] = raw_lineups_file[x].rank(ascending=False)
|
410 |
+
|
411 |
+
players_only=players_only.drop(['Player'], axis=1)
|
412 |
+
|
413 |
+
salary_4x_check = (overall_file - (salary_file*4))
|
414 |
+
salary_5x_check = (overall_file - (salary_file*5))
|
415 |
+
salary_6x_check = (overall_file - (salary_file*6))
|
416 |
+
gpp_check = (overall_file - ((salary_file*5)+10))
|
417 |
+
|
418 |
+
players_only['Average_Rank'] = players_only.mean(axis=1)
|
419 |
+
players_only['Top_finish'] = players_only[players_only == 1].count(axis=1)/total_sims
|
420 |
+
players_only['Top_5_finish'] = players_only[players_only <= 5].count(axis=1)/total_sims
|
421 |
+
players_only['Top_10_finish'] = players_only[players_only <= 10].count(axis=1)/total_sims
|
422 |
+
players_only['20+%'] = overall_file[overall_file >= 20].count(axis=1)/float(total_sims)
|
423 |
+
players_only['4x%'] = salary_4x_check[salary_4x_check >= 1].count(axis=1)/float(total_sims)
|
424 |
+
players_only['5x%'] = salary_5x_check[salary_5x_check >= 1].count(axis=1)/float(total_sims)
|
425 |
+
players_only['6x%'] = salary_6x_check[salary_6x_check >= 1].count(axis=1)/float(total_sims)
|
426 |
+
players_only['GPP%'] = gpp_check[gpp_check >= 1].count(axis=1)/float(total_sims)
|
427 |
+
|
428 |
+
players_only['Player'] = hold_file[['Player']]
|
429 |
+
|
430 |
+
final_outcomes = players_only[['Player', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
|
431 |
+
|
432 |
+
final_Proj = pd_merge(hold_file, final_outcomes, on="Player")
|
433 |
+
final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%']]
|
434 |
+
|
435 |
+
final_Proj['name_team'] = final_Proj['Player'] + final_Proj['Position']
|
436 |
+
final_Proj['Own'] = final_Proj['Player'].map(own_dict)
|
437 |
+
final_Proj['Small_Own'] = final_Proj['Player'].map(small_own_dict)
|
438 |
+
final_Proj['Large_Own'] = final_Proj['Player'].map(large_own_dict)
|
439 |
+
final_Proj['Cash_Own'] = final_Proj['Player'].map(cash_own_dict)
|
440 |
+
final_Proj['CPT_Own'] = final_Proj['Own'] / 6
|
441 |
+
final_Proj['LevX'] = ((final_Proj[['Top_finish', '6x%', 'Top_5_finish']].mean(axis=1))*100) - final_Proj['Own']
|
442 |
+
final_Proj['ValX'] = ((final_Proj[['5x%', '6x%']].mean(axis=1))*100) + final_Proj['LevX']
|
443 |
+
|
444 |
+
final_Proj = final_Proj[['Player', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '4x%', '5x%', '6x%', 'GPP%', 'Own', 'Small_Own', 'Large_Own', 'Cash_Own', 'CPT_Own', 'LevX', 'ValX']]
|
445 |
+
final_Proj['Salary'] = final_Proj['Salary'].astype(int)
|
446 |
+
final_Proj = final_Proj.sort_values(by='Median', ascending=False)
|
447 |
+
|
448 |
+
return final_Proj.copy()
|