-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
139 lines (121 loc) · 5.36 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import streamlit as st
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from plot import plot_pitch
# ADDING CACHE OPTIONS
@st.cache
def load_data():
return pd.read_csv('data/AppData.gz', encoding='utf-8-sig')
df = load_data()
@st.cache
def pre_format_data():
df_copy = df.copy()
df_copy['isGoal'] = df_copy['isGoal'].apply(abs)
return df_copy
df_fil = pre_format_data()
st.title("All About GameStates")
st.text("An app for finding Understat player by player shot data based on their shooting\nand finishing performance in different gamestates")
st.caption("Made by Shreyas Khatri. Twitter: @khatri_shreyas", unsafe_allow_html=False)
# WIDGETS SIDEBAR
st.sidebar.header("PLAYER INPUTS")
player_id = st.sidebar.selectbox("Input Understat Player Name and ID:",
options= sorted(df['Player ID'].unique()),
index = 3178,
)
start_year, end_year = st.sidebar.select_slider(
"Select Range of Seasons:",
options=[x for x in range(2014, 2023)],
value=(2014,2021)
)
filter_gs = st.sidebar.multiselect(
"Filter for Game States:",
options = df['Filter Game_State'].unique().tolist(),
default = df['Filter Game_State'].unique().tolist()
)
player_col = st.sidebar.color_picker('Pick A Color:', '#EA2304')
theme = st.sidebar.radio(
"Visualisation Theme:",
('dark', 'light')
)
goal_alpha = st.sidebar.number_input('Change Transparency of Goal Scatter Points:',
max_value=1.0, min_value=0.0, value=0.5)
only_goals = st.sidebar.checkbox('Show Only Goals')
add_pens = st.sidebar.checkbox('Keep Penalties')
only_changed_gs = st.sidebar.checkbox('Show Only Game State Altering Moments')
# FILTER DF
df_fil = df_fil[df_fil['Player ID']==player_id]
df_fil = df_fil[df_fil['Season'].between(start_year, end_year)]
df_fil = df_fil[df_fil['Filter Game_State'].isin(filter_gs)]
if only_goals:
df_fil = df_fil[df_fil['Shot Result']=='Goal']
if not add_pens:
df_fil = df_fil[df_fil['Situation']!='Penalty']
if only_changed_gs:
df_fil = df_fil[df_fil['Changed Game State']==True]
def find_distance(x, y):
return np.sqrt(np.sum(np.square(np.array([100, 50])-np.array([x*100, y*100]))))
# GROUPBY DF
def generate_gb_df():
df_gb = df_fil[['Player ID','x','y', 'Shot xG','isGoal','Filter Game_State']].groupby("Filter Game_State").agg(['sum', 'count'])
df_gb['Distance'] = df_gb.apply(lambda x: find_distance(x['x']['sum']/x['x']['count'], x['y']['sum']/x['y']['count']), axis=1)
df_gb = df_gb.drop([('Player ID', 'sum'), ( 'Shot xG', 'count'), ( 'isGoal', 'count'),
( 'x', 'count'), ( 'x', 'sum'), ( 'y', 'count'), ( 'y', 'sum')], axis=1)
df_gb.columns = ['Total Shots', 'Total xG', 'Total Goals', 'Distance From Goal']
df_gb['xG per Shot'] = df_gb['Total xG']/df_gb['Total Shots']
df_gb['xG Overperformance'] = df_gb['Total Goals'] - df_gb['Total xG']
df_gb['xG O/P per Shot'] = df_gb['xG Overperformance']/df_gb['Total Shots']
return df_gb
def convert_df(df):
# IMPORTANT: Cache the conversion to prevent computation on every rerun
return df.to_csv().encode('utf-8-sig')
# INTERFACE
if len(df_fil) > 0:
st.header("\n\nPlayer: {}".format(df_fil['Player'].unique()[0]))
st.subheader("\nPLAYER SHOT BY GAMESTATE COMPARISON")
st.table(generate_gb_df())
else:
st.error("Sorry no entries with these inputs, please change your inputs")
if st.button('Click For Visualisation Generation'):
st.header("PLAYER SHOT DATAFRAME")
st.dataframe(df_fil.drop(['x','y', 'h_a', 'isGoal', 'Player ID', 'Filter Game_State'], axis=1).reset_index(drop=True),
width = 2000, height=250)
df_fil_save = convert_df(df_fil)
st.download_button(
label="Download Shot DataFrame as CSV",
data=df_fil_save,
file_name='PlayerShots.csv',
mime='text/csv',
)
theme_dict = {'light':'white', 'dark':'black'}
st.header("\nPLAYER SHOT MAP")
fig, ax = plot_pitch(df_fil, theme=theme, player_col=player_col, alpha=goal_alpha)
plt.savefig('static/PlayerShots.png', dpi=300, facecolor=theme_dict[theme])
st.pyplot(fig)
with open("static/PlayerShots.png", "rb") as file:
btn = st.download_button(
label="Download image",
data=file,
file_name="PlayerShots.png",
mime="image/png"
)
# GUIDE AND CREDITS
st.markdown("""
### GUIDE TO THE WIDGET ELEMENTS
1. Player ID - Player Name and ID for player on Understat.com (https://www.understat.com)
2. Range Of Season - Filter for seasons of relevance
3. Filter Game State - Shots during a particular type (or types) of game state
4. Show Only Goals - Filter for only goals
5. Keep Penalties - Do you want to keep penalties or not?
6. Game State Altering Moments - Show only those goals which altered the game state
7. Pick A Colour - Choose colour for goal scatter points
8. Visualisation Theme - Choose between light and dark themed templates
9. Change Transparency - Adjust transparency parameter for goal scatter points in shotmap
""")
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)