Skip to content

Commit

Permalink
Merge pull request #565 from plotly/add-soccer-analytics
Browse files Browse the repository at this point in the history
Add soccer analytics demo

Former-commit-id: 6ec68e5
  • Loading branch information
Xing Han Lu authored Feb 17, 2021
2 parents 6e2a10b + 8cee98b commit 1389f95
Show file tree
Hide file tree
Showing 20 changed files with 3,785 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/dbc-soccer-analytics/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn app:server
20 changes: 20 additions & 0 deletions apps/dbc-soccer-analytics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Soccer Match Analytics

## Overview

* `Soccer Match Analytics` helps coaches and analysts to analyze the events and view a digital recreation of action in a single match

## Usage
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.

Pre-processing of animated match data can be accomplished by doing the following:
- 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.
- Save the resulting file in the data directory and name it using a .json file extension
- The file will now be visible and selectable within the app
- The submit button must be selected to view the match

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.

## Acknowledgements
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.
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!
59 changes: 59 additions & 0 deletions apps/dbc-soccer-analytics/TrackingDataPreProcessing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pandas as pd
import numpy as np

# raw_df = pd.read_csv(cv_file, names=header_list, error_bad_lines=False, dtype=str)
raw_df1 = pd.read_csv("data/Sample_Game_2_RawTrackingData_Away_Team.csv")
# sample every 5th row
raw_df1 = raw_df1.iloc[::7, :]

raw_df2 = pd.read_csv("data/Sample_Game_2_RawTrackingData_Home_Team.csv")
raw_df2 = raw_df2.iloc[::7, :]

column = 1
df = pd.DataFrame(columns=["half", "frame", "time", "x", "y"])
# x range needs adjusted depending on how many columns there are. Should really calculate this not eyeball items
# and do it manually. But hey it's just for a demo not ongoing
for x in range(0, 13):
column = column + 2
df_temp = raw_df1.iloc[:, [0, 1, 2, column, column + 1]].copy()
df_temp.columns = ["half", "frame", "time", "x", "y"]
df_temp["jersey_number"] = raw_df1.columns[column]
df = pd.concat([df, df_temp]).reset_index(drop=True)
df["team"] = "Away"
df.loc[df["jersey_number"] == "0", "team"] = "Ball"
df.loc[df["x"].isna(), "x"] = None
df.loc[df["y"].isna(), "y"] = None
df = df[df["x"].notna()]
df.drop(df.loc[df["half"] == "Period"].index, inplace=True)

column = 1
df2 = pd.DataFrame(columns=["half", "frame", "time", "x", "y"])
for x in range(0, 12):
column = column + 2
df_temp2 = raw_df2.iloc[:, [0, 1, 2, column, column + 1]].copy()
df_temp2.columns = ["half", "frame", "time", "x", "y"]
df_temp2["jersey_number"] = raw_df2.columns[column]
df2 = pd.concat([df2, df_temp2]).reset_index(drop=True)
df2["team"] = "Home"
df2.loc[df2["x"].isna(), "x"] = 0.5
df2.loc[df2["y"].isna(), "y"] = 0.5
df2 = df2[df2["x"].notna()]
df2.drop(df2.loc[df2["half"] == "Period"].index, inplace=True)

df = df.iloc[1:]
df["frame"] = df["frame"].apply(pd.to_numeric, errors="coerce")
df = df.sort_values(by=["frame"])

df2 = df2.iloc[1:]
df2["frame"] = df2["frame"].apply(pd.to_numeric, errors="coerce")
df2 = df2.sort_values(by=["frame"])

df_export = pd.concat([df, df2]).reset_index(drop=True)
df_export = df_export.sort_values(by=["frame"])
df_export["time"] = df_export["time"].apply(pd.to_numeric, errors="coerce")
df_export["time"] = df_export["time"].div(60).round(4)
export_file_name = input(
"Please enter a name for the file to be exported (ending with .csv): "
)
export_file_name = "data/" + export_file_name
df_export.to_csv(export_file_name, index=False)
Loading

0 comments on commit 1389f95

Please sign in to comment.