02-06-2023, 12:02 PM
How I Created a Football Prediction App on Streamlit
<div>
<div class="kk-star-ratings kksr-auto kksr-align-left kksr-valign-top" data-payload='{"align":"left","id":"1111357","slug":"default","valign":"top","ignore":"","reference":"auto","class":"","count":"2","legendonly":"","readonly":"","score":"5","starsonly":"","best":"5","gap":"5","greet":"Rate this post","legend":"5\/5 - (2 votes)","size":"24","width":"142.5","_legend":"{score}\/{best} - ({count} {votes})","font_factor":"1.25"}'>
<div class="kksr-stars">
<div class="kksr-stars-inactive">
<div class="kksr-star" data-star="1" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="2" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="3" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="4" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="5" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
</p></div>
<div class="kksr-stars-active" style="width: 142.5px;">
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
</p></div>
</div>
<div class="kksr-legend" style="font-size: 19.2px;"> 5/5 – (2 votes) </div>
</p></div>
<p>This tutorial shows you how I created a model to predict football results using Poisson distribution. You’ll learn how I designed an interactive dashboard on Streamlit where our users can select a team and get to know the odds of a home win, draw, or away win.</p>
<p>Here’s a live demo of using the app to predict different games, such as <strong>Arsenal vs. Southampton</strong>:</p>
<figure class="wp-block-video"><video autoplay controls src="https://blog.finxter.com/wp-content/uploads/2023/02/streamlit-app-2023-02-05-18-02-19.webm"></video></figure>
<div class="is-content-justification-center is-layout-flex wp-container-1 wp-block-buttons">
<div class="wp-block-button has-custom-width wp-block-button__width-75"><a class="wp-block-button__link wp-element-button" href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" target="_blank" rel="noreferrer noopener">Try The Live App</a></div>
</div>
<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
<p>The purpose of this tutorial is purely educational, to introduce you to some concepts in Python. Using this app other than what it is stated for, for example, to compare bookmakers’ odds, and place a stake, is entirely at your own risk.</p>
<p>We will be predicting the English Premier League as it’s the most-watched sport in the world.</p>
<h2>Poisson Distribution</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="582" height="869" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-61.png" alt="" class="wp-image-1111471" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-61.png 582w, https://blog.finxter.com/wp-content/uplo...01x300.png 201w" sizes="(max-width: 582px) 100vw, 582px" /></figure>
</div>
<p>Speaking in a football context, how likely will a match result in a win or draw within 90 minutes of gameplay? If it’s to result in a win, what are the chances of a team scoring 3 goals with a clean sheet? </p>
<p>That is exactly what a Poisson distribution tends to answer. </p>
<p class="has-global-color-8-background-color has-background"><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/2139.png" alt="ℹ" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Info</strong>: A Poisson distribution is a type of probability distribution that helps to calculate the chance of a certain number of events happening in a given space or time period. It considers the average rate of these events and assumes they are independent of each other.</p>
<p>So, here are our assumptions:</p>
<ol type="1">
<li><strong>Two or more events occurring are independent of each other.</strong> This means that if Tottenham FC were to pack the box, it does not prevent Manchester City from scoring against them in a match.</li>
<li><strong>Two events cannot occur simultaneously at the same time.</strong> This means that if Chelsea were to score a goal, it would not result in an instant equalizer.</li>
<li><strong>The number of events occurring in a given time interval can be counted.</strong> This means we can precisely say that Liverpool will commit a painful mistake that will gift their rival the trophy.</li>
</ol>
<p>As we can see from the above examples, the assumptions are not always the case in real-life situations, thus rendering the Poisson distribution as pointless as it appears to offer anything useful. Despite the inherent limitations, we can still draw insight from this model to see if its features can form a basis for further research for any predictive football model.</p>
<p>Sparing you with the theories and mathematical formula, we get down to business to see how we can implement the Poisson distribution using Python.</p>
<h2>The Dataset</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="733" height="423" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-62.png" alt="" class="wp-image-1111474" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-62.png 733w, https://blog.finxter.com/wp-content/uplo...00x173.png 300w" sizes="(max-width: 733px) 100vw, 733px" /></figure>
</div>
<p>We will import match results from the English Premier League (EPL). There are various sources to get this data, Kaggle<sup>1</sup>, GitHub<sup>2</sup>, and football API<sup>3</sup>. But we will source our data from football-data.co.uk<sup>4</sup>.</p>
<p><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/26bd.png" alt="⚽" class="wp-smiley" style="height: 1em; max-height: 1em;" /> At the point of writing, the EPL has gone halfway. It is now becoming more interesting than when it commenced. Arsenal’s dramatic resurgence means they are seen by many as favorites to win the crown. Manchester City are relentlessly in hot pursuit, especially with the arrival of Erling Haaland. Newcastle have become a surprising contender for the title.</p>
<p>On the other hand, Chelsea is nowhere to be found in the Champions League places, and so is Liverpool. These indicate that football is unpredictable. Hence, using the past to predict the future may not yield the expected results.</p>
<p>Furthermore, some Premier League clubs have undergone dramatic changes. From the change of ownership to managerial change to the transfer of players in and out of the competition. All these have made football prediction a very difficult one.</p>
<p>For these and other reasons, I used only the data from the current season to train the model.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import pandas as pd
data = pd.read_csv('https://www.football-data.co.uk/mmz4281/2223/E0.csv')
print(data.shape)
# (199, 106)</pre>
<p>We will not save the data. It is going to be in such a way that we will be getting real-time updates to make the prediction. The data has 106 columns, but we are only interested in 4 columns. </p>
<p>Let’s select and rename them.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">epl = data[['HomeTeam', 'AwayTeam','FTHG', 'FTAG']]
epl = epl.rename(columns={'FTHG': 'HomeGoals', 'FTAG':'AwayGoals'})
print(epl.head())</pre>
<p>Output:</p>
<pre class="wp-block-preformatted has-medium-font-size"><code> HomeTeam AwayTeam HomeGoals AwayGoals
0 Crystal Palace Arsenal 0 2
1 Fulham Liverpool 2 2
2 Bournemouth Aston Villa 2 0
3 Leeds Wolves 2 1
4 Newcastle Nott'm Forest 2 0</code></pre>
<p>We want to compare our predictions with live results. So, we will reserve the last 20 rows representing two game weeks. Then we see if we can draw insights from the home and away goals.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">test = epl[-20:]
epl = epl[:-20]
print(epl[['HomeGoals', 'AwayGoals']].mean())</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code>HomeGoals 1.631285
AwayGoals 1.217877
dtype: float64</code></pre>
<p>We now have 179 rows and 4 columns. You can see that, on average, the home team scores more goals than the away team but only by a small margin. </p>
<p>This information is vital. If an event follows a Poisson distribution, the mean also known as <em>lambda;</em> is the only thing we need to know to find the probability of that event occurring a certain number of times.</p>
<p>A <strong>skellam distribution</strong> is the difference between two means of a Poisson distribution (the mean of the home and away goals in our case). </p>
<p>We can then calculate the probability mass function (PMF) for a skellam distribution using the mean goals to determine the probability of a draw or a win between home and away teams.</p>
<p>from scipy.stats import skellam, poisson</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from scipy.stats import skellam, poisson # probability of a draw
skellam.pmf(0.0, epl.HomeGoals.mean(), epl.AwayGoals.mean())
# Output: 0.24434197359198495 # probability of a win by one goal
skellam.pmf(1.0, epl.HomeGoals.mean(), epl.AwayGoals.mean())
# Output: 0.22500333061251618
</pre>
<p>The result shows that the probability of a draw in EPL is 24% while a win by one goal is 25%. Remember, this is a combination of all the matches. We will then follow this process to model specific matches.</p>
<h2>Data Preparation</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="582" height="870" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-63.png" alt="" class="wp-image-1111475" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-63.png 582w, https://blog.finxter.com/wp-content/uplo...01x300.png 201w" sizes="(max-width: 582px) 100vw, 582px" /></figure>
</div>
<p>Before we begin building the model, let’s first prepare our data, making it suitable for modeling.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">home = epl.iloc[:,0:3].assign(home=1).rename(columns={'HomeTeam':'team', 'AwayTeam':'opponent', 'HomeGoals':'goals'})
away = epl.iloc[:, [1, 0, 3]].assign(home=0).rename(columns={'AwayTeam': 'team', 'HomeTeam': 'opponent', 'AwayGoals': 'goals'})
df = pd.concat([home, away])
print(df)</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code> team opponent goals home
0 Crystal Palace Arsenal 0 1
1 Fulham Liverpool 2 1
2 Bournemouth Aston Villa 2 1
3 Leeds Wolves 2 1
4 Newcastle Nott'm Forest 2 1
.. ... ... ... ...
174 Tottenham Crystal Palace 4 0
175 Man City Chelsea 1 0
176 Chelsea Fulham 1 0
177 Leeds Aston Villa 1 0
178 Man City Man United 1 0 [358 rows x 4 columns]</code>
</pre>
<p>We wanted to merge everything that represents home and away into a single column. </p>
<p>So, what we did was to filter them out, gave them similar names, then, concatenate them. </p>
<p>To differentiate away goals from home goals, we created a column and assigned 1 to represent home goals and 0 for away goals. Our data is now suitable for modeling.</p>
<h2>The Generalized Linear Model</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="740" height="412" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-64.png" alt="" class="wp-image-1111476" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-64.png 740w, https://blog.finxter.com/wp-content/uplo...00x167.png 300w" sizes="(max-width: 740px) 100vw, 740px" /></figure>
</div>
<p>The generalized linear model is a family of models in which <a rel="noreferrer noopener" href="https://blog.finxter.com/logistic-regression-in-one-line-python/" data-type="post" data-id="2537" target="_blank">logistic regression</a> and <a rel="noreferrer noopener" href="https://blog.finxter.com/python-linear-regression-1-liner/" data-type="post" data-id="1920" target="_blank">linear regression</a> models we use in machine learning are included. It is used to model different types of data. Poisson regression as part of the generalized linear model is used to analyze count data. </p>
<p>Remember, we are dealing with count data. For example, the number of goals per match. Since count data follows a Poisson distribution, we will be using Poisson regression to build our model.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import statsmodels.api as sm
import statsmodels.formula.api as smf formula = 'goals ~ team + opponent + home'
model = smf.glm(formula=formula, data=df, family=sm.families.Poisson()).fit()
print(model.summary())
</pre>
<p>We imported <code><a href="https://blog.finxter.com/logistic-regression-scikit-learn-vs-statsmodels/" data-type="post" data-id="22984" target="_blank" rel="noreferrer noopener">statsmodels</a></code> library to help us build the model.</p>
<p>The formula to predict the number of goals is defined as the combination of the team, opponent, and whether it is home or away goals. Take a look at the summary. The result of the <em>Generalized Linear Model</em> contains so much that we cannot explain all of them in this article. </p>
<p>But let’s focus on the <code>coef</code> column.</p>
<p>As you already know, the team side means a home match, and the opponent side means an away match. If the value is closer to 0, it indicates the possibility of a draw. If the value of the home side is positive, it means the team has a strong attacking ability. Teams with a negative value indicate that they have a not-so-strong attacking ability.</p>
<p>Having trained the model, we can now use it to make predictions. Let’s create a function to do so.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def predict_match(model, homeTeam, awayTeam, max_goals=10): home_goals = model.predict(pd.DataFrame(data={'team': homeTeam, 'opponent':awayTeam, 'home': 1}, index=[1])).values[0] away_goals = model.predict(pd.DataFrame(data={'team': awayTeam, 'opponent': homeTeam, 'home':0}, index=[1])).values[0] pred = [[poisson.pmf(i, team_avg) for i in range(0, max_goals+1)] for team_avg in [home_goals, away_goals]] return(np.outer(np.array(pred[0]), np.array(pred[1])))
</pre>
<p>The function has four parameters: </p>
<ul>
<li>the Poisson model to be used to make the predictions, </li>
<li>the home team, </li>
<li>the away team, and </li>
<li>the maximum number of goals. </li>
</ul>
<p>We set it to 10 as the highest a team can score within 90 minutes of gameplay. Remember, the formula combines all these to predict the number of goals.</p>
<p>We looped over the predicted number of home and away goals. We also looped over the maximum goals. </p>
<p>In each iteration, we calculate the probability mass function of the Poisson distribution. This tells us the probability of a team scoring several goals. Taking the outer product of the two sets of probabilities, the function created and returned a matrix.</p>
<p>Let me assume Arsenal and Manchester City are to face each other at Emirate Stadium and you want to make the prediction.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">print(model.predict(pd.DataFrame(data={'team': 'Arsenal', 'opponent': 'Man City', 'home':1}, index=[1])))</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code>1. 2.026391
dtype: float64
</code></pre>
<p>The model is predicting Arsenal to score two goals…</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">print(model.predict(pd.DataFrame(data={'team': 'Man City', 'opponent': 'Arsenal', 'home':0}, index=[1])))</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code>1 1.284658
dtype: float64</code></pre>
<p>… and Manchester City to score 1.23 goals, approximately 3 goals in the match.</p>
<p>The model roughly predicts a 2-1 home win for Arsenal. </p>
<p>Now that the three members of the formula are complete, we can feed it to the <code>predict_match()</code> function to get the odds of a home win, away win, and a draw.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ars_man = predict_match(model, 'Arsenal', 'Man City', max_goals=3)</pre>
<p>Result:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">array([[0.03647786, 0.04686159, 0.03010057, 0.01288965], [0.07391843, 0.09495992, 0.06099553, 0.02611947], [0.07489383, 0.09621298, 0.06180041, 0.02646414], [0.05058807, 0.06498838, 0.04174394, 0.01787557]])
</pre>
<p>The rows and columns represent Arsenal and Manchester City’s chances of scoring a particular goal respectively. </p>
<p>The diagonal entries represent a draw since it is where both teams score the same number of goals. Below the line (the lower triangle of the array found using <code>numpy.tril</code>) is Arsenal’s victory, and above (the upper triangle of the array found using <code>numpy.triu</code>) is Man City’s.</p>
<p>Let’s automate this with Python.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import numpy as np # victory for Arsenal
np.sum(np.tril(ars¬_man, -1)) * 100
# 40.23456259724963 # victory for Man City
np.sum(np.triu(ars_man, 1)) * 100
# 20.34309498981432 # a draw
np.sum(np.diag(ars_man)) * 100
# 21.111376045176485
</pre>
</p>
<p>Our model tells us that Arsenal has a 40% chance of winning which is much more than Man City’s odds at 21%. That makes the earlier prediction of 2-1 correspond accordingly.</p>
<p>Feel free to compare your prediction with the test data and see how far or close you are to predict live results. We can now proceed to create a football prediction app on Streamlit. </p>
<p><a href="https://github.com/finxter/Football_Prediction" data-type="URL" data-id="https://github.com/finxter/Football_Prediction" target="_blank" rel="noreferrer noopener">Check my GitHub page</a> to see the full script.</p>
<p><a href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" data-type="URL" data-id="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" target="_blank" rel="noreferrer noopener">Check out the live demo app</a> to play with it!</p>
<h2>Streamlit Dashboard</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="733" height="541" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-65.png" alt="" class="wp-image-1111479" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-65.png 733w, https://blog.finxter.com/wp-content/uplo...00x221.png 300w" sizes="(max-width: 733px) 100vw, 733px" /></figure>
</div>
<p>In the file named <code>app.py</code>, you will see how I used <code>st.sidebar.selectbox</code> to display a list of all the clubs in the Premier League. This will appear on the left-hand side. Since the names of the club appeared twice, I made sure that only one was selected for prediction.</p>
<p>The rest of the code has been explained. If the button is pressed, the <code>get_scores()</code> function is executed and displays the prediction results.</p>
<p class="has-base-background-color has-background"><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f449.png" alt="?" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Recommended</strong>: <a href="https://blog.finxter.com/learning-streamlits-buttons-features/" data-type="post" data-id="462652" target="_blank" rel="noreferrer noopener">Streamlit Button — Ultimate Guide with Video</a></p>
<p>Notice that I didn’t save the dataset. </p>
<p>Whenever the app is opened, it will get real-time updates that will help it train the model for the next prediction. Also, since every code is not wrapped in a function, the order is important. </p>
<p>That is why the <code>get_scores()</code> function was called last. Of course, there are many ways to write the code and get the same result.</p>
<h2>A Word of Caution</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="728" height="482" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-66.png" alt="" class="wp-image-1111483" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-66.png 728w, https://blog.finxter.com/wp-content/uplo...00x199.png 300w" sizes="(max-width: 728px) 100vw, 728px" /></figure>
</div>
<p>I clarified to you from the beginning that this article is for educational purposes only and should not be used for anything else. </p>
<p>Many things can impact the result of a match that the model didn’t put into consideration. Change of a manager, injury, refereeing decision, player fitness, team morale, weather condition, plus the limitations of Poisson distribution used to make these predictions. </p>
<p>Of course, no model is perfect. So, use responsibly.</p>
<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" target="_blank" rel="noreferrer noopener"><img decoding="async" loading="lazy" width="725" height="408" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-58.png" alt="" class="wp-image-1111393" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-58.png 725w, https://blog.finxter.com/wp-content/uplo...00x169.png 300w" sizes="(max-width: 725px) 100vw, 725px" /></a></figure>
</div>
<h2>Prediction Result</h2>
<p>I deployed the app on Streamlit Cloud and tried to predict upcoming matches in the English Premier League. </p>
<p>The results were amazing. You can give it a try. I don’t expect the Premier League clubs to get those scores. Predicted result is not always the same as actual result. But I will rate the performance of our model if some, if not all, the home wins, draws, or away wins were predicted correctly.</p>
<h2>Conclusion</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="721" height="880" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-67.png" alt="" class="wp-image-1111484" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-67.png 721w, https://blog.finxter.com/wp-content/uplo...46x300.png 246w" sizes="(max-width: 721px) 100vw, 721px" /></figure>
</div>
<p>We have learned a lot today, ranging from data manipulation to model building. </p>
<p>You learned how to make football predictions using Poisson distribution. I did my best to make the explanation simple by leaving the mathematical theories and calculations behind. If you want to know more, you have the internet at your disposal. Alright, have a nice day.</p>
<p class="has-base-background-color has-background"><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f449.png" alt="?" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Recommended</strong>: <a href="https://blog.finxter.com/how-i-built-a-house-price-prediction-app-using-streamlit/" data-type="post" data-id="1104457" target="_blank" rel="noreferrer noopener">How I Built a House Price Prediction App Using Streamlit</a></p>
<h2>Resources</h2>
<ol type="1">
<li><a href="https://www.kaggle.com/hugomathien/soccer" target="_blank" rel="noreferrer noopener"><sup>https://www.kaggle.com/hugomathien/soccer</sup></a></li>
<li><a href="https://github.com/jalapic/engsoccerdata" target="_blank" rel="noreferrer noopener"><sup>https://github.com/jalapic/engsoccerdata</sup></a></li>
<li><a href="http://api.football-data.org/index" target="_blank" rel="noreferrer noopener"><sup>http://api.football-data.org/index</sup></a></li>
<li><a href="http://www.football-data.co.uk/englandm.php" target="_blank" rel="noreferrer noopener"><sup>http://www.football-data.co.uk/englandm.php</sup></a></li>
<li><a href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app" target="_blank" rel="noreferrer noopener"><sup>https://jonaben1-football-prediction-app-nlr1w7.streamlit.app</sup></a></li>
</ol>
</div>
https://www.sickgaming.net/blog/2023/02/...streamlit/
<div>
<div class="kk-star-ratings kksr-auto kksr-align-left kksr-valign-top" data-payload='{"align":"left","id":"1111357","slug":"default","valign":"top","ignore":"","reference":"auto","class":"","count":"2","legendonly":"","readonly":"","score":"5","starsonly":"","best":"5","gap":"5","greet":"Rate this post","legend":"5\/5 - (2 votes)","size":"24","width":"142.5","_legend":"{score}\/{best} - ({count} {votes})","font_factor":"1.25"}'>
<div class="kksr-stars">
<div class="kksr-stars-inactive">
<div class="kksr-star" data-star="1" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="2" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="3" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="4" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" data-star="5" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
</p></div>
<div class="kksr-stars-active" style="width: 142.5px;">
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
<div class="kksr-star" style="padding-right: 5px">
<div class="kksr-icon" style="width: 24px; height: 24px;"></div>
</p></div>
</p></div>
</div>
<div class="kksr-legend" style="font-size: 19.2px;"> 5/5 – (2 votes) </div>
</p></div>
<p>This tutorial shows you how I created a model to predict football results using Poisson distribution. You’ll learn how I designed an interactive dashboard on Streamlit where our users can select a team and get to know the odds of a home win, draw, or away win.</p>
<p>Here’s a live demo of using the app to predict different games, such as <strong>Arsenal vs. Southampton</strong>:</p>
<figure class="wp-block-video"><video autoplay controls src="https://blog.finxter.com/wp-content/uploads/2023/02/streamlit-app-2023-02-05-18-02-19.webm"></video></figure>
<div class="is-content-justification-center is-layout-flex wp-container-1 wp-block-buttons">
<div class="wp-block-button has-custom-width wp-block-button__width-75"><a class="wp-block-button__link wp-element-button" href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" target="_blank" rel="noreferrer noopener">Try The Live App</a></div>
</div>
<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
<p>The purpose of this tutorial is purely educational, to introduce you to some concepts in Python. Using this app other than what it is stated for, for example, to compare bookmakers’ odds, and place a stake, is entirely at your own risk.</p>
<p>We will be predicting the English Premier League as it’s the most-watched sport in the world.</p>
<h2>Poisson Distribution</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="582" height="869" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-61.png" alt="" class="wp-image-1111471" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-61.png 582w, https://blog.finxter.com/wp-content/uplo...01x300.png 201w" sizes="(max-width: 582px) 100vw, 582px" /></figure>
</div>
<p>Speaking in a football context, how likely will a match result in a win or draw within 90 minutes of gameplay? If it’s to result in a win, what are the chances of a team scoring 3 goals with a clean sheet? </p>
<p>That is exactly what a Poisson distribution tends to answer. </p>
<p class="has-global-color-8-background-color has-background"><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/2139.png" alt="ℹ" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Info</strong>: A Poisson distribution is a type of probability distribution that helps to calculate the chance of a certain number of events happening in a given space or time period. It considers the average rate of these events and assumes they are independent of each other.</p>
<p>So, here are our assumptions:</p>
<ol type="1">
<li><strong>Two or more events occurring are independent of each other.</strong> This means that if Tottenham FC were to pack the box, it does not prevent Manchester City from scoring against them in a match.</li>
<li><strong>Two events cannot occur simultaneously at the same time.</strong> This means that if Chelsea were to score a goal, it would not result in an instant equalizer.</li>
<li><strong>The number of events occurring in a given time interval can be counted.</strong> This means we can precisely say that Liverpool will commit a painful mistake that will gift their rival the trophy.</li>
</ol>
<p>As we can see from the above examples, the assumptions are not always the case in real-life situations, thus rendering the Poisson distribution as pointless as it appears to offer anything useful. Despite the inherent limitations, we can still draw insight from this model to see if its features can form a basis for further research for any predictive football model.</p>
<p>Sparing you with the theories and mathematical formula, we get down to business to see how we can implement the Poisson distribution using Python.</p>
<h2>The Dataset</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="733" height="423" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-62.png" alt="" class="wp-image-1111474" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-62.png 733w, https://blog.finxter.com/wp-content/uplo...00x173.png 300w" sizes="(max-width: 733px) 100vw, 733px" /></figure>
</div>
<p>We will import match results from the English Premier League (EPL). There are various sources to get this data, Kaggle<sup>1</sup>, GitHub<sup>2</sup>, and football API<sup>3</sup>. But we will source our data from football-data.co.uk<sup>4</sup>.</p>
<p><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/26bd.png" alt="⚽" class="wp-smiley" style="height: 1em; max-height: 1em;" /> At the point of writing, the EPL has gone halfway. It is now becoming more interesting than when it commenced. Arsenal’s dramatic resurgence means they are seen by many as favorites to win the crown. Manchester City are relentlessly in hot pursuit, especially with the arrival of Erling Haaland. Newcastle have become a surprising contender for the title.</p>
<p>On the other hand, Chelsea is nowhere to be found in the Champions League places, and so is Liverpool. These indicate that football is unpredictable. Hence, using the past to predict the future may not yield the expected results.</p>
<p>Furthermore, some Premier League clubs have undergone dramatic changes. From the change of ownership to managerial change to the transfer of players in and out of the competition. All these have made football prediction a very difficult one.</p>
<p>For these and other reasons, I used only the data from the current season to train the model.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import pandas as pd
data = pd.read_csv('https://www.football-data.co.uk/mmz4281/2223/E0.csv')
print(data.shape)
# (199, 106)</pre>
<p>We will not save the data. It is going to be in such a way that we will be getting real-time updates to make the prediction. The data has 106 columns, but we are only interested in 4 columns. </p>
<p>Let’s select and rename them.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">epl = data[['HomeTeam', 'AwayTeam','FTHG', 'FTAG']]
epl = epl.rename(columns={'FTHG': 'HomeGoals', 'FTAG':'AwayGoals'})
print(epl.head())</pre>
<p>Output:</p>
<pre class="wp-block-preformatted has-medium-font-size"><code> HomeTeam AwayTeam HomeGoals AwayGoals
0 Crystal Palace Arsenal 0 2
1 Fulham Liverpool 2 2
2 Bournemouth Aston Villa 2 0
3 Leeds Wolves 2 1
4 Newcastle Nott'm Forest 2 0</code></pre>
<p>We want to compare our predictions with live results. So, we will reserve the last 20 rows representing two game weeks. Then we see if we can draw insights from the home and away goals.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">test = epl[-20:]
epl = epl[:-20]
print(epl[['HomeGoals', 'AwayGoals']].mean())</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code>HomeGoals 1.631285
AwayGoals 1.217877
dtype: float64</code></pre>
<p>We now have 179 rows and 4 columns. You can see that, on average, the home team scores more goals than the away team but only by a small margin. </p>
<p>This information is vital. If an event follows a Poisson distribution, the mean also known as <em>lambda;</em> is the only thing we need to know to find the probability of that event occurring a certain number of times.</p>
<p>A <strong>skellam distribution</strong> is the difference between two means of a Poisson distribution (the mean of the home and away goals in our case). </p>
<p>We can then calculate the probability mass function (PMF) for a skellam distribution using the mean goals to determine the probability of a draw or a win between home and away teams.</p>
<p>from scipy.stats import skellam, poisson</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from scipy.stats import skellam, poisson # probability of a draw
skellam.pmf(0.0, epl.HomeGoals.mean(), epl.AwayGoals.mean())
# Output: 0.24434197359198495 # probability of a win by one goal
skellam.pmf(1.0, epl.HomeGoals.mean(), epl.AwayGoals.mean())
# Output: 0.22500333061251618
</pre>
<p>The result shows that the probability of a draw in EPL is 24% while a win by one goal is 25%. Remember, this is a combination of all the matches. We will then follow this process to model specific matches.</p>
<h2>Data Preparation</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="582" height="870" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-63.png" alt="" class="wp-image-1111475" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-63.png 582w, https://blog.finxter.com/wp-content/uplo...01x300.png 201w" sizes="(max-width: 582px) 100vw, 582px" /></figure>
</div>
<p>Before we begin building the model, let’s first prepare our data, making it suitable for modeling.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">home = epl.iloc[:,0:3].assign(home=1).rename(columns={'HomeTeam':'team', 'AwayTeam':'opponent', 'HomeGoals':'goals'})
away = epl.iloc[:, [1, 0, 3]].assign(home=0).rename(columns={'AwayTeam': 'team', 'HomeTeam': 'opponent', 'AwayGoals': 'goals'})
df = pd.concat([home, away])
print(df)</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code> team opponent goals home
0 Crystal Palace Arsenal 0 1
1 Fulham Liverpool 2 1
2 Bournemouth Aston Villa 2 1
3 Leeds Wolves 2 1
4 Newcastle Nott'm Forest 2 1
.. ... ... ... ...
174 Tottenham Crystal Palace 4 0
175 Man City Chelsea 1 0
176 Chelsea Fulham 1 0
177 Leeds Aston Villa 1 0
178 Man City Man United 1 0 [358 rows x 4 columns]</code>
</pre>
<p>We wanted to merge everything that represents home and away into a single column. </p>
<p>So, what we did was to filter them out, gave them similar names, then, concatenate them. </p>
<p>To differentiate away goals from home goals, we created a column and assigned 1 to represent home goals and 0 for away goals. Our data is now suitable for modeling.</p>
<h2>The Generalized Linear Model</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="740" height="412" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-64.png" alt="" class="wp-image-1111476" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-64.png 740w, https://blog.finxter.com/wp-content/uplo...00x167.png 300w" sizes="(max-width: 740px) 100vw, 740px" /></figure>
</div>
<p>The generalized linear model is a family of models in which <a rel="noreferrer noopener" href="https://blog.finxter.com/logistic-regression-in-one-line-python/" data-type="post" data-id="2537" target="_blank">logistic regression</a> and <a rel="noreferrer noopener" href="https://blog.finxter.com/python-linear-regression-1-liner/" data-type="post" data-id="1920" target="_blank">linear regression</a> models we use in machine learning are included. It is used to model different types of data. Poisson regression as part of the generalized linear model is used to analyze count data. </p>
<p>Remember, we are dealing with count data. For example, the number of goals per match. Since count data follows a Poisson distribution, we will be using Poisson regression to build our model.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import statsmodels.api as sm
import statsmodels.formula.api as smf formula = 'goals ~ team + opponent + home'
model = smf.glm(formula=formula, data=df, family=sm.families.Poisson()).fit()
print(model.summary())
</pre>
<p>We imported <code><a href="https://blog.finxter.com/logistic-regression-scikit-learn-vs-statsmodels/" data-type="post" data-id="22984" target="_blank" rel="noreferrer noopener">statsmodels</a></code> library to help us build the model.</p>
<p>The formula to predict the number of goals is defined as the combination of the team, opponent, and whether it is home or away goals. Take a look at the summary. The result of the <em>Generalized Linear Model</em> contains so much that we cannot explain all of them in this article. </p>
<p>But let’s focus on the <code>coef</code> column.</p>
<p>As you already know, the team side means a home match, and the opponent side means an away match. If the value is closer to 0, it indicates the possibility of a draw. If the value of the home side is positive, it means the team has a strong attacking ability. Teams with a negative value indicate that they have a not-so-strong attacking ability.</p>
<p>Having trained the model, we can now use it to make predictions. Let’s create a function to do so.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def predict_match(model, homeTeam, awayTeam, max_goals=10): home_goals = model.predict(pd.DataFrame(data={'team': homeTeam, 'opponent':awayTeam, 'home': 1}, index=[1])).values[0] away_goals = model.predict(pd.DataFrame(data={'team': awayTeam, 'opponent': homeTeam, 'home':0}, index=[1])).values[0] pred = [[poisson.pmf(i, team_avg) for i in range(0, max_goals+1)] for team_avg in [home_goals, away_goals]] return(np.outer(np.array(pred[0]), np.array(pred[1])))
</pre>
<p>The function has four parameters: </p>
<ul>
<li>the Poisson model to be used to make the predictions, </li>
<li>the home team, </li>
<li>the away team, and </li>
<li>the maximum number of goals. </li>
</ul>
<p>We set it to 10 as the highest a team can score within 90 minutes of gameplay. Remember, the formula combines all these to predict the number of goals.</p>
<p>We looped over the predicted number of home and away goals. We also looped over the maximum goals. </p>
<p>In each iteration, we calculate the probability mass function of the Poisson distribution. This tells us the probability of a team scoring several goals. Taking the outer product of the two sets of probabilities, the function created and returned a matrix.</p>
<p>Let me assume Arsenal and Manchester City are to face each other at Emirate Stadium and you want to make the prediction.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">print(model.predict(pd.DataFrame(data={'team': 'Arsenal', 'opponent': 'Man City', 'home':1}, index=[1])))</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code>1. 2.026391
dtype: float64
</code></pre>
<p>The model is predicting Arsenal to score two goals…</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">print(model.predict(pd.DataFrame(data={'team': 'Man City', 'opponent': 'Arsenal', 'home':0}, index=[1])))</pre>
<p>Output:</p>
<pre class="wp-block-preformatted"><code>1 1.284658
dtype: float64</code></pre>
<p>… and Manchester City to score 1.23 goals, approximately 3 goals in the match.</p>
<p>The model roughly predicts a 2-1 home win for Arsenal. </p>
<p>Now that the three members of the formula are complete, we can feed it to the <code>predict_match()</code> function to get the odds of a home win, away win, and a draw.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ars_man = predict_match(model, 'Arsenal', 'Man City', max_goals=3)</pre>
<p>Result:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">array([[0.03647786, 0.04686159, 0.03010057, 0.01288965], [0.07391843, 0.09495992, 0.06099553, 0.02611947], [0.07489383, 0.09621298, 0.06180041, 0.02646414], [0.05058807, 0.06498838, 0.04174394, 0.01787557]])
</pre>
<p>The rows and columns represent Arsenal and Manchester City’s chances of scoring a particular goal respectively. </p>
<p>The diagonal entries represent a draw since it is where both teams score the same number of goals. Below the line (the lower triangle of the array found using <code>numpy.tril</code>) is Arsenal’s victory, and above (the upper triangle of the array found using <code>numpy.triu</code>) is Man City’s.</p>
<p>Let’s automate this with Python.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import numpy as np # victory for Arsenal
np.sum(np.tril(ars¬_man, -1)) * 100
# 40.23456259724963 # victory for Man City
np.sum(np.triu(ars_man, 1)) * 100
# 20.34309498981432 # a draw
np.sum(np.diag(ars_man)) * 100
# 21.111376045176485
</pre>
</p>
<p>Our model tells us that Arsenal has a 40% chance of winning which is much more than Man City’s odds at 21%. That makes the earlier prediction of 2-1 correspond accordingly.</p>
<p>Feel free to compare your prediction with the test data and see how far or close you are to predict live results. We can now proceed to create a football prediction app on Streamlit. </p>
<p><a href="https://github.com/finxter/Football_Prediction" data-type="URL" data-id="https://github.com/finxter/Football_Prediction" target="_blank" rel="noreferrer noopener">Check my GitHub page</a> to see the full script.</p>
<p><a href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" data-type="URL" data-id="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" target="_blank" rel="noreferrer noopener">Check out the live demo app</a> to play with it!</p>
<h2>Streamlit Dashboard</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="733" height="541" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-65.png" alt="" class="wp-image-1111479" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-65.png 733w, https://blog.finxter.com/wp-content/uplo...00x221.png 300w" sizes="(max-width: 733px) 100vw, 733px" /></figure>
</div>
<p>In the file named <code>app.py</code>, you will see how I used <code>st.sidebar.selectbox</code> to display a list of all the clubs in the Premier League. This will appear on the left-hand side. Since the names of the club appeared twice, I made sure that only one was selected for prediction.</p>
<p>The rest of the code has been explained. If the button is pressed, the <code>get_scores()</code> function is executed and displays the prediction results.</p>
<p class="has-base-background-color has-background"><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f449.png" alt="?" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Recommended</strong>: <a href="https://blog.finxter.com/learning-streamlits-buttons-features/" data-type="post" data-id="462652" target="_blank" rel="noreferrer noopener">Streamlit Button — Ultimate Guide with Video</a></p>
<p>Notice that I didn’t save the dataset. </p>
<p>Whenever the app is opened, it will get real-time updates that will help it train the model for the next prediction. Also, since every code is not wrapped in a function, the order is important. </p>
<p>That is why the <code>get_scores()</code> function was called last. Of course, there are many ways to write the code and get the same result.</p>
<h2>A Word of Caution</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="728" height="482" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-66.png" alt="" class="wp-image-1111483" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-66.png 728w, https://blog.finxter.com/wp-content/uplo...00x199.png 300w" sizes="(max-width: 728px) 100vw, 728px" /></figure>
</div>
<p>I clarified to you from the beginning that this article is for educational purposes only and should not be used for anything else. </p>
<p>Many things can impact the result of a match that the model didn’t put into consideration. Change of a manager, injury, refereeing decision, player fitness, team morale, weather condition, plus the limitations of Poisson distribution used to make these predictions. </p>
<p>Of course, no model is perfect. So, use responsibly.</p>
<div class="wp-block-image">
<figure class="aligncenter size-full"><a href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app/" target="_blank" rel="noreferrer noopener"><img decoding="async" loading="lazy" width="725" height="408" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-58.png" alt="" class="wp-image-1111393" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-58.png 725w, https://blog.finxter.com/wp-content/uplo...00x169.png 300w" sizes="(max-width: 725px) 100vw, 725px" /></a></figure>
</div>
<h2>Prediction Result</h2>
<p>I deployed the app on Streamlit Cloud and tried to predict upcoming matches in the English Premier League. </p>
<p>The results were amazing. You can give it a try. I don’t expect the Premier League clubs to get those scores. Predicted result is not always the same as actual result. But I will rate the performance of our model if some, if not all, the home wins, draws, or away wins were predicted correctly.</p>
<h2>Conclusion</h2>
<div class="wp-block-image">
<figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="721" height="880" src="https://blog.finxter.com/wp-content/uploads/2023/02/image-67.png" alt="" class="wp-image-1111484" srcset="https://blog.finxter.com/wp-content/uploads/2023/02/image-67.png 721w, https://blog.finxter.com/wp-content/uplo...46x300.png 246w" sizes="(max-width: 721px) 100vw, 721px" /></figure>
</div>
<p>We have learned a lot today, ranging from data manipulation to model building. </p>
<p>You learned how to make football predictions using Poisson distribution. I did my best to make the explanation simple by leaving the mathematical theories and calculations behind. If you want to know more, you have the internet at your disposal. Alright, have a nice day.</p>
<p class="has-base-background-color has-background"><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f449.png" alt="?" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>Recommended</strong>: <a href="https://blog.finxter.com/how-i-built-a-house-price-prediction-app-using-streamlit/" data-type="post" data-id="1104457" target="_blank" rel="noreferrer noopener">How I Built a House Price Prediction App Using Streamlit</a></p>
<h2>Resources</h2>
<ol type="1">
<li><a href="https://www.kaggle.com/hugomathien/soccer" target="_blank" rel="noreferrer noopener"><sup>https://www.kaggle.com/hugomathien/soccer</sup></a></li>
<li><a href="https://github.com/jalapic/engsoccerdata" target="_blank" rel="noreferrer noopener"><sup>https://github.com/jalapic/engsoccerdata</sup></a></li>
<li><a href="http://api.football-data.org/index" target="_blank" rel="noreferrer noopener"><sup>http://api.football-data.org/index</sup></a></li>
<li><a href="http://www.football-data.co.uk/englandm.php" target="_blank" rel="noreferrer noopener"><sup>http://www.football-data.co.uk/englandm.php</sup></a></li>
<li><a href="https://jonaben1-football-prediction-app-nlr1w7.streamlit.app" target="_blank" rel="noreferrer noopener"><sup>https://jonaben1-football-prediction-app-nlr1w7.streamlit.app</sup></a></li>
</ol>
</div>
https://www.sickgaming.net/blog/2023/02/...streamlit/