Skip to content

Commit 1389f95

Browse files
author
Xing Han Lu
authored
Merge pull request #565 from plotly/add-soccer-analytics
Add soccer analytics demo Former-commit-id: 6ec68e5
2 parents 6e2a10b + 8cee98b commit 1389f95

20 files changed

+3785
-0
lines changed

apps/dbc-soccer-analytics/Procfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: gunicorn app:server

apps/dbc-soccer-analytics/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Soccer Match Analytics
2+
3+
## Overview
4+
5+
* `Soccer Match Analytics` helps coaches and analysts to analyze the events and view a digital recreation of action in a single match
6+
7+
## Usage
8+
The app is actually comprised of two parts: a visual app and a game animation preparation script. This pre-preparation of animated game activity is necessary in order to speed up the graphical rendering process and minimize the amount of data processing downloading required to view a match. It is recommended to process no more than 25 minutes of a match at at time. Beyond this threshold it may be too difficult to create and render graphs.
9+
10+
Pre-processing of animated match data can be accomplished by doing the following:
11+
- Executing the motion-graph.py script and selecting the time period of a match that you would like to pre-process (again in max 25 minute windows). You will need to select a .csv tracking file when executing the script.
12+
- Save the resulting file in the data directory and name it using a .json file extension
13+
- The file will now be visible and selectable within the app
14+
- The submit button must be selected to view the match
15+
16+
The event viewer is fairly self-explanatory and the user can select the team that they wish to see using the menus at the very top of the app.
17+
18+
## Acknowledgements
19+
With great gratitude I would like to thank Bruno Dadnino and his team at Metrica Sports (https://metrica-sports.com/about-us/) for the sample tracking and event data used in this application.
20+
The original files are here: https://github.com/metrica-sports/sample-data. These files are part of Metrica's Elite Data product. So if you are subscribers to the Elite Data package you will be able to use those files with this application with very minimal changes required (they have been very lightly modified for the purposes of this app). Just copy the format used in the demo files (the changes/differences are very small). Better yet, do it programmatically and it's even easier!
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import pandas as pd
2+
import numpy as np
3+
4+
# raw_df = pd.read_csv(cv_file, names=header_list, error_bad_lines=False, dtype=str)
5+
raw_df1 = pd.read_csv("data/Sample_Game_2_RawTrackingData_Away_Team.csv")
6+
# sample every 5th row
7+
raw_df1 = raw_df1.iloc[::7, :]
8+
9+
raw_df2 = pd.read_csv("data/Sample_Game_2_RawTrackingData_Home_Team.csv")
10+
raw_df2 = raw_df2.iloc[::7, :]
11+
12+
column = 1
13+
df = pd.DataFrame(columns=["half", "frame", "time", "x", "y"])
14+
# x range needs adjusted depending on how many columns there are. Should really calculate this not eyeball items
15+
# and do it manually. But hey it's just for a demo not ongoing
16+
for x in range(0, 13):
17+
column = column + 2
18+
df_temp = raw_df1.iloc[:, [0, 1, 2, column, column + 1]].copy()
19+
df_temp.columns = ["half", "frame", "time", "x", "y"]
20+
df_temp["jersey_number"] = raw_df1.columns[column]
21+
df = pd.concat([df, df_temp]).reset_index(drop=True)
22+
df["team"] = "Away"
23+
df.loc[df["jersey_number"] == "0", "team"] = "Ball"
24+
df.loc[df["x"].isna(), "x"] = None
25+
df.loc[df["y"].isna(), "y"] = None
26+
df = df[df["x"].notna()]
27+
df.drop(df.loc[df["half"] == "Period"].index, inplace=True)
28+
29+
column = 1
30+
df2 = pd.DataFrame(columns=["half", "frame", "time", "x", "y"])
31+
for x in range(0, 12):
32+
column = column + 2
33+
df_temp2 = raw_df2.iloc[:, [0, 1, 2, column, column + 1]].copy()
34+
df_temp2.columns = ["half", "frame", "time", "x", "y"]
35+
df_temp2["jersey_number"] = raw_df2.columns[column]
36+
df2 = pd.concat([df2, df_temp2]).reset_index(drop=True)
37+
df2["team"] = "Home"
38+
df2.loc[df2["x"].isna(), "x"] = 0.5
39+
df2.loc[df2["y"].isna(), "y"] = 0.5
40+
df2 = df2[df2["x"].notna()]
41+
df2.drop(df2.loc[df2["half"] == "Period"].index, inplace=True)
42+
43+
df = df.iloc[1:]
44+
df["frame"] = df["frame"].apply(pd.to_numeric, errors="coerce")
45+
df = df.sort_values(by=["frame"])
46+
47+
df2 = df2.iloc[1:]
48+
df2["frame"] = df2["frame"].apply(pd.to_numeric, errors="coerce")
49+
df2 = df2.sort_values(by=["frame"])
50+
51+
df_export = pd.concat([df, df2]).reset_index(drop=True)
52+
df_export = df_export.sort_values(by=["frame"])
53+
df_export["time"] = df_export["time"].apply(pd.to_numeric, errors="coerce")
54+
df_export["time"] = df_export["time"].div(60).round(4)
55+
export_file_name = input(
56+
"Please enter a name for the file to be exported (ending with .csv): "
57+
)
58+
export_file_name = "data/" + export_file_name
59+
df_export.to_csv(export_file_name, index=False)

0 commit comments

Comments
 (0)