Review ML
Explanations are below images:
import pandas as pd
from sklearn.tree import DecisionTreeRegressor
from sklearn.linear_model import LinearRegression
class SoccerScoreModel:
"""A class used to represent the Soccer Score Prediction Model."""
_instance = None
def __init__(self):
self.model = None
self.dt = None
# Initial features list, will be updated after cleaning the data
self.features = ['home_score', 'neutral'] # Note: This will be updated dynamically after one-hot encoding
self.target = None
self.soccer_data = None
def _clean(self):
# Drop rows with missing values in the initial features list
self.soccer_data.dropna(subset=['home_score', 'tournament', 'city', 'country', 'neutral'], inplace=True)
# Convert 'neutral' to numeric if it's not already
if self.soccer_data['neutral'].dtype != 'int64':
self.soccer_data['neutral'] = self.soccer_data['neutral'].astype(int)
# Apply One-Hot Encoding to categorical columns
categorical_features = ['tournament', 'city', 'country']
self.soccer_data = pd.get_dummies(self.soccer_data, columns=categorical_features, drop_first=True)
# Update the features list to include the new one-hot encoded columns
self.features += [col for col in self.soccer_data.columns if col.startswith(tuple(categorical_features)) and col not in self.features]
def _train(self):
X = self.soccer_data[self.features]
y = self.soccer_data[self.target]
self.model = LinearRegression()
self.model.fit(X, y)
self.dt = DecisionTreeRegressor()
self.dt.fit(X, y)
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
cls._instance._load_data()
cls._instance._clean()
cls._instance._set_target()
cls._instance._train()
return cls._instance
def _load_data(self):
self.soccer_data = pd.read_csv('results.csv')
def _set_target(self):
self.soccer_data['home_wins'] = (self.soccer_data['home_score'] > self.soccer_data['away_score']).astype(int)
self.target = 'home_wins'
def predict_winner_likelihood(self, team1, team2):
team1_matches = self.soccer_data[(self.soccer_data['home_team'] == team1) | (self.soccer_data['away_team'] == team1)]
team2_matches = self.soccer_data[(self.soccer_data['home_team'] == team2) | (self.soccer_data['away_team'] == team2)]
matches = pd.concat([team1_matches, team2_matches])
if matches.empty:
return {"error": "No past matches found for the given teams."}
team1_avg_score = (matches[matches['home_team'] == team1]['home_score'].mean() or 0) + (matches[matches['away_team'] == team1]['away_score'].mean() or 0)
team2_avg_score = (matches[matches['home_team'] == team2]['home_score'].mean() or 0) + (matches[matches['away_team'] == team2]['away_score'].mean() or 0)
total_score = team1_avg_score + team2_avg_score
team1_likelihood = (team1_avg_score / total_score) * 100
team2_likelihood = (team2_avg_score / total_score) * 100
return {team1: team1_likelihood, team2: team2_likelihood}
def feature_weights(self):
return {feature: importance for feature, importance in zip(self.features, self.dt.feature_importances_)}
This code implements a soccer score prediction model using linear regression and decision tree regression for part of the machine learning. Also, I used idata cleaning, training, and prediction methods in a singleton class structure. This is based of the titanic model except modified for my project
from flask import Blueprint, request, jsonify
from flask_restful import Api, Resource
from model.Soccermodel import SoccerScoreModel # Import the soccer model class
soccer_api = Blueprint('soccer_api', __name__, url_prefix='/api/soccer')
api = Api(soccer_api)
class Predict(Resource):
def post(self):
# Get the team data from the request
data = request.get_json()
# Get the team names from the data
team1 = data.get('team1')
team2 = data.get('team2')
# Get the singleton instance of the SoccerScoreModel
soccer_model = SoccerScoreModel.get_instance()
# Predict the winner likelihood of the soccer game
likelihood = soccer_model.predict_winner_likelihood(team1, team2)
return jsonify(likelihood)
# Add the Predict resource to the API with the /predict endpoint
api.add_resource(Predict, '/predict')
Sets an a restful api using flask to predict the chances two teams have at wining using the model via a post request also, based on the api for the titanic.
---
title: Soccer Game Predictor
layout: post
permalink: /soccer
type: tangibles
courses: { timebox: { week: 6 } }
---
<body style="background-image: url('images/soccer.webp'); background-size: cover;">
<form id="soccerForm" style="background: rgba(255, 255, 255, 0.8); padding: 20px; border-radius: 10px; width: fit-content; margin: auto;">
<p>Enter the names of the home and away teams</p>
<p> to predict the winner of the soccer</p>
<p> game.P.S. the soccer ball is the button 😁. </p>
<label for="team1">Home Team:</label>
<input type="text" id="team1" name="team1" required><br><br>
<label for="team2">Away Team:</label>
<input type="text" id="team2" name="team2" required><br><br>
<!-- Replace the button with an image acting as a button -->
<img src="images/soccerball.png" alt="Predict Winner" style="cursor: pointer; width: 200px; height: 200px;" onclick="predictWinner()"/>
</form>
<div id="result" style="background: rgba(255, 255, 255, 0.9); padding: 20px; border-radius: 10px; width: fit-content; margin: 20px auto; text-align: center; position: relative;"></div>
<script>
function predictWinner() {
var form = document.getElementById('soccerForm');
var team1 = document.getElementById('team1').value;
var team2 = document.getElementById('team2').value;
var resultDiv = document.getElementById('result');
// Prepare data as JSON
var data = {
"team1": team1,
"team2": team2
};
// Send the data to the backend API
fetch('http://localhost:8086/api/soccer/predict', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
// Find the team with the higher win rate
var winningTeam = Object.keys(data).reduce((a, b) => data[a] > data[b] ? a : b);
// Update the header
resultDiv.innerHTML = '<h2 style="margin-bottom: 20px;">Predicted Winner</h2>'; // Maintain spacing between text and images
// Create a container for the crown and flag images with added margin-top
var imagesContainer = '<div style="position: relative; display: inline-block; margin-top: 100px;">'; // Move the container down by 100 pixels
imagesContainer += '<img src="images/crown.png" alt="Crown" style="width: 250px; height: auto; position: absolute; left: 50%; transform: translateX(-50%); top: -125px; z-index: 2;"/>';
// Adjust the image source to match the path to the winning team's flag
var imgSrc = 'images/Nations/' + winningTeam + '.png'; // Construct the path to the flag image
// Flag image placed in the container
imagesContainer += '<img src="' + imgSrc + '" alt="Flag of ' + winningTeam + '" style="width: 400px; height: auto; display: block; z-index: 1;"/>';
imagesContainer += '</div>';
// Display the container in the result div
resultDiv.innerHTML += imagesContainer;
resultDiv.innerHTML += '<p>' + winningTeam + ' wins with ' + data[winningTeam].toFixed(4) + '% win rate!</p>'; // Display winning team and win rate
for (var team in data) {
var winRate = data[team].toFixed(4); // Display win rate to four decimal places
resultDiv.innerHTML += '<p>' + team + ': ' + winRate + '%</p>'; // Display win rate for each team
}
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
This frontend code is just mostly styling, containers, and forms. It uses a post request to send teh team input to the backend then outputs the result. I included the flag of the winning team, a crown, and a soccer ball as the button just for some custimization.
This is how the csv file is structured see below:
date,home_team,away_team,home_score,away_score,tournament,city,country,neutral 1872-11-30,Scotland,England,0,0,Friendly,Glasgow,Scotland,FALSE 1873-03-08,England,Scotland,4,2,Friendly,London,England,FALSE 1874-03-07,Scotland,England,2,1,Friendly,Glasgow,Scotland,FALSE 1875-03-06,England,Scotland,2,2,Friendly,London,England,FALSE