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

Descriptive text

This is how it looks before

Descriptive text

This is how it looks after