Data Science im Fußball

Laufwege & Gewinnchancen mit Python, pandas und plotly analysieren!

 08.06.2021     Data Science

"Wer nicht genug läuft, kann auch nicht gewinnen." Das schrieb mein Kollege neulich an dieser Stelle in seinem Blog-Artikel zu Big Data Als ebenso begeisterter Fußball-Anhänger mit Vorliebe für Datenanalyse stellte sich mir sofort die Frage, ob es diesen Trend wirklich gibt.

 

Data Science im Fußball

Abbildung 1: Laufend gewinnen dank Data Science?

 

Gewinnt die Mannschaft mit mehr zurückgelegten Kilometern wirklich häufiger als der Gegner? Läuft sie den Gegner in Grund und Boden oder läuft sie nicht eher hinterher? Die Idee für diesen Blogeintrag war sofort umrissen. Auch wenn die Frage hier nicht abschließend und umfassend wissenschaftlich beantwortet werden soll, möchte ich doch mögliche Wege zur Beantwortung aufzeigen und darlegen, wie wir Python nutzen können, um diesem Ziel ein gutes Stück näher zu kommen.

Tabellenposition und Laufleistung

Eine sehr einfache Idee zur Beantwortung ist der Vergleich der Saison-Abschlusstabelle mit der Reihenfolge an abgespulten Gesamtkilometern eines Teams. Wir ziehen die Daten der Saison 2019/20 heran und suchen hier Korrelationen nach dem Strickmuster: „Stehen die Mannschaften, die insgesamt am meisten gelaufen sind, auch weit oben in der Tabelle?“ Das statistische Maß, welches genau diese Frage beantwortet, nennt sich übrigens Rangkorrelationskoeffizient.

Tabelle

Abbildung 2: Abschlusstabelle und gelaufene Kilometer der Saison 2019/2020

 

Wir können sowohl die Abschlusstabelle als auch die Tabelle der absolvierten Gesamtstrecke von der kicker Website abfragen und direkt aus der HTML-Datei mithilfe von pandas einlesen. Die ausgezeichnete pandas-Bibliothek stellt uns eine Vielzahl an Werkzeugen zur Datenanalyse bereit und bildet das Herzstück unseres kleinen Analyseprojektes in Python. Zunächst nutzen wir sie hier, um den beschriebenen Rangkorrelationskoeffizienten zu ermitteln.

Keine ausgeprägte Rangkorrelation

Mögliche Werte für die Rangkorrelation liegen zwischen -1 und +1. Bei einem Wert von +1 wäre die Reihenfolge der Vereine in beiden Tabellen exakt gleich. Wer in der Abschlusstabelle ganz oben steht, steht dann auch in der Laufleistungsrangfolge ganz oben. Eine Rangkorrelation von -1 ergäbe sich im gegenteiligen Fall, wenn die Reihenfolge gerade umgekehrt wäre. Ein Wert von 0 würde bedeuten, dass es keinerlei Zusammenhang zwischen der Position in der Abschlusstabelle und der Laufleistung gibt.

In unserem Fall ist diese Spearman’sche Rangkorrelation mit einem Wert von -0,11 äußerst gering (und negativ) ausgeprägt. Die Reihenfolgen in beiden Tabellen sind folglich nicht miteinander korreliert. Ein Ergebnis, das sich subjektiv gut nachvollziehen lässt: Während der souveräne Meister FC Bayern ebenso wie Vizemeister Borussia Dortmund im Mittelfeld der Laufleistung rangiert (Ränge 10 und 12), hat das abgeschlagene Schlusslicht SC Paderborn die drittmeisten Kilometer abgespult. Passenderweise sind andere Top-Teams wie Bayer Leverkusen und RB Leipzig dagegen auch überdurchschnittlich viel gelaufen (Rang 1 und 4) und Fortuna Düsseldorf musste sich in beiden Tabellen mit dem vorletzten Platz arrangieren.

Simpson-Paradoxon bei Fußballspielen?

Aber Vorsicht: Obwohl der FC Bayern nur eine durchschnittliche Gesamtstrecke im Saisonablauf zurückgelegt hat, könnte er durchaus in jedem einzelnen Spiel mehr gelaufen sein, als der jeweilige Gegner! Ein solches Phänomen wird als Simpson-Paradoxon bezeichnet. Wir könnten es fußballspezifisch folgendermaßen erklären: In Spielen mit Beteiligung des FC Bayern ergibt sich zuweilen eine einseitige Statik der Partie mit ausgeprägten Ballbesitzphasen. Weit in des Gegners Hälfte kreisen Spieler und Spielgerät dann in Handball-Manier um den Strafraum. Auf diese Weise verliert das Spiel aber an Chaos und Hektik und es wird in der Folge insgesamt wenig gelaufen. Hierbei kann die überlegene Mannschaft in direkter Relation zum Gegner noch immer mehr laufen, etwa um Löcher im Abwehrverbund zu erzeugen. Wir möchten daher den Blick von Statistiken der Gesamtsaison abwenden und uns die Daten einzelner Spiele anschauen.

Saisonanalyse für Eintracht Frankfurt

In der detaillierteren Analyse beschränken wir uns auf ein Team, für welches wir alle Saisonspiele separat betrachten möchten. Hierfür bieten sich bevorzugt Mannschaften aus dem Tabellenmittelfeld mit einer ausgeglichenen Saisonbilanz an, die sowohl einige Spiele gewonnen als auch verloren haben. Unsere Wahl fällt geographisch naheliegend auf Eintracht Frankfurt.

Wir greifen über Pythons request-Modul (für http-Anfragen) auf die kicker-Daten von allen Saisonspielen mit Beteiligung der Eintracht zu und legen diese unter Verwendung von Pythons sqlite3 in einer kleinen SQLite Datenbank ab. Sqlite3 ist Pythons Werkzeug aus der Standardbibliothek zur Anbindung an Datenbanken. Es ist auf die Verwendung von SQLite beschränkt, was für unser kleines Projekt aber mehr als ausreichend ist.

Dieser Vorgang erfordert im Detail ein wenig Aufmerksamkeit und könnte in einem weiteren Blog-Artikel genauer beleuchtet werden. Die grundsätzliche Idee ist jedoch einfach. Wir erfassen die von kicker bereitgestellten Spieldaten.

kicker Spieldaten


Abbildung 3: Die Spieldaten zum ersten Saisonspiel der Eintracht(Quelle: kicker)

 

Datenimport mit pandas

An dieser Stelle starten wir mit dem pandas-Import der Daten aus unserer kleinen SQLite Datenbank. Wir hatten die Daten dort genau in der Form eingespeist, wie wir sie aus den kicker-Spieldaten ausgelesen haben. Werfen wir einen Blick auf die Form dieser Rohdaten in unserem pandas-DataFrame:

 

pandas-DataFrame als Tabelle
Abbildung 4: Ein pandas-DataFrame kann als Tabelle aufgefasst werden. Die Datenstruktur bietet unzählige nützliche Transformations- und Analysemöglichkeiten. Hier sind die eingelesenen Rohdaten gezeigt.


Einzelne Spiele erstrecken sich hier also über zwei Zeilen (Heim- und Auswärtsteam) und die Einträge sind als Textinformation (Strings) hinterlegt. Somit kann hinter den Zahlen zwar ihre Einheit (km oder %) gekennzeichnet werden, die Spalten jedoch nicht direkt für Berechnungen herangezogen werden. Damit wir später diese Angaben entfernen und eine Konvertierung in Zahlen (Integer/Floats) ermöglichen, bedienen wir uns der eingebauten String-Manipulationsmöglichkeiten von pandas. Bevorzugt möchten wir außerdem einzelne Begegnungen in einer Zeile darstellen, so können wir direkt neue Spaltenwerte nach dem Motto: Differenz = Laufleistung_Frankfurt - Laufleistung_Gegner berechnen.

Datentransformation mit pandas

Dafür teilen wir unseren DataFrame in zwei Teile (Heim und Auswärts), die wir anhand des Spieltages in einem neuen DataFrame zusammenführen ("mergen"). Wir bekommen so jeweils die Begegnung von Heim- und Auswärtsteam in eine Zeile. Dort stellen wir die statistischen Größen (Ballbesitz, Passquote etc.), welche wir nicht weiter analysieren, einfach als Strings mit einem Doppelpunkt gegenüber.

Wir führen außerdem wichtige Spalten für unsere weitere Auswertung ein, welche wir aus Sicht von Eintracht Frankfurt gestalten. Wie viel ist Frankfurt gelaufen? Wieviel im Vergleich zum Gegner? Hat die Eintracht gewonnen? Dafür gehen wir vorzugsweise zweistufig vor. Für das Ergebnis zum Beispiel:

  1. War Frankfurt das Heimteam?
  2. Hat das Heimteam mehr Tore geschossen?

Schließlich sortieren wir einige Spalten aus, benennen andere um und erhalten folgendes DataFrame:

DataFrame aufbereitet

Abbildung 5: Unser für die Analyse aufbereitetes DataFrame.

 

In den einzelnen Begegnungen ist nun also die Laufleistung von Frankfurt und ihre Differenz zum Gegner als Fließkommawert sowie das Resultat aus Sicht der Eintracht verfügbar. Diese Informationen werden wir gleich für eine graphische Auswertung nutzen. Als Zusatzinformationen können wir weiterhin einige statische Kennzahlen (Torschüsse, Zweikampfquote usw.) angeben.

Graphische Auswertung mit plotly

Zwischen den verfügbaren Visualisierungsmöglichkeiten in Python (matplotlib, seaborn, bokeh u.v.m.) entscheiden wir uns für eine Darstellung mit plotly. Die Bibliothek plotly ermöglicht das Anfertigen von interaktiven Graphiken, sodass wir unsere Darstellung mit einigen Zusatzinformationen versehen und zur explorativen Erkundung nutzen können.

Im Kern legen wir einen Scatterplot an, in welchem die Laufdifferenz gegen die Laufleistung von Frankfurt aufgetragen wird. Jeder eingezeichnete Datenpunkt entspricht somit einer einzelnen Begegnung. Je weiter oben ein Datenpunkt liegt, desto größer ist die Laufdifferenz (Frankfurt ist viel mehr gelaufen als der Gegner). Je weiter rechts der Datenpunkt liegt, desto mehr ist Frankfurt absolut gelaufen. Die Form der Datenpunkte spiegelt das Ergebnis aus Sicht von Frankfurt wider.

Abbildung 6: Laufdifferenz versus Laufleistung als interaktive Grafik

 

Wir erkennen, dass sich die Laufleistung beider Mannschaften selten um deutlich mehr als 5 km unterscheiden. Die Ausnahme bildet ein Datenpunkt oben rechts, mit einer Differenz von rund 13,5 km. Fahren wir mit der Maus über die Datenpunkte, werden zusätzliche Informationen eingeblendet. So erkennen wir, dass unser Ausreißer einen 5:1 Sieg von Frankfurt gegen Bayern am 10. Spieltag beschreibt.

Zusätzlich können wir auf die Punkte klicken, woraufhin sich ein Link zur kicker Webseite des Datenursprungs öffnet. Auf diesem Wege erkennen wir, dass dieses Spiel auch für die Laufstatistik eine Besonderheit darstellt. Bereits in der 9. Spielminute erhielt Bayern einen Platzverweis, sodass fortan lediglich 10 Spieler (9 Feldspieler) zur Laufleistung des Teams beitragen konnten. Dies erklärt die große Differenz in der Laufleistung beider Teams – und vermutlich auch ein wenig das Zustandekommen des Endergebnisses. Für eine umfassend wissenschaftliche Analyse hinsichtlich des Einflusses von Laufleistung auf den Spielausgang sollten wir sensibilisiert sein, eine saubere Drittvariablenkontrolle bezüglich roter Karten vorzunehmen. Der Einfluss von roten Karten sollte somit möglichst ausgeklammert werden.

Ein deutlicher Trend für Frankfurt

An dieser Stelle entscheiden wir uns dafür, den Datenpunkt einfach auszublenden und eine neue Abbildung zu erzeugen. Die neue Graphik skaliert nun standardmäßig besser auf den interessanten Bereich von +/- 5 km Laufdifferenz und schließt zugleich den Ausreißer bei Erstellung der Randgraphiken aus.

Abbildung 7: Laufdifferenz versus Laufleistung als interaktive Grafik (modifizierte Version)


Diese Box-Plot Diagramme am oberen und rechten Rand der Abbildung zeigen die Verteilung jeweils vorkommender Werte einer Dimension getrennt nach dem Spielergebnis aus Sicht der Eintracht (verschiedene Farben der Boxen). Ihre Boxen erstrecken sich vom 25%-Perzentil bis zum 75%-Perzentil.

Wobei das 25%-Perzentil jener Datenpunkt ist, der größer als 25% und kleiner als 75% der anderen Datenpunkte ist. Entsprechend sehen wir "die mittleren 50%" der Datenpunkte in der Box, welche auch den markierten Median (50%-Perzentil oder Zentralwert) einschließt. Für eine Einschätzung der Genauigkeit (zuverlässige Aussage) ist das Konfidenzintervall um den Median als Delle in der Box dargestellt.

Ergänzt werden die Boxen um Antennen, welche häufig eine maximal 1,5-fache Box-Länge aufweisen (nicht standardisiert). Sie enden jedoch beim letzten, beobachteten Datenpunkt, sind somit also hier zumeist deutlich kürzer. Mit Hilfe der Antennen lassen sich insbesondere gut Ausreißer erkennen. Dies sind zunächst einmal alle Datenpunkte außerhalb der Antennen, welche in dieser Darstellungsform als Punkte extra markiert werden.

Wir finden hier einen Ausreißer bei der Laufdifferenz für Unentschieden und in der vorherigen Abbildung den beschriebenen Datenpunkt des Frankfurt-Sieges gegen Bayern.

Inhaltlich erkennen wir, dass am rechten Rand die blaue Box für Frankfurt-Siege sichtbar (und signifikant) oberhalb der orangen Box für Frankfurt-Niederlagen liegt. Die grüne Box für Unentschieden reiht sich in deren Mitte mit deutlicher Überlappung – insbesondere zur blauen Box – ein. Wir beobachten bei Siegen also tendenziell eine größere Laufdifferenz als bei Niederlagen.

Eine solche Tendenz lässt sich auch deutlich "mit bloßem Auge" im zentralen Scatterplot aufspüren. Während sich die orangen x-Punkte für Niederlagen eher im unteren Bereich befinden, sind die blauen Kreuze für Frankfurt-Siege systematisch nach oben verschoben. Die Trennung ist zwar nicht überschneidungsfrei, die Tendenz trotz der relativ geringen Anzahl von 34 (33 ohne Ausreißer) Datenpunkten dennoch bemerkenswert deutlich. Die absolute Laufleistung von Frankfurt (ohne Relation zum Gegner) wirkt sich im Übrigen dagegen praktisch nicht auf den Spielausgang aus (Vergleich der Box-Plots am oberen Rand).

Weiterhin ist auffällig, dass die meisten Datenpunkte unterhalb der Nulllinie liegen. Frankfurt scheint von der Anlage also eher zu den laufschwachen Teams zu gehören. Interessanterweise finden sich außerdem die beiden Siege gegen Hoffenheim am linken und rechten Rand des Spektrums. Es haben sich hier trotz gleichem Gegner scheinbar zwei Spiele mit äußerst unterschiedlicher Dynamik entwickelt. Sicher lassen sich noch weitere, interessante Erkenntnisse aus der Graphik entnehmen. Viel Spaß beim Erkunden!

Ein solches Projekt selber angehen?

Wir konnten mit unserem kleinen Python-Analyseprojekt der eingangs aufgeworfenen Frage ein ordentliches Stück näherkommen. Neben Web-Scraping per request-Modul und Datenbankanbindung mittels sqlite3, hat sich die ausgezeichnete pandas-Bibliothek für das Datenhandling als äußerst nützlich erwiesen. Anstelle von statistischen Kennzahlen (mit pandas ebenfalls komfortabel) haben wir uns für eine graphische Auswertung mithilfe von plotly entschieden.

Wer zu diesen Themen einen geeigneten Einstieg sucht, um selbst solche Analysen zu erstellen, sollte sich folgenden Kurs ansehen:

Python für Data Science – Datenanalyse und Einblick Machine Learning

 

Das könnte Sie auch interessieren

 

Python Schulung bei ExperTeach – eine Übersicht unserer Python Kurse

Warum Sie Python lernen sollten – Blog Artikel zur Programmiersprache Nummer Eins

Machine Learning & Big Data Training – Seminare zu Data Science