text_plain_output_2.png application_vnd.jupyter.stderr_output_1.png
from datetime import datetime, timedelta

from sklearn.model_selection import train_test_split

from sklearn.preprocessing import OneHotEncoder, StandardScaler

import numpy as np

import pandas as pd

import tensorflow as tf

REF_DATE_STR = '2021-10-08 06:00:00+00:00'
RANDOM_SEED = 42069
tf.random.set_seed(RANDOM_SEED)

def treatment_by_players(ref_date_str: str):
    """Pretreatment pipeline to build a dataframe set for models by players
    :param ref_date_str: A reference date as string, to put a weight on matches based to how old those games are
    :return: Dataset formatted for modeling and players name and ID database.
    """
    ref_date = datetime.strptime(ref_date_str, '%Y-%m-%d %H:%M:%S%z')
    players_df = pd.read_csv('/kaggle/input/rlcs-202122/by_players.csv', low_memory=False, encoding='utf8')
    general_df = pd.read_csv('/kaggle/input/rlcs-202122/general.csv', low_memory=False, encoding='utf8')
    players_df = players_df.rename(columns={'name': 'team'}).rename(columns=lambda x: x[2:] if x.startswith('p_') else x)
    general_df = general_df.loc[:, ['ballchasing_id', 'correction', 'region', 'split', 'event', 'phase', 'stage', 'round', 'date', 'duration', 'overtime', 'overtime_seconds']]
    players_df = players_df.drop(['start_time', 'end_time', 'mvp', 'car_id'], axis=1)
    dataframe = general_df.merge(players_df)
    dataframe = dataframe.loc[~dataframe.correction].drop('correction', axis=1)
    results = dataframe.loc[:, ['ballchasing_id', 'color', 'core_mvp']].drop_duplicates().groupby(['ballchasing_id', 'color'], as_index=False).mean()
    results = results.loc[results.core_mvp > 0].drop('core_mvp', axis=1).rename(columns={'color': 'win'})
    dataframe = dataframe.merge(results)
    dataframe.win = np.where(dataframe.color == dataframe.win, 1, 0)
    dataframe.platform_id = dataframe['platform'] + '_' + dataframe['platform_id'].astype(str)
    dataframe = dataframe.drop(['platform'], axis=1)
    dataframe.date = pd.to_datetime(dataframe.date, utc=True)
    dataframe.date = (dataframe.date - ref_date) / np.timedelta64(1, 'D')
    dataframe = dataframe.rename(columns={'date': 'since_ref_date'})
    dataframe.overtime_seconds = dataframe.overtime_seconds.fillna(0)
    players_db = dataframe.loc[:, ['team', 'name', 'platform_id', 'since_ref_date']].sort_values('since_ref_date', ascending=False).drop_duplicates(subset=['platform_id'], keep='first').reset_index(drop=True).sort_values(['team', 'name']).drop('since_ref_date', axis=1)
    df_reduced = dataframe.loc[:, ['ballchasing_id', 'color', 'team', 'platform_id', 'core_score']]
    bl_side = df_reduced.loc[df_reduced.color == 'blue'].rename(columns={'platform_id': 'id_list'}).sort_values(['ballchasing_id', 'core_score'], ascending=False).groupby(['ballchasing_id', 'color', 'team'])['id_list'].apply(list).reset_index()
    or_side = df_reduced.loc[df_reduced.color == 'orange'].rename(columns={'platform_id': 'id_list'}).sort_values(['ballchasing_id', 'core_score'], ascending=False).groupby(['ballchasing_id', 'color', 'team'])['id_list'].apply(list).reset_index()
    bl_teammates_list_v1 = df_reduced.loc[df_reduced.color == 'blue', ['ballchasing_id', 'platform_id']].merge(bl_side.drop('team', axis=1))
    bl_teammates_ex = bl_teammates_list_v1.explode('id_list').reset_index(drop=True)
    bl_teammates_list_v2 = bl_teammates_ex[bl_teammates_ex.id_list != bl_teammates_ex.platform_id].groupby(['ballchasing_id', 'platform_id'])['id_list'].apply(list).reset_index()
    bl_teammates = pd.concat([bl_teammates_list_v2.loc[:, ['ballchasing_id', 'platform_id']], bl_teammates_list_v2.id_list.apply(pd.Series)], axis=1).rename(columns={0: 'teammate_1', 1: 'teammate_2'})
    or_teammates_list_v1 = df_reduced.loc[df_reduced.color == 'orange', ['ballchasing_id', 'platform_id']].merge(or_side.drop('team', axis=1))
    or_teammates_ex = or_teammates_list_v1.explode('id_list').reset_index(drop=True)
    or_teammates_list_v2 = or_teammates_ex[or_teammates_ex.id_list != or_teammates_ex.platform_id].groupby(['ballchasing_id', 'platform_id'])['id_list'].apply(list).reset_index()
    or_teammates = pd.concat([or_teammates_list_v2.loc[:, ['ballchasing_id', 'platform_id']], or_teammates_list_v2.id_list.apply(pd.Series)], axis=1).rename(columns={0: 'teammate_1', 1: 'teammate_2'})
    teammates = pd.concat([or_teammates, bl_teammates])
    bl_as_opponent_series = bl_side.id_list.apply(pd.Series)
    bl_as_opponent = bl_side.merge(bl_as_opponent_series, left_index=True, right_index=True).drop('id_list', axis=1).rename(columns={0: 'opponent_1', 1: 'opponent_2', 2: 'opponent_3', 'team': 'opponent_team'}).replace({'color': {'blue': 'orange'}})
    or_as_opponent_series = or_side.id_list.apply(pd.Series)
    or_as_opponent = or_side.merge(or_as_opponent_series, left_index=True, right_index=True).drop('id_list', axis=1).rename(columns={0: 'opponent_1', 1: 'opponent_2', 2: 'opponent_3', 'team': 'opponent_team'}).replace({'color': {'orange': 'blue'}})
    opps = pd.concat([or_as_opponent, bl_as_opponent])
    dataframe = dataframe.merge(teammates, how='outer').merge(opps)
    dataframe = dataframe.drop(['ballchasing_id', 'name', 'color'], axis=1)
    dataframe.overtime = np.where(dataframe.overtime, 1, 0)
    dataframe.core_mvp = np.where(dataframe.core_mvp, 1, 0)
    return (dataframe, players_db)

def model_pretreatment(original_data: pd.DataFrame, split_data: pd.DataFrame):
    """Prepare raw data for further treatments
    :param original_data: original dataframe for fit step application and to get columns by type
    :param split_data: split / sample data resulting from sklearn 'train_test_split' or K-folds operation
    :return split_data_final: formatted data as numpy array.
    """
    num_cols = original_data.select_dtypes(include=np.number).columns.to_list()
    cat_cols = original_data.select_dtypes(exclude=np.number).columns.to_list()
    split_data_cat = OneHotEncoder(handle_unknown='ignore').fit(original_data[cat_cols]).transform(split_data[cat_cols]).toarray()
    split_data_scaled = StandardScaler().fit(original_data[num_cols]).transform(split_data[num_cols])
    split_data_final = np.concatenate((split_data_cat, split_data_scaled), axis=1)
    return split_data_final

def compile_model(train: np.array, train_target: np.array, validation: np.array, val_target: np.array, batch_size: float, alpha: float=0.01, es_rate: float=0.2, epochs: int=100, workers: int=1, verbose: bool=True):
    """Compile and fit a keras model
    :param train: train array
    :param train_target: target array
    :param validation: validation array
    :param val_target: validation target array
    :param batch_size: number of samples to work through before updating the internal model parameters
    :param alpha: initial learning rate
    :param es_rate: early stopping rate, epochs percentage to set early stopping
    :param epochs: number times that the learning algorithm will work through the entire training dataset
    :param workers: maximum number of processes to spin up when using process-based threading
    :param verbose: to display progress or not
    :return: model fitted (Keras model + Keras History).
    """
    if es_rate > 1:
        es_rate = 1
    elif es_rate < 0:
        es_rate = 0.2
    early_stopping = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=es_rate * epochs, mode='min', restore_best_weights=True)
    checkpoint = tf.keras.callbacks.ModelCheckpoint(filepath='/kaggle/working/tmp_mdl.hdf5', monitor='val_accuracy', save_weights_only=True, mode='max', save_best_only=True)
    model = tf.keras.Sequential([tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(256, activation='relu'), tf.keras.layers.Dense(256, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid')])
    model.compile(loss=tf.keras.losses.binary_crossentropy, optimizer=tf.keras.optimizers.Adam(learning_rate=alpha), metrics=tf.keras.metrics.BinaryAccuracy(name='accuracy'))
    history = model.fit(train, train_target, callbacks=[early_stopping, checkpoint], batch_size=batch_size, epochs=epochs, validation_data=(validation, val_target), verbose=verbose, workers=workers)
    model.load_weights('/kaggle/working/tmp_mdl.hdf5')
    return (model, history)

def compil_best_model(x: np.array, y: np.array, epochs: int, es_rate: float, batch_size: int, alpha: float, workers: int=1, verbose: bool=True):
    """Compil best model (the best combination of batch size & alpha) with Keras model implemented in 'compile_model' function
    :param x: training instances to class
    :param y: target array relative to x
    :param epochs: number times that the learning algorithm will work through the entire training dataset
    :param es_rate: early stopping rate, epochs percentage to set early stopping
    :param batch_size: batch size, number of samples to work through before updating the internal model parameters
    :param alpha: initial learning rate
    :param workers: maximum number of processes to spin up when using process-based threading
    :param verbose: to display progress or not
    :return model, model_history: Keras model and model's history
    """
    x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=1 / 3, random_state=RANDOM_SEED, stratify=y)
    new_x_train = model_pretreatment(original_data=x, split_data=x_train)
    new_x_val = model_pretreatment(original_data=x, split_data=x_val)
    model, model_history = compile_model(train=new_x_train, train_target=y_train, validation=new_x_val, val_target=y_val, batch_size=int(batch_size), alpha=alpha, epochs=epochs, es_rate=es_rate, verbose=verbose, workers=workers)
    return (model, model_history)

DF_GAMES, PLAYERS_DB = treatment_by_players(ref_date_str=REF_DATE_STR)
DATA = DF_GAMES.drop('win', axis=1)
TARGET = DF_GAMES.win
BEST_SETTINGS = {'batch_size': 64, 'init_alpha': 1e-06}

MY_MODEL, _ = compil_best_model(x=DATA, y=TARGET, epochs=1000, es_rate=0.1, batch_size=BEST_SETTINGS['batch_size'], alpha=BEST_SETTINGS['init_alpha'])