{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 1. Introduction\n\n<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>1.1 Background</b></p>\n</div>\n\nThis months TPS competition we are working with **Rocket League** data! This is the game where you play **football with cars**. \n\n<center>\n<img src='https://pm1.narvii.com/6922/56ece18e5e61afcb8c6dc13f797322f063d190f2r1-1280-640v2_hq.jpg' width=600>\n</center>\n<br>\n\n> The goal of the competition is to predict -- from a given snapshot in the game -- for each team, the probability that they will score within the next 10 seconds of game time.\n\n<br>\n\n*Initial thoughts:*\n* This competition is definitely going to be a **challenge**; the dataset is massive (10GB) and the relationships between the features are very complex. \n* The exciting part though is that there is lots of potential for **feature engineering**. \n\n<br>\n\n<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>1.2 Libraries</b></p>\n</div>","metadata":{}},{"cell_type":"code","source":"# Core\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nsns.set(style='darkgrid', font_scale=1.6)\nimport matplotlib.pyplot as plt\n%matplotlib inline\nfrom itertools import combinations\nimport math\nimport statistics\nfrom scipy import stats\nfrom scipy.stats import pearsonr\nfrom scipy.stats import shapiro\nfrom scipy.stats import chi2\nfrom scipy.stats import poisson\nimport time\nfrom datetime import datetime\nimport matplotlib.dates as mdates\nimport plotly.express as px\nfrom termcolor import colored\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\n# Sklearn\nimport sklearn\nfrom sklearn.decomposition import PCA\nfrom sklearn.manifold import TSNE\nfrom sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA\nfrom sklearn.cluster import KMeans\nfrom sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV, TimeSeriesSplit, GroupKFold, cross_validate\nfrom sklearn.preprocessing import StandardScaler, RobustScaler, PowerTransformer, OneHotEncoder, LabelEncoder\nfrom sklearn.impute import SimpleImputer, KNNImputer\nfrom sklearn.experimental import enable_iterative_imputer\nfrom sklearn.impute import IterativeImputer\nfrom sklearn.pipeline import make_pipeline\nfrom sklearn.compose import make_column_transformer\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay, accuracy_score, roc_auc_score\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.linear_model import LinearRegression, Ridge\nfrom sklearn.mixture import GaussianMixture, BayesianGaussianMixture\n\n# UMAP\nimport umap\nimport umap.plot\n\n# Models\nfrom sklearn.linear_model import LinearRegression, LogisticRegression\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.svm import SVC\nfrom sklearn.tree import DecisionTreeClassifier\nfrom sklearn.ensemble import RandomForestClassifier\nfrom xgboost import XGBClassifier\nfrom lightgbm import LGBMClassifier\nfrom catboost import CatBoostClassifier\nfrom sklearn.naive_bayes import GaussianNB","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:18:22.793789Z","iopub.execute_input":"2022-10-05T10:18:22.794182Z","iopub.status.idle":"2022-10-05T10:18:22.812262Z","shell.execute_reply.started":"2022-10-05T10:18:22.794158Z","shell.execute_reply":"2022-10-05T10:18:22.810784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Data\n\n<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>2.1 Load data</b></p>\n</div>\n\n* The dataset is **very large** (10GB) - the train set is broken down into **10 parts**.\n* We will just use **small subset** of the train set to perform this EDA.\n","metadata":{}},{"cell_type":"code","source":"# Data types\ndtypes_dict_train = dict(pd.read_csv('../input/tabular-playground-series-oct-2022/train_dtypes.csv').values)\ndtypes_dict_test = dict(pd.read_csv('../input/tabular-playground-series-oct-2022/test_dtypes.csv').values)\n\n# Data (only 10% of train set)\ntrain = pd.read_csv(\"/kaggle/input/tabular-playground-series-oct-2022/train_0.csv\", dtype=dtypes_dict_train)\ntest = pd.read_csv(\"../input/tabular-playground-series-oct-2022/test.csv\", dtype=dtypes_dict_test)","metadata":{"execution":{"iopub.status.busy":"2022-10-05T10:18:23.819398Z","iopub.execute_input":"2022-10-05T10:18:23.819891Z","iopub.status.idle":"2022-10-05T10:18:57.565261Z","shell.execute_reply.started":"2022-10-05T10:18:23.819865Z","shell.execute_reply":"2022-10-05T10:18:57.564333Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Shape and preview\nprint('Train set shape:', train.shape)\ndisplay(train.head(3))\n\nprint('Test set shape:', test.shape)\ndisplay(test.head(3))","metadata":{"execution":{"iopub.status.busy":"2022-10-05T10:18:57.566660Z","iopub.execute_input":"2022-10-05T10:18:57.566890Z","iopub.status.idle":"2022-10-05T10:18:57.623205Z","shell.execute_reply.started":"2022-10-05T10:18:57.566867Z","shell.execute_reply":"2022-10-05T10:18:57.621695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# To speed up analysis, we only use a subset of the data\ntrain = train.iloc[:200000]","metadata":{"execution":{"iopub.status.busy":"2022-10-05T10:21:29.333545Z","iopub.execute_input":"2022-10-05T10:21:29.334956Z","iopub.status.idle":"2022-10-05T10:21:29.344467Z","shell.execute_reply.started":"2022-10-05T10:21:29.334913Z","shell.execute_reply":"2022-10-05T10:21:29.342765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>2.2 File information</b></p>\n</div>\n\n**Files**\n\n> * **train_[0-9].csv**: Train set split into 10 files. Rows are sorted by game_num, event_id, and event_time, and each event is entirely contained in one file.\n> * **test.csv**: Test set. Unlike the train set, the rows are scrambled.\n> * **[train|test]_dtypes.csv**: pandas dtypes for the columns in the train / test set.\n> * **sample_submission.csv**: A sample submission in the correct format.\n\n**Feature descriptions**\n\n> * **game_num** (train only): Unique identifier for the game from which the event was taken.\n> * **event_id** (train only): Unique identifier for the sequence of consecutive frames.\n> * **event_time** (train only): Time in seconds before the event (e.g. goal or game end) ended.\n> * **ball_pos_[xyz]**: Ball's position as a 3d vector.\n> * **ball_vel_[xyz]**: Ball's velocity as a 3d vector.\n> * **p{i}_pos_[xyz]**: Player i's position as a 3d vector.\n> * **p{i}_vel_[xyz]**: Player i's velocity as a 3d vector.\n> * **p{i}_boost**: Player i's boost remaining, in [0, 100]. Boost temporarily increases player speed.\n> * **boost{i}_timer**: Time in seconds until big boost orb (resets boost to 100) i respawns, or 0 if it's available.\n> * **player_scoring_next** (train only): Which player scores at end of event, in [0, 5], or -1 if no goal.\n> * **team_scoring_next (train only)**: Which team scores at the end of event (A or B), or NaN if no goal.\n> * **team_[A|B]_scoring_within_10sec** (train only): 1 if team [A|B] scores in next 10 seconds, 0 otherwise.\n> * **id** (test and submission only): Unique identifier for each test row. \n\n**Additional notes**\n\n> * All p{i} columns will be **NaN if and only if the player is demolished** (will respawn within a few seconds).\n> * Players 0, 1, and 2 make up **team A** and players 3, 4, and 5 make up **team B**.","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>2.3 Missing values</b></p>\n</div>\n\n* Missing values appear when players have been **demolished**. They also appear in the feature 'team_scoring_next' if a team doesn't score at the end of the event.\n* Missing values represent **less than 1%** of the data.","metadata":{}},{"cell_type":"code","source":"# Heatmap of missing values (subset of data)\nplt.figure(figsize=(15,8))\nsns.heatmap(train.loc[:,train.columns[train.isnull().any()]].isna().T, cmap='summer')\nplt.title('Heatmap of missing values')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:21:38.377157Z","iopub.execute_input":"2022-10-05T10:21:38.377521Z","iopub.status.idle":"2022-10-05T10:21:52.913327Z","shell.execute_reply.started":"2022-10-05T10:21:38.377496Z","shell.execute_reply":"2022-10-05T10:21:52.912144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Missing values summary\nmv=pd.DataFrame(train[train.columns[train.isnull().any()]].isna().sum(), columns=['Number_missing (TRAIN)'])\nmv['Percentage_missing (TRAIN)']=np.round(100*mv['Number_missing (TRAIN)']/len(train),2)\nmv['Number_missing (TEST)']=test[test.columns[test.isnull().any()]].isna().sum()\nmv['Percentage_missing (TEST)']=np.round(100*mv['Number_missing (TEST)']/len(test),2)\nmv.head()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:22:02.952394Z","iopub.execute_input":"2022-10-05T10:22:02.952734Z","iopub.status.idle":"2022-10-05T10:22:03.121979Z","shell.execute_reply.started":"2022-10-05T10:22:02.952711Z","shell.execute_reply":"2022-10-05T10:22:03.121026Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations:*\n* Missing values indicate **demolitions**, which means one of the teams is outnumbered for a few seconds.\n* We can **create features to indicate missing values** (e.g. number of players on the pitch) as this may affect the target.","metadata":{}},{"cell_type":"markdown","source":"# 3. EDA\n\n<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>3.1 Targets</b></p>\n</div>\n\nFirst, we'll visualise the **target variables**.\n","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,5))\nplt.subplot(1,2,1)\nsns.countplot(data=train, x='team_A_scoring_within_10sec')\nplt.title('Target A')\n\nplt.subplot(1,2,2)\nsns.countplot(data=train, x='team_B_scoring_within_10sec')\nplt.title('Target B')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:22:09.760706Z","iopub.execute_input":"2022-10-05T10:22:09.761047Z","iopub.status.idle":"2022-10-05T10:22:10.017141Z","shell.execute_reply.started":"2022-10-05T10:22:09.761022Z","shell.execute_reply":"2022-10-05T10:22:10.015712Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Team A target mean', train['team_A_scoring_within_10sec'].mean())\nprint('Team B target mean', train['team_B_scoring_within_10sec'].mean())","metadata":{"execution":{"iopub.status.busy":"2022-10-05T10:22:23.066722Z","iopub.execute_input":"2022-10-05T10:22:23.067062Z","iopub.status.idle":"2022-10-05T10:22:23.076243Z","shell.execute_reply.started":"2022-10-05T10:22:23.067030Z","shell.execute_reply":"2022-10-05T10:22:23.074637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(20,5))\nplt.subplot(1,2,1)\nsns.countplot(data=train, x='team_scoring_next')\nplt.title('Team scoring next')\n\nplt.subplot(1,2,2)\nsns.countplot(data=train, x='player_scoring_next')\nplt.title('Player scoring next')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:22:30.481235Z","iopub.execute_input":"2022-10-05T10:22:30.481604Z","iopub.status.idle":"2022-10-05T10:22:30.854786Z","shell.execute_reply.started":"2022-10-05T10:22:30.481578Z","shell.execute_reply":"2022-10-05T10:22:30.853767Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* -1 corresponds to no player scoring next.","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>3.2 Temporal features</b></p>\n</div>\n\nNow we'll look at the **features that depend on time**. Note that these only appear in the train set.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,5))\nplt.subplot(1,2,1)\nsns.histplot(data=train, x='game_num', binwidth=1)\nplt.title('Game number')\n\nplt.subplot(1,2,2)\nsns.histplot(data=train, x='event_id', binwidth=1000)\nplt.title('Frame number in event')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:22:48.450548Z","iopub.execute_input":"2022-10-05T10:22:48.450920Z","iopub.status.idle":"2022-10-05T10:22:49.600091Z","shell.execute_reply.started":"2022-10-05T10:22:48.450894Z","shell.execute_reply":"2022-10-05T10:22:49.598700Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12,4))\nsns.histplot(data=train, x='event_time')\nplt.title('Time before event (s)')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:22:57.827344Z","iopub.execute_input":"2022-10-05T10:22:57.827719Z","iopub.status.idle":"2022-10-05T10:22:58.214452Z","shell.execute_reply.started":"2022-10-05T10:22:57.827694Z","shell.execute_reply":"2022-10-05T10:22:58.212647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>3.3 Gameplay variables</b></p>\n</div>\n\nWe'll visualise position and velocity of the ball and players using **pairplots**. These show the **pairwise relationships** in a dataset.","metadata":{}},{"cell_type":"markdown","source":"**Ball position & velocity**","metadata":{}},{"cell_type":"code","source":"g = sns.PairGrid(train[['ball_pos_x','ball_pos_y','ball_pos_z']], height=4, aspect=1.3, corner=True)\ng.map_lower(sns.scatterplot, s=10, alpha=0.02, color='green')\ng.map_diag(sns.histplot)\nplt.suptitle('Ball position', y=1.02, fontsize=22)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-10-05T10:32:40.908921Z","iopub.execute_input":"2022-10-05T10:32:40.909247Z","iopub.status.idle":"2022-10-05T10:32:43.037416Z","shell.execute_reply.started":"2022-10-05T10:32:40.909223Z","shell.execute_reply":"2022-10-05T10:32:43.036476Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations:*\n* This plot gives us the **dimensions of the pitch**.\n* Notice how the ball spends **more time** on the **floor** and at the **edge** of the pitch.\n* There is also a **spike at the center** of the pitch due to kick-offs.","metadata":{}},{"cell_type":"code","source":"g = sns.PairGrid(train[['ball_vel_x','ball_vel_y','ball_vel_z']], height=4, aspect=1.3, corner=True)\ng.map_lower(sns.scatterplot, s=10, alpha=0.02, color='green')\ng.map_diag(sns.histplot)\nplt.suptitle('Ball velocity', y=1.02, fontsize=22)\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:38:29.130907Z","iopub.execute_input":"2022-10-05T10:38:29.131355Z","iopub.status.idle":"2022-10-05T10:38:31.680045Z","shell.execute_reply.started":"2022-10-05T10:38:29.131323Z","shell.execute_reply":"2022-10-05T10:38:31.678666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations:*\n* The pairplots are roughly **spherical**, meaning they can attain any combination of velocities (up to a limit).\n* There is also a **spike at 0** due to kick-offs.","metadata":{}},{"cell_type":"markdown","source":"**Player position & velocity**","metadata":{}},{"cell_type":"code","source":"g = sns.PairGrid(train[['p0_pos_x','p0_pos_y','p0_pos_z']], height=4, aspect=1.3, corner=True)\ng.map_lower(sns.scatterplot, s=10, alpha=0.02, color='orange')\ng.map_diag(sns.histplot)\nplt.suptitle('Player0 position', y=1.02, fontsize=22)\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:43:21.260092Z","iopub.execute_input":"2022-10-05T10:43:21.260476Z","iopub.status.idle":"2022-10-05T10:43:24.130902Z","shell.execute_reply.started":"2022-10-05T10:43:21.260451Z","shell.execute_reply":"2022-10-05T10:43:24.130031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations:*\n* The **position** of the players covers the **whole pitch**.\n* **Some routes are used more than others**, e.g. to pick up boosts, or climb walls.\n* Most of the time the player is **on/close to the ground**. ","metadata":{}},{"cell_type":"code","source":"g = sns.PairGrid(train[['p0_vel_x','p0_vel_y','p0_vel_z']], height=4, aspect=1.3, corner=True)\ng.map_lower(sns.scatterplot, s=10, alpha=0.02, color='orange')\ng.map_diag(sns.histplot)\nplt.suptitle('Player0 velocity', y=1.02, fontsize=22)\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:53:25.750516Z","iopub.execute_input":"2022-10-05T10:53:25.750911Z","iopub.status.idle":"2022-10-05T10:53:41.570344Z","shell.execute_reply.started":"2022-10-05T10:53:25.750874Z","shell.execute_reply":"2022-10-05T10:53:41.569127Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations:*\n* The circular plot shows a players **speed is bounded**, i.e. cannot exceed a certain amount.\n* There are **2 rings** corresponding to **maximum speed** with and without boost.","metadata":{}},{"cell_type":"markdown","source":"**Boost variables**","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,5))\nplt.subplot(1,2,1)\nsns.histplot(data=train, x='p0_boost', binwidth=1)\nplt.title('Boost amount for player 0')\n\nplt.subplot(1,2,2)\nsns.histplot(data=train, x='boost0_timer')\nplt.title('Time until boost pad 0 resets')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T10:52:38.690630Z","iopub.execute_input":"2022-10-05T10:52:38.690997Z","iopub.status.idle":"2022-10-05T10:52:39.301028Z","shell.execute_reply.started":"2022-10-05T10:52:38.690972Z","shell.execute_reply":"2022-10-05T10:52:39.298958Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations*:\n* **Spikes at 0 and 100** on the left plot are from players **running out/picking up big boost pads**.\n* There are **spikes at multiples of 12's** on the left plot because of '**mini boost pads**'.\n* The plot on the right shows big boost pads take **10 seconds to respawn**.","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>3.4 Correlations</b></p>\n</div>\n\n* There are many **feature interactions** in the data.\n* Every players position and velocity is correlated, **usually positively**, with the ball's features.\n* Players' boost variables tend to be **negatively correlated** with other features.","metadata":{}},{"cell_type":"code","source":"# Heatmap of correlations\nplt.figure(figsize=(10,7))\nsns.heatmap(train.corr(), cmap='bwr', vmin=-1, vmax=1)\nplt.title('Correlations')","metadata":{"execution":{"iopub.status.busy":"2022-10-03T14:51:18.723083Z","iopub.execute_input":"2022-10-03T14:51:18.723522Z","iopub.status.idle":"2022-10-03T14:51:20.388954Z","shell.execute_reply.started":"2022-10-03T14:51:18.723489Z","shell.execute_reply":"2022-10-03T14:51:20.387542Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>3.5 Violinplots</b></p>\n</div>\n\nWe will use violinplots to show how variables are **related to the target**.","metadata":{}},{"cell_type":"markdown","source":"**Ball position vs target**","metadata":{}},{"cell_type":"code","source":"for i in ['x','y','z']:\n    plt.figure(figsize=(18,4))\n    plt.subplot(1,2,1)\n    sns.violinplot(data=train, x='team_A_scoring_within_10sec', y=f'ball_pos_{i}')\n\n    plt.subplot(1,2,2)\n    sns.violinplot(data=train, x='team_B_scoring_within_10sec', y=f'ball_pos_{i}')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-10-05T12:08:28.956830Z","iopub.execute_input":"2022-10-05T12:08:28.957194Z","iopub.status.idle":"2022-10-05T12:08:31.882038Z","shell.execute_reply.started":"2022-10-05T12:08:28.957168Z","shell.execute_reply":"2022-10-05T12:08:31.880655Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations*:\n* The ball is **more central** (in the x-axis) before a goal.\n* The ball is **closer to the goal** (in the y-axis) before a goal.\n* The ball tends to be **more elevated** (in the z-axis) before a goal. ","metadata":{}},{"cell_type":"markdown","source":"**Player0 position vs target**","metadata":{}},{"cell_type":"code","source":"for i in ['x','y','z']:\n    plt.figure(figsize=(18,4))\n    plt.subplot(1,2,1)\n    sns.violinplot(data=train, x='team_A_scoring_within_10sec', y=f'p0_pos_{i}')\n\n    plt.subplot(1,2,2)\n    sns.violinplot(data=train, x='team_B_scoring_within_10sec', y=f'p0_pos_{i}')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-10-05T12:09:24.501804Z","iopub.execute_input":"2022-10-05T12:09:24.502372Z","iopub.status.idle":"2022-10-05T12:09:28.670983Z","shell.execute_reply.started":"2022-10-05T12:09:24.502326Z","shell.execute_reply":"2022-10-05T12:09:28.669457Z"},"_kg_hide-output":false,"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations*:\n* **Similar relationships** can be seen with 'ball position vs target' (above).","metadata":{}},{"cell_type":"markdown","source":"# 4. Feature Engineering\n\nSome initial ideas to get started with feature engineering.\n\n<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>4.1 Speed</b></p>\n</div>\n\nWe can calculate the ball's and player's speed using the formula:\n\n$$\n\\text{speed} = \\sqrt{v_x^2+v_y^2+v_z^2}\n$$","metadata":{}},{"cell_type":"code","source":"# Calculate absolute speed of ball and players\ndef calc_speeds(df):\n    df['ball_speed'] = np.sqrt((df['ball_vel_x']**2)+(df['ball_vel_y']**2)+(df['ball_vel_z']**2))\n    for i in range(6):\n        df[f'p{i}_speed'] = np.sqrt((df[f'p{i}_vel_x']**2)+(df[f'p{i}_vel_y']**2)+(df[f'p{i}_vel_z']**2))\n    return df\n\ntrain = calc_speeds(train)","metadata":{"execution":{"iopub.status.busy":"2022-10-05T10:56:45.554129Z","iopub.execute_input":"2022-10-05T10:56:45.555417Z","iopub.status.idle":"2022-10-05T10:56:45.575409Z","shell.execute_reply.started":"2022-10-05T10:56:45.555367Z","shell.execute_reply":"2022-10-05T10:56:45.574140Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"g = sns.PairGrid(train[['p0_speed','p1_speed','p2_speed']], height=4, aspect=1.3, corner=True)\ng.map_lower(sns.scatterplot, s=10, alpha=0.02, color='purple')\ng.map_diag(sns.histplot)\nplt.suptitle('Team A speeds', y=1.02, fontsize=22)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-10-05T10:58:53.073947Z","iopub.execute_input":"2022-10-05T10:58:53.074299Z","iopub.status.idle":"2022-10-05T10:58:55.300413Z","shell.execute_reply.started":"2022-10-05T10:58:53.074259Z","shell.execute_reply":"2022-10-05T10:58:55.298233Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations*:\n* There is a clear **positive correlation** between player's speeds.\n* The sharp lines correspond to **maximum speeds** with and without boost.","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>4.2 Demolitions</b></p>\n</div>\n\nDemolitions result in a team having a **temporary numbers advantage**. We can keep track of these via the missing values and create additional features that could be helpful the our models.","metadata":{}},{"cell_type":"code","source":"def demolitions(df):\n    for i in range(6):\n        df[f'p{i}_demo'] = (df[f'p{i}_pos_x'].isna()).astype(int)\n    df['active_players_A'] = 3-df['p0_demo']-df['p1_demo']-df['p2_demo']\n    df['active_players_B'] = 3-df['p3_demo']-df['p4_demo']-df['p5_demo']\n    return df\n\ntrain = demolitions(train)","metadata":{"execution":{"iopub.status.busy":"2022-10-05T11:00:43.682560Z","iopub.execute_input":"2022-10-05T11:00:43.682945Z","iopub.status.idle":"2022-10-05T11:00:43.699749Z","shell.execute_reply.started":"2022-10-05T11:00:43.682910Z","shell.execute_reply":"2022-10-05T11:00:43.698587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,5))\nsns.histplot(train, x='active_players_A', hue='team_B_scoring_within_10sec')\nplt.yscale('log')\nplt.show()","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-10-05T12:40:47.453348Z","iopub.execute_input":"2022-10-05T12:40:47.453684Z","iopub.status.idle":"2022-10-05T12:40:48.128189Z","shell.execute_reply.started":"2022-10-05T12:40:47.453660Z","shell.execute_reply":"2022-10-05T12:40:48.127020Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations:*\n* Teams with **less active players** are more likely to **conceed a goal** in the next 10 seconds.","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>4.3 Distance to goal</b></p>\n</div>\n\nWe can calculate the distance of the ball/players to the goal using different metrics, like **euclidean distance** or **manhattan distance**.","metadata":{}},{"cell_type":"code","source":"def dist_to_goal(df):\n    # Estimates\n    goal1_coord = (0,-102.5,1.2)\n    goal2_coord = (0,102.5,1.2)\n    \n    # Euclidean distance\n    df['ball_dist_to_goal1_euclid'] = np.sqrt((df['ball_pos_x']-goal1_coord[0])**2 + (df['ball_pos_y']-goal1_coord[1])**2 + (df['ball_pos_z']-goal1_coord[2])**2)\n    df['ball_dist_to_goal2_euclid'] = np.sqrt((df['ball_pos_x']-goal2_coord[0])**2 + (df['ball_pos_y']-goal2_coord[1])**2 + (df['ball_pos_z']-goal2_coord[2])**2)\n    \n    # Manhattan distance\n    df['ball_dist_to_goal1_manhat'] = np.absolute(df['ball_pos_x']-goal1_coord[0]) + np.absolute(df['ball_pos_y']-goal1_coord[1]) + np.absolute(df['ball_pos_z']-goal1_coord[2])\n    df['ball_dist_to_goal2_manhat'] = np.absolute(df['ball_pos_x']-goal2_coord[0]) + np.absolute(df['ball_pos_y']-goal2_coord[1]) + np.absolute(df['ball_pos_z']-goal2_coord[2])\n        \n    for i in range(6):\n        # Euclidean distance\n        df[f'p{i}_dist_to_goal1_euclid'] = np.sqrt((df[f'p{i}_pos_x']-goal1_coord[0])**2 + (df[f'p{i}_pos_y']-goal1_coord[1])**2 + (df[f'p{i}_pos_z']-goal1_coord[2])**2)\n        df[f'p{i}_dist_to_goal2_euclid'] = np.sqrt((df[f'p{i}_pos_x']-goal2_coord[0])**2 + (df[f'p{i}_pos_y']-goal2_coord[1])**2 + (df[f'p{i}_pos_z']-goal2_coord[2])**2)\n        \n        # Manhattan distance\n        df[f'p{i}_dist_to_goal1_manhat'] = np.absolute(df[f'p{i}_pos_x']-goal1_coord[0]) + np.absolute(df[f'p{i}_pos_y']-goal1_coord[1]) + np.absolute(df[f'p{i}_pos_z']-goal1_coord[2])\n        df[f'p{i}_dist_to_goal2_manhat'] = np.absolute(df[f'p{i}_pos_x']-goal2_coord[0]) + np.absolute(df[f'p{i}_pos_y']-goal2_coord[1]) + np.absolute(df[f'p{i}_pos_z']-goal2_coord[2])\n    \n    return df\n\ntrain = dist_to_goal(train)","metadata":{"execution":{"iopub.status.busy":"2022-10-05T12:34:32.037838Z","iopub.execute_input":"2022-10-05T12:34:32.038179Z","iopub.status.idle":"2022-10-05T12:34:32.100358Z","shell.execute_reply.started":"2022-10-05T12:34:32.038155Z","shell.execute_reply":"2022-10-05T12:34:32.098946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in ['A','B']:\n    plt.figure(figsize=(18,4))\n    plt.subplot(1,2,1)\n    sns.violinplot(data=train, x=f'team_{i}_scoring_within_10sec', y='ball_dist_to_goal1_euclid')\n\n    plt.subplot(1,2,2)\n    sns.violinplot(data=train, x=f'team_{i}_scoring_within_10sec', y='ball_dist_to_goal2_euclid')\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-10-05T12:44:57.266826Z","iopub.execute_input":"2022-10-05T12:44:57.267178Z","iopub.status.idle":"2022-10-05T12:44:59.206293Z","shell.execute_reply.started":"2022-10-05T12:44:57.267152Z","shell.execute_reply":"2022-10-05T12:44:59.205533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Observations:*\n* The **closer** the ball is to the goal, the **more likely** a goal is scored in that goal.","metadata":{}},{"cell_type":"markdown","source":"<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>4.4 Min/max/mean distance to goal</b></p>\n</div>\n\nThe minimum distance of a team to a goal can indicate whether there is a **goal keeper** defending the goal. Similar features with max (is there a **stricker**) and mean can also be computed. ","metadata":{}},{"cell_type":"code","source":"def min_dist_to_goal(df):\n    # Team A\n    df['min_dist_to_goal1_A'] = df[[f'p{i}_dist_to_goal1_euclid' for i in range(3)]].min(axis=1)\n    df['min_dist_to_goal2_A'] = df[[f'p{i}_dist_to_goal2_euclid' for i in range(3)]].min(axis=1)\n    \n    # Team B\n    df['min_dist_to_goal1_B'] = df[[f'p{i}_dist_to_goal1_euclid' for i in range(3,6)]].min(axis=1)\n    df['min_dist_to_goal2_B'] = df[[f'p{i}_dist_to_goal2_euclid' for i in range(3,6)]].min(axis=1)\n    return df\n\ndef max_dist_to_goal(df):\n    # Team A\n    df['max_dist_to_goal1_A'] = df[[f'p{i}_dist_to_goal1_euclid' for i in range(3)]].max(axis=1)\n    df['max_dist_to_goal2_A'] = df[[f'p{i}_dist_to_goal2_euclid' for i in range(3)]].max(axis=1)\n    \n    # Team B\n    df['max_dist_to_goal1_B'] = df[[f'p{i}_dist_to_goal1_euclid' for i in range(3,6)]].max(axis=1)\n    df['max_dist_to_goal2_B'] = df[[f'p{i}_dist_to_goal2_euclid' for i in range(3,6)]].max(axis=1)\n    return df\n\ndef mean_dist_to_goal(df):\n    # Team A\n    df['mean_dist_to_goal1_A'] = df[[f'p{i}_dist_to_goal1_euclid' for i in range(3)]].mean(axis=1)\n    df['mean_dist_to_goal2_A'] = df[[f'p{i}_dist_to_goal2_euclid' for i in range(3)]].mean(axis=1)\n    \n    # Team B\n    df['mean_dist_to_goal1_B'] = df[[f'p{i}_dist_to_goal1_euclid' for i in range(3,6)]].mean(axis=1)\n    df['mean_dist_to_goal2_B'] = df[[f'p{i}_dist_to_goal2_euclid' for i in range(3,6)]].mean(axis=1)\n    return df\n\ntrain = min_dist_to_goal(train)\ntrain = max_dist_to_goal(train)\ntrain = mean_dist_to_goal(train)","metadata":{"execution":{"iopub.status.busy":"2022-10-05T12:48:58.464806Z","iopub.execute_input":"2022-10-05T12:48:58.465197Z","iopub.status.idle":"2022-10-05T12:48:58.889110Z","shell.execute_reply.started":"2022-10-05T12:48:58.465172Z","shell.execute_reply":"2022-10-05T12:48:58.886963Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Next steps\n\n<div style=\"color:white;display:fill;\n            background-color:#deb500;font-size:150%;\n            font-family:Nexa;letter-spacing:0.5px\">\n    <p style=\"padding: 4px;color:white;\"><b>5.1 Ideas</b></p>\n</div>\n\nHere are some of the next steps that you can consider taking in your own notebooks.\n\n* Fill **missing values** (with 0 or penalise in some other way)\n* More **feature engineering** (angles, distances, trajectories, etc - get creative)\n* Set up a **cross-validation** scheme to validate models (train-test split or group-k-fold)\n* Build **models** (tree-based like LGBM, neural networks or others)\n* **Compress the data** to be able to train the model on a bigger proportion of the dataset (parquet, feather, etc)","metadata":{}}]}