hectorjelly Cyllum AI commited on
Commit
75d0ed2
·
0 Parent(s):

Duplicate from cyllum/soccertwos-analytics

Browse files

Co-authored-by: Cyllum AI <[email protected]>

Files changed (6) hide show
  1. .gitignore +134 -0
  2. .streamlit/config.toml +9 -0
  3. Dockerfile +21 -0
  4. README.md +14 -0
  5. app.py +206 -0
  6. requirements.txt +5 -0
.gitignore ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ pip-wheel-metadata/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ .python-version
87
+
88
+ # pipenv
89
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
90
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
91
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
92
+ # install all needed dependencies.
93
+ #Pipfile.lock
94
+
95
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
96
+ __pypackages__/
97
+
98
+ # Celery stuff
99
+ celerybeat-schedule
100
+ celerybeat.pid
101
+
102
+ # SageMath parsed files
103
+ *.sage.py
104
+
105
+ # Environments
106
+ .env
107
+ .venv
108
+ env/
109
+ venv/
110
+ ENV/
111
+ env.bak/
112
+ venv.bak/
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+
132
+ .vscode/
133
+
134
+ data/audio/
.streamlit/config.toml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ [theme]
2
+ primaryColor="#e5ab00"
3
+ backgroundColor="#0e1117"
4
+ secondaryBackgroundColor="#282929"
5
+ textColor = "#ffffff"
6
+ font="sans serif"
7
+
8
+ [server]
9
+ maxUploadSize = 200
Dockerfile ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY ./requirements.txt /app/requirements.txt
6
+
7
+ RUN pip3 install --no-cache-dir -r /app/requirements.txt
8
+
9
+ # User
10
+ RUN useradd -m -u 1000 user
11
+ USER user
12
+ ENV HOME /home/user
13
+ ENV PATH $HOME/.local/bin:$PATH
14
+
15
+ WORKDIR $HOME
16
+ RUN mkdir app
17
+ WORKDIR $HOME/app
18
+ COPY . $HOME/app
19
+
20
+ EXPOSE 8501
21
+ CMD streamlit run app.py
README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: SoccerTwos Analytics
3
+ emoji: ⚽
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ app_port: 8501
8
+ pinned: true
9
+ duplicated_from: cyllum/soccertwos-analytics
10
+ ---
11
+
12
+ ## SoccerTwos Analytics
13
+
14
+ A dashboard for analysing your team's performance in the [SoccerTwos competition](https://huggingface.co/spaces/huggingface-projects/AIvsAI-SoccerTwos).
app.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+
3
+ import pandas as pd
4
+ import streamlit as st
5
+ import timeago
6
+ import plotly.graph_objects as go
7
+
8
+
9
+ st.set_page_config(layout="wide")
10
+ st.markdown(
11
+ """
12
+ <style>
13
+ .block-container {
14
+ padding-top: 1rem;
15
+ }
16
+ #MainMenu {visibility: hidden;}
17
+ </style>
18
+ """,
19
+ unsafe_allow_html=True
20
+ )
21
+
22
+ now = datetime.datetime.now()
23
+ MATCH_RESULTS_URL = "https://huggingface.co/datasets/huggingface-projects/bot-fight-data/raw/main/soccer_history.csv"
24
+
25
+
26
+ @st.cache_data(ttl=1800)
27
+ def fetch_match_history():
28
+ """
29
+ Fetch match history.
30
+ Cache the result for 30min to avoid unnecessary requests.
31
+ Return a DataFrame.
32
+ """
33
+ df = pd.read_csv(MATCH_RESULTS_URL)
34
+ df["timestamp"] = pd.to_datetime(df.timestamp, unit="s")
35
+ df.columns = ["home", "away", "timestamp", "result"]
36
+ return df
37
+
38
+
39
+ def days_left():
40
+ end_date = datetime.date(2023, 4, 30)
41
+ today = datetime.date.today()
42
+ time_until_date = end_date - today
43
+ return time_until_date.days
44
+
45
+
46
+ def num_matches_played():
47
+ return match_df.shape[0]
48
+
49
+
50
+ match_df = fetch_match_history()
51
+ teams = sorted(
52
+ list(pd.concat([match_df["home"], match_df["away"]]).unique()), key=str.casefold
53
+ )
54
+
55
+ st.title("🤗 SoccerTwos Challenge Analytics")
56
+
57
+ team_results = {}
58
+ for i, row in match_df.iterrows():
59
+ home_team = row["home"]
60
+ away_team = row["away"]
61
+ result = row["result"]
62
+
63
+ if home_team not in team_results:
64
+ team_results[home_team] = [0, 0, 0]
65
+
66
+ if away_team not in team_results:
67
+ team_results[away_team] = [0, 0, 0]
68
+
69
+ if result == 0:
70
+ team_results[home_team][2] += 1
71
+ team_results[away_team][0] += 1
72
+ elif result == 1:
73
+ team_results[home_team][0] += 1
74
+ team_results[away_team][2] += 1
75
+ else:
76
+ team_results[home_team][1] += 1
77
+ team_results[away_team][1] += 1
78
+
79
+
80
+ df = pd.DataFrame.from_dict(
81
+ team_results, orient="index", columns=["wins", "draws", "losses"]
82
+ ).sort_index()
83
+ df[["owner", "team"]] = df.index.to_series().str.split("/", expand=True)
84
+ df = df[["owner", "team", "wins", "draws", "losses"]]
85
+ df["win_pct"] = (df["wins"] / (df["wins"] + df["draws"] + df["losses"])) * 100
86
+
87
+ stats = df
88
+
89
+ tab_team, tab_competition = st.tabs(["Results", "Competition stats"])
90
+
91
+
92
+ def get_text_result(row, team_name):
93
+ if row["home"] == team_name:
94
+ if row["result"] == 1:
95
+ return "Win"
96
+ elif row["result"] == 0.5:
97
+ return "Draw"
98
+ else:
99
+ return "Loss"
100
+ elif row["away"] == team_name:
101
+ if row["result"] == 0:
102
+ return "Win"
103
+ elif row["result"] == 0.5:
104
+ return "Draw"
105
+ else:
106
+ return "Loss"
107
+
108
+
109
+ with tab_team:
110
+ team = st.selectbox("Team", teams)
111
+
112
+ col1, col2 = st.columns(2)
113
+
114
+ with col1:
115
+ c1, c2, c3 = st.columns(3)
116
+ with c1:
117
+ st.metric("Wins", f"{stats.loc[[team]]['wins'][0]}")
118
+ with c2:
119
+ st.metric("Draws", f"{stats.loc[[team]]['draws'][0]}")
120
+ with c3:
121
+ st.metric("Losses", f"{stats.loc[[team]]['losses'][0]}")
122
+
123
+ st.write("Results")
124
+ res_df = match_df[(match_df["home"] == team) | (match_df["away"] == team)]
125
+ res_df["result"] = res_df.apply(lambda row: get_text_result(row, team), axis=1)
126
+ opponent_column = res_df.apply(
127
+ lambda row: row["away"] if row["home"] == team else row["home"], axis=1
128
+ )
129
+ res_df["vs"] = opponent_column
130
+ result_column = res_df["result"]
131
+ new_df = pd.concat([opponent_column, result_column], axis=1)
132
+ new_df.columns = ["vs", "result"]
133
+ res_df[["owner", "team"]] = res_df["vs"].str.split("/", expand=True)
134
+ res_df["played"] = res_df["timestamp"].apply(lambda x: timeago.format(x, now))
135
+ res_df.sort_values(by=["timestamp"], ascending=True, inplace=True)
136
+ disp_res_df = res_df.drop(["home", "away", "vs", "timestamp"], axis=1)
137
+
138
+ def highlight_results(s):
139
+ colour = {
140
+ "Win": "LightGreen",
141
+ "Draw": "LightYellow",
142
+ "Loss": "LightSalmon",
143
+ }
144
+ return [f"background-color: {colour[s.result]}"] * len(s)
145
+
146
+ # Create a friendly index.
147
+ disp_res_df.reset_index(inplace=True, drop=True)
148
+ disp_res_df.index += 1
149
+ disp_res_df = disp_res_df.iloc[::-1]
150
+
151
+ # Display the table.
152
+ st.dataframe(disp_res_df.style.apply(highlight_results, axis=1))
153
+
154
+ with col2:
155
+ c1, c2 = st.columns(2)
156
+ with c1:
157
+ st.metric("Win rate", f"{stats.loc[[team]]['win_pct'][0]:.2f}%")
158
+
159
+ joined = res_df["timestamp"].min()
160
+ with c2:
161
+ st.metric("Competing since", f"{timeago.format(joined, now)}")
162
+
163
+ grouped = (
164
+ res_df.groupby([res_df["timestamp"].dt.date, "result"])
165
+ .size()
166
+ .reset_index(name="count")
167
+ )
168
+
169
+ loss_trace = go.Bar(
170
+ x=grouped.loc[grouped["result"] == "Loss", "timestamp"],
171
+ y=grouped.loc[grouped["result"] == "Loss", "count"],
172
+ name="Losses",
173
+ marker=dict(color="red"),
174
+ )
175
+ draw_trace = go.Bar(
176
+ x=grouped.loc[grouped["result"] == "Draw", "timestamp"],
177
+ y=grouped.loc[grouped["result"] == "Draw", "count"],
178
+ name="Draws",
179
+ marker=dict(color="orange"),
180
+ )
181
+ win_trace = go.Bar(
182
+ x=grouped.loc[grouped["result"] == "Win", "timestamp"],
183
+ y=grouped.loc[grouped["result"] == "Win", "count"],
184
+ name="Wins",
185
+ marker=dict(color="green"),
186
+ )
187
+
188
+ fig = go.Figure(data=[loss_trace, draw_trace, win_trace])
189
+ fig.update_layout(barmode="stack")
190
+ st.plotly_chart(fig)
191
+
192
+
193
+ with tab_competition:
194
+ col1, col2, col3 = st.columns(3)
195
+
196
+ col1.metric("Matches played", f"{num_matches_played():,d}")
197
+ col2.metric("Live models", f"{len(teams)}")
198
+ col3.metric("Season ends in", f"{days_left()} days")
199
+
200
+ match_counts = (
201
+ match_df.groupby(match_df["timestamp"].dt.date).size().reset_index(name="count")
202
+ )
203
+ match_counts["matches_played"] = match_counts["count"].cumsum()
204
+
205
+ st.title("Matches played")
206
+ st.area_chart(match_counts.set_index("timestamp")["matches_played"])
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ streamlit
2
+ numpy
3
+ pandas
4
+ plotly
5
+ timeago