Jag bygger en modell för att förutspå fotbollsmatcher (del 2)

Jag har tidigare byggt en modell för att förutspå fotboll där jag använde mig av en målrankning baserad på hur många nål hemma- och bortalaget gjort under sina senaste sex matcher. Nu tänker jag göra det igen men med en helt annan approach. Jag tänker ta inspiration från dataspelens värld, närmare bestämt den omåttligt populära FIFA-serien från EA Sports. Idén att använda dataspelssiffor för att bygga prediktiva modeller fick jag från den här boken av Masaru Kanemoto. De flest, inklusive jag själv, saknar förmågan att bedöma exakt vad som gör en fotbollsspelare bättre än en annan fotbollsspelare. Det är här EAs team av professionella bedömare kommer in och gör jobbet åt oss. Dessa bedömare gör ett utmärkt jobb att ta reda på hur bra olika spelare är och det bästa är att vi får en exakt siffra som gör mitt jobb enklare. De som spelar FIFA-spelen är ofta stenhårda entusiaster som vill vara sin idoler i spelet och om spelarna i spelet inte beter sig som de verkliga spelarna kommer det leda till en massa sura miner och färre sålda spel. Därför råder det stenhårt tryck på bedömarna att ta fram realistiska värden.

En spelare i EAs FIFA bedöms från 1 till 100 på en mängd olika färdigheter, bland annat skott, passningar och brytningar. Detta resulterar i ett helhetsbetyg, till exempel har den högst rankade spelaren nu, Lionel Messi, 93 i helhetsbetyg. Baserat på spelarbetygen får varje lag också ett helhetsbetyg (FIFA-score) mellan 1 till 100 och det är dessa jag kommer att använda mig av.

Metod

Metoden jag kommer använda är i stort sett samma som förra gången, en scatter plot i Excel för att ta fram hur stark korrelationen är mellan lagens FIFA-score och resultatet i matchen. En scatter plot gör man enkelt i Excel genom att markera två talserier och sedan välja ”Infoga”, ”Scatter plot”. Men att ta fram datan till detta kommer inte bli så enkelt och en hel del jobb kommer att krävas.

Jag börjar med att ta fram ett datablad med matcher och resultat som ser ut ungefär så här.

Datum Hemmalag Bortalag Resultat 
2022-11-05 Barcelona Almeria Hemmavinst 
2022-11-05 AC Milan Spezia Hemmavinst 
2022-11-05 Everton Leicester Bortavinst 
2022-11-05 FC Andorra Racing Santander Bortavinst 

En lista med matcher innehållandes datum, hemma- , bortalag och resultatet. Anledningen till att jag kodat det så där är att det ska gå att summera antalen av resultaten i en Pivot-tabell i Excel vilket det inte gått att göra om formatet varit till exempel ”1-0”. Jag hade detta blad liggande på datorn men det kan väldigt enkelt tas fram till exempel härifrån där man kan ladda ner Excel-filer med resultat för en massa ligor från långt tillbaka.

Jag blir tvungen att koda (suck!)

Mitt Excel-blad innehöll 10729 matcher från åren 2020 till 2022. Nu till det lite knepigare: Jag var på jakt efter ett liknande Excel-blad fast för FIFA-scores och rotade runt på Internet en bra stund utan att hitta något. Det finns en massa olika sidor som listar FIFA-scores men ingen av dem var schysst nog att tillhandahålla ett databehandlingsvänligt format, inte ens .csv. Suck tänkte jag och började rulla upp ärmarna, dags att börja skrapa data. Jag tog fram ett skript i C# som skrapar sidan fifaindex.com och sparar dessa till en fil kallad ”fifaoutput.csv”. Att skrapa data är helt enkelt att läsa av det som står på en hemsida och sedan bearbeta denna info på någor sätt.

Koden är inte vacker men den gör sitt jobb och jag behöver bara använda den en gång. Variabeln kallad ”datecounter” är till för att hämta data från 2020 till 2022, anledningen till att den börjar på 350 är för att sajtens url-struktur är sådan.

using System;
using System.Linq;
using System.Net;
using System.IO;

using HtmlAgilityPack;

namespace fifaScores
{
public void getFifa()
        {
            string urlbuf = "https://www.fifaindex.com/teams/fifa22_";
            string url="";
            using (StreamWriter writer = new StreamWriter("fifaoutput.csv"))
            {
                for (int datecounter = 350; datecounter <= 565; datecounter++)
                {
                    for (int page = 1; page <= 22; page++)
                    {
                        try
                        {
                            url = urlbuf + datecounter.ToString() + "/?page=" + page.ToString();
                            Console.WriteLine(url);
                            HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
                            HttpWebResponse response = request.GetResponse() as HttpWebResponse;

                            var ResponseStream = response.GetResponseStream();

                            HtmlDocument document = new HtmlDocument();
                            document.Load(ResponseStream, Encoding.UTF8);

                            var root = document.DocumentNode;

                            List<string> f = new List<string>();
                            List<string> f2 = new List<string>();
                            List<string> f3 = new List<string>();
                            List<string> f4 = new List<string>();
                            List<string> f5 = new List<string>();
                            
                            foreach (HtmlNode node in document.DocumentNode.SelectNodes("//*[contains(@data-title,'Name') ]"))
                            {
                                f.Add(node.InnerText);
                            }
                            foreach (HtmlNode node in document.DocumentNode.SelectNodes("//*[contains(@data-title,'League') ]"))
                            {
                                f2.Add(node.InnerText);
                            }
                            foreach (HtmlNode node in document.DocumentNode.SelectNodes("//*[contains(@data-title,'OVR') ]"))
                            {
                                f3.Add(node.InnerText);
                            }
                            foreach (HtmlNode node in document.DocumentNode.SelectNodes("//*[contains(@class,'dropdown-toggle') ]"))
                            {
                                f4.Add(node.InnerText);
                            }                   

                            int i = 0;
                            string strbuf = f4[5].Trim();
                            try
                            {
                                DateTime date = DateTime.ParseExact(strbuf, "MMM. dd, yyyy", System.Globalization.CultureInfo.InvariantCulture);
                                strbuf = date.ToString("yyyy-MM-dd");
                            }
                            catch
                            {
                                try
                                {
                                    DateTime date = DateTime.ParseExact(strbuf, "MMM. d, yyyy", System.Globalization.CultureInfo.InvariantCulture);
                                    strbuf = date.ToString("yyyy-MM-dd");
                                }
                                catch
                                {
                                    DateTime date = DateTime.ParseExact(strbuf, "MMMM d, yyyy", System.Globalization.CultureInfo.InvariantCulture);
                                    strbuf = date.ToString("yyyy-MM-dd");
                                }
                            }
                            foreach (string item in f)
                            {
                                writer.WriteLine(item + ";" + f2[i] + ";" + f3[i] + ";" + strbuf);
                                i++;
                            }

                        }
                        catch { continue; }

                    }
                }
            }
         
        }
     static void Main(string[] args)
        {
            Program prog = new Program();
prog.getFifa();
}
}

Koden körs i konsollen, jag använde mig av Visual Studio som är gratis för privatpersoner att använda. Efter att programmet körts så fick jag en .csv-fil som jag importerade till Excel. Slutresultatet såg som i tabell 2 nedan.

Team League rating Date 
Real Madrid Spain Primera División (1) 85 2023-01-30 
Manchester City England Premier League (1) 85 2023-01-30 
Liverpool England Premier League (1) 85 2023-01-30 
Bayern München Germany 1. Bundesliga (1) 84 2023-01-30 
Paris Saint-Germain France Ligue 1 (1) 84 2023-01-30 
Tabell 2.

Jag fick alltså en Excel-fil med lagnamn, liga, FIFA-score och datum (125 377 rader lång). Nu är det ju så att dessa FIFA-scores inte är statiska utan varierar hela tiden, detta blir ofta väldigt tydligt för nykomlingar i en liga som ofta har höjt sin rating väsentligt inför den nya säsongen i den högre ligan. Varje datum i tabell 1 innebär en ny uppdatering av FIFA-scores Detta betyder ju att jag måste matcha min lista i tabell 1 med den här listan i tid, för varje match måste jag hitta de FIFA-scores för hemma- och bortalaget som ligger närmast uppdateringsdatumet i tabell 2. Jag insåg att det här också kommer att innebära lite jobb.

Jag orkade inte krångla för mycket utan bestämde mig för att göra detta i Excel, tyvärr insåg jag också att det inte skulle gå utan att koda igen. Nu var jag rejält trött på pillande och lät därför min kompis ChatGPT göra ett makro i Excels inbyggda programmeringsspråk Visual Basic. Till lite av min förvåning lyckades chat-boten göra ett dylikt skript nästan omgående.

Sub FindClosestRating()
    Dim ws1 As Worksheet, ws2 As Worksheet
    Dim homeTeam As String, closestDate As Date, closestRating As Integer
    Dim currentDiff As Long, closestDiff As Long
    Dim homeflag As Integer
    Dim awayflag As Integer
    
    Dim awayTeam As String, closestDateaway As Date, closestRatingaway As Integer
    Dim currentDiffaway As Long, closestDiffaway As Long
    
    ' Set references to Sheet1 and Sheet2
    Set ws1 = ThisWorkbook.Worksheets("Sheet1")
    Set ws2 = ThisWorkbook.Worksheets("Sheet2")
    
    For i2 = 1 To 10739
    ' Get the home team and date from Sheet1
    homeTeam = ws1.Range("B" & i2).Value
    closestDate = ws1.Range("A" & i2).Value
     awayTeam = ws1.Range("C" & i2).Value
    closestDateaway = ws1.Range("A" & i2).Value
    ' Initialize the closest difference to a large value
    closestDiff = 99999
      closestDiffaway = 99999
      homeflag = 0
      awayflag = 0
    ' Loop through each row in Sheet2 to find the closest matching rating
    For i = 2 To ws2.Range("A" & Rows.Count).End(xlUp).Row
        ' Check if the team name matches the home team
        If ws2.Range("A" & i).Value = homeTeam Then
        homeflag = 1
            ' Calculate the absolute difference between the date and the current row's date in Sheet2
            currentDiff = Abs(closestDate - ws2.Range("D" & i).Value)
            
            ' If the current difference is smaller than the closest difference so far, update the closest rating and difference
            If currentDiff < closestDiff Then
                closestRating = ws2.Range("C" & i).Value
                closestDiff = currentDiff
            End If
      End If
             If ws2.Range("A" & i).Value = awayTeam Then
             awayflag = 1
            ' Calculate the absolute difference between the date and the current row's date in Sheet2
            currentDiffaway = Abs(closestDateaway - ws2.Range("D" & i).Value)
            
            ' If the current difference is smaller than the closest difference so far, update the closest rating and difference
            If currentDiffaway < closestDiffaway Then
                closestRatingaway = ws2.Range("C" & i).Value
                closestDiffaway = currentDiffaway
            End If
        End If
    Next i
    
    ws1.Range("F" & i2).Value = "-"
    ws1.Range("G" & i2).Value = "-"
    ' Update the rating in Sheet1
    If homeflag = 1 Then
    ws1.Range("F" & i2).Value = closestRating
    homeflag = 0
    End If
    If awayflag = 1 Then
     ws1.Range("G" & i2).Value = closestRatingaway
     awayflag = 0
     End If
     Next i2
End Sub

Redo att ”plotta”

Jag la in båda filerna (tabell 1 och 2) i en Excel-fil som två blad (”Sheet1”, ”Sheet2”). Makrot tar sedan lagnamn från ”Sheet1” och läser av alla celler med samma lagnamn i ”Sheet2” sedan matchar det jämför det datumen i båda bladen och uppdaterar om datumet i ”Sheet2” är närmare det i ”Sheet1” än föregående datum. För det närmaste datumet hämtar det aktuellt FIFA-score och lägger in det i ”Sheet1”, så här:

Datum Hemmalag Bortalag Resultat Hemma-FIFA Borta-FIFA FIFA-rating 
2022-11-05 Barcelona Almeria Hemmavinst 84 73 11 
2022-11-05 AC Milan Spezia Hemmavinst 81 72 
2022-11-05 Everton Leicester Bortavinst 77 79 -2 
2022-11-05 FC Andorra Racing Santander Bortavinst 63 67 -4 
2022-11-05 Huesca Villarreal II Hemmavinst 69 64 
Tabell 3

Makrot var inte snabbt, det tog nog 10 timmar för det att blir klar med denna körning men efter det fick jag äntligen data som jag kunde köra Scatter plot på. Skillnaden mellan hemma-FIFA och borta-FIFA har jag kallat för FIFA-rating och är positiv om hemmalaget har en högre rating än bortalaget, som Barcelona i tabell 3. På motsvarande sätt blir den negativ om bortalaget är högre rankat som Racing Santander ovan. Slutresultatet blev 7690 mot 10739 i min första fil, anledningen är att lagnamnen i de båda filerna inte alltid är lika och vissa lag i min ursprungsfil inte finns med listan med FIFA-scores. Sedan gjorde jag en pivot-tabell i Excel med FIFA-rating och andel hemma- och bortavinster som i tabell 4. Anledningen till att jag inte tog med oavgjort som resultat är att jag har kommit fram till att det är mycket svårt att tippa kryss.

FIFA-rating Andel bortavinster Andel hemmavinster 
-14 0% 100% 
-13 67% 0% 
-12 77% 15% 
-11 74% 4% 
-10 73% 18% 
-9 68% 8% 
-8 68% 15% 
-7 50% 18% 
-6 51% 23% 
-5 48% 24% 
-4 45% 27% 
-3 39% 31% 
-2 39% 33% 
-1 33% 38% 
30% 41% 
29% 45% 
24% 47% 
20% 53% 
19% 56% 
16% 61% 
11% 64% 
9% 77% 
7% 80% 
14% 63% 
10 11% 76% 
11 7% 78% 
12 0% 77% 
13 17% 67% 
14 50% 50% 
Tabell 4

Modellen börjar ”ta sig”

I Tabell 4 har jag lagt in andelen bortavinster och hemmavinster för varje FIFA-rating som rör sig mellan -14 och 14. Det finns alltså inte någon liga där något lag har mer än 14 i skillnad i FIFA-score mot något annat lag. I ändarna blir procentfördelningen tokig på grund av för få matcher och jag bestämde mig för att bara använda FIFA-rating mellan -9 och 9.

Scatterplotten ger en modell för hemma- och bortamatcher med mycket högt R²-värde. R² ger ett mått på korrelationens styrka där 1 är det högsta värdet. Mina modeller ger 0.96 och 0.95, mycket höga således och uttrycker ett starkare samband än vad min målrankning gjorde i min förra artikel. Tittar man på bild 1 och 2 ser man att punkterna ligger som ett fint band.

Bild 1. FIFA-rating mot andel hemmavinster
Bild 2. FIFA-rating mot andel bortavinster

Vi kan nu räkna ut sannolikhet för att en match ska resultera i hemmavinst genom att ta formeln:

y = 0.0361x + 0.4226 där x är FIFA-ratingen: Tar vi ett exempel från tabell AC Milan mot Spezia är FIFA-ratingen 9 och sannolikheten för hemmavinst blir därför 75 %. Nästa steg är att testa modellen i ett så kallat Walk forward-test.

2 svar på ”Jag bygger en modell för att förutspå fotbollsmatcher (del 2)”

  1. Mycket intressant har fått 13 rätt 4 ggr sedan 2015 flera ggr miljoner 1 st ensam på Europa har skrivit ner all statistik på papper har idéer i huvudet kan inte Excel hör gärna av dig

    Svara

Lämna en kommentar