<jupyter_start><jupyter_text>Prepare the dataWe first need to load both our league-data and EMA data and combine these together.<jupyter_code>league_data = pd.read_csv('data/league_data.csv')
league_data.drop(['Unnamed: 0', 'Date', 'HomeTeam', 'AwayTeam', 
                 'HTGS', 'ATGS', 'HTGC', 'ATGC', 'HM1', 'HM2', 'HM3', 
                 'HM4', 'HM5', 'AM1', 'AM2', 'AM3', 'AM4', 'AM5', 'HTFormPts',
                 'ATFormPts', 'MW', 'HTFormPtsStr', 'ATFormPtsStr'], 1, inplace=True)
league_data.columns
EMA_data = pd.read_csv('data/EMA_data.csv')
EMA_data.drop(['Unnamed: 0', 'f_DateHome', 'f_seasonHome', 'HomeTeam',
               'homeGame_x', 'f_yellowsAgainstAway', 'f_yellowsForAway',
               'f_yellowsAgainstHome', 'f_yellowsForHome', 'f_DateAway', 'f_seasonAway', 
               'AwayTeam', 'homeGame_y'], 1, inplace=True)
EMA_data.columns
df = pd.merge(EMA_data, league_data, left_on='gameId', right_index=True)
df.head()<jupyter_output><empty_output><jupyter_text>Cleaning and splitting the dataBecause the machine -learning model only takes numeric input we will change our labels from strings to integers and use categorical cross-entropy as our loss function. We will also scale our data using sklearn StandardScaler.First we will seperate the labels from the rest of our data and split into training and testing.<jupyter_code>training_data = df.loc[df['season'] != 1920].reset_index(drop=True)
testing_data = df.loc[df['season'] == 1920].reset_index(drop=True)

X = training_data.drop(['gameId', 'gameId_x', 'gameId_y', 'FTR', 
           'season', 'gameId_y', ], 1)
Y = training_data['FTR']

X_test = testing_data.drop(['gameId', 'gameId_x', 'gameId_y', 'FTR', 
           'season', 'gameId_y', ], 1)
y_test = testing_data['FTR']
def transform_results(results):
    transformed = []
    for i in range(len(results)):
        if results[i] == 'H':
            transformed.append(0)
        elif results[i] == 'A':
            transformed.append(2)
        else:
            transformed.append(1)
    return np.array(transformed)
            
Y = transform_results(Y)
y_test = transform_results(y_test)
scaler = StandardScaler()

X = scaler.fit_transform(X)
X_test = scaler.fit_transform(X_test)
print('Number of matches in training data:', X.shape[0])
print('Number of matches in test data:', X_test.shape[0])
print('Number of features:', X.shape[1])
# Split our data into train/validation. We are using the 
# most recent full season (18/19) for validation data.
X_train = X[:-380]
y_train = Y[:-380]
X_val = X[-380:]
y_val = Y[-380:]<jupyter_output><empty_output><jupyter_text>Build the modelNow we have cleaned the data we can now create our model and train it.<jupyter_code># input dimension is number of features
input_dim = X_train.shape[1]

activation_func = 'relu'
kernel_init = 'glorot_normal'
learning_rate = 0.001
batch_size = 16

model = keras.Sequential([
    Dense(48, input_shape=(input_dim,), activation=activation_func),
    Dropout(0.3),
    Dense(16),
    Dropout(0.2),
    Dense(3, activation='softmax')
])
es = EarlyStopping(monitor='loss', patience=3, verbose=1)
from keras.optimizers import Adam, SGD

opt = Adam(lr=learning_rate, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0)
model.compile(optimizer=opt, loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=batch_size, 
              callbacks=[es], epochs=500, verbose=0)
train_loss, train_acc = model.evaluate(X_train, y_train)
val_loss, val_acc = model.evaluate(X_val, y_val)
print('Training loss:', train_loss)
print('Training accuracy:', train_acc)
print('Validation loss:', val_loss)
print('Validation accuracy:', val_acc)<jupyter_output>4123/4123 [==============================] - 0s 18us/step
380/380 [==============================] - 0s 15us/step
Training loss: 0.7936528218680148
Training accuracy: 0.6480717658996582
Validation loss: 0.8073749272446883
Validation accuracy: 0.6184210777282715<jupyter_text>We are getting around 65% on training and 62% on validation. This is good as is well above the bookies accuracy. Once we are happy with how the model is performing we can check the accuracy on the test set. First let's get a more detailed breakdown of the model's accuracy by using classification report. This will show how we perform for each label.<jupyter_code>labels = ['Home', 'Draw', 'Away']
y_preds = model.predict(X_val)
y_pred_argmax = [np.argmax(i) for i in y_preds]
print(classification_report(y_val, y_pred_argmax, target_names=labels))
print(confusion_matrix(y_val, y_pred_argmax))
# Saving best model
# model.save('25Nov19.h5')<jupyter_output><empty_output><jupyter_text>Check test accuracyOnce we have our model performing well we will use our 'holdout' test-set, season 19/20 so far<jupyter_code>test_loss, test_acc = model.evaluate(X_test, y_test)
print('Test loss:', test_loss)
print('Test accuracy:', test_acc)
y_preds = model.predict(X_test)
y_pred_argmax = [np.argmax(i) for i in y_preds]
print(classification_report(y_test, y_pred_argmax))<jupyter_output>precision    recall  f1-score   support

           0       0.67      0.91      0.77        54
           1       0.45      0.15      0.22        34
           2       0.70      0.78      0.74        40

    accuracy                           0.66       128
   macro avg       0.61      0.61      0.58       128
weighted avg       0.62      0.66      0.62       128<jupyter_text>Create a betting strategyNow that we have an accurate model let's see if we can make it as profitable as possible. Our confusion matrix looks good. I mainly wanted to check the performance for draws as these are difficult to predict, the results are ok.We will now use the for-loop below to see how much we would have won had we bet.<jupyter_code>funds = 100
wager = 10
favourites = 0
no_bets = 0
min_diff = 0.03

y_preds = model.predict(X_test)

for i in range(len(X_test)):
    prediction = np.argmax(y_preds[i])
    print('\nPrediction', prediction)
    print('Actual', y_test[i])
    print('Favourite', np.argmin([testing_data['B365H'][i], testing_data['B365D'][i], 
                                  testing_data['B365A'][i]]))
    print('Prediction proba', y_preds[i])
    print('Home, Draw and Away odds', testing_data['B365H'][i],       
          testing_data['B365D'][i], testing_data['B365A'][i])
    
    if prediction == 0:
        odds_diff = y_preds[i][prediction] - (1/testing_data['B365H'][i])
        if odds_diff > min_diff:
            if prediction == np.argmin([testing_data['B365H'][i], testing_data['B365D'][i], 
                                  testing_data['B365A'][i]]):
                favourites +=1
                
            if  prediction == y_test[i]:
                funds += (wager * testing_data['B365H'][i]) - wager
            else:
                funds -= wager
        else:
            no_bets +=1
            
    elif prediction == 1:
        odds_diff = y_preds[i][prediction] - (1/testing_data['B365D'][i])
        if odds_diff > min_diff:
            if prediction == np.argmin([testing_data['B365H'][i], testing_data['B365D'][i], 
                                  testing_data['B365A'][i]]):
                favourites +=1
            if  prediction == y_test[i]:
                funds +=( wager * testing_data['B365D'][i]) - wager
            else:
                funds -= wager
        else:
            no_bets +=1
    else:
        odds_diff = y_preds[i][prediction] - (1/testing_data['B365A'][i])
        if odds_diff >  min_diff:
            if prediction == np.argmin([testing_data['B365H'][i], testing_data['B365D'][i], 
                                  testing_data['B365A'][i]]):
                favourites +=1
            if  prediction == y_test[i]:
                funds += (wager * testing_data['B365A'][i]) - wager
            else:
                funds -= wager
        else:
            no_bets +=1
   
    print('Funds', funds)
    
print(f'Betted on favourite {favourites} times out of {len(X_test)} matches.')
print(f'No bet placed {no_bets} times')<jupyter_output>Prediction 0
Actual 0
Favourite 0
Prediction proba [0.6406146  0.2976048  0.06178055]
Home, Draw and Away odds 1.14 10.0 19.0
Funds 100

Prediction 2
Actual 2
Favourite 2
Prediction proba [0.1498708  0.09711154 0.75301766]
Home, Draw and Away odds 12.0 6.5 1.22
Funds 100

Prediction 2
Actual 1
Favourite 0
Prediction proba [0.24385048 0.229821   0.5263285 ]
Home, Draw and Away odds 1.95 3.6 3.6
Funds 90

Prediction 2
Actual 1
Favourite 2
Prediction proba [0.23566064 0.30126706 0.46307233]
Home, Draw and Away odds 3.0 3.25 2.37
Funds 80

Prediction 2
Actual 2
Favourite 0
Prediction proba [0.26342666 0.29570556 0.44086778]
Home, Draw and Away odds 1.9 3.4 4.0
Funds 110.0

Prediction 0
Actual 0
Favourite 0
Prediction proba [0.36956793 0.30569366 0.32473844]
Home, Draw and Away odds 1.3 5.25 10.0
Funds 110.0

Prediction 2
Actual 1
Favourite 0
Prediction proba [0.21831408 0.30487978 0.4768061 ]
Home, Draw and Away odds 2.2 3.2 3.4
Funds 100.0

Prediction 2
Actual 2
Favourite 2
Prediction pr[...]
StarcoderdataJupyterStructuredCleanDedup