Viel später als erhofft geht es weiter mit der Tutorial Reihe. Beginnen mag ich heute mit einem etwas ausführlicheren theoretischen Teil über Fehleinschätzung und Verzerrung, die uns allen zu eigen sind. Danach folgt, wie versprochen, eine Erklärung zu den Grundlagen der grafischen Darstellung in R-Studio.
Fehleinschätzung als ständiger Gegner
Eines der Axiome der Psychologie ist, dass Menschen sich ihre Umwelt verständlich machen wollen. In einer komplexen Welt fällt dies schwer. Daher neigen wir alle dazu, zu vereinfachen, zu verkürzen, schlicht Fehler in der Interpretation unserer Umwelt zu begehen. Würden wir das nicht machen, würde uns wohl recht schnell der Kopf platzen. Die Mechanismen sind also hilfreich dabei, uns in der Welt zurecht zu finden. Wollen wir aber spezielle Beobachtungen über unsere Umwelt auf allgemeine Erkenntnisse auszuweiten, ist es wichtig, uns dessen bewusst zu sein, wie wir denken, uns zu reflektieren.
Im Folgenden umreiße ich einige so genannte Biases, denen wir oftmals aufsitzen, ohne sie zwangsläufig bewusst zu wollen. Ein aktuelleres Beispiel, was auch die Analytics Gemeinschaft betrifft, wird in einem Artikel von Seth Walder für ESPN deutlich: er befragte 26 Menschen aus der NFL Analytics Community zu diversen Fragen. Namita Nandakumar machte darauf aufmerksam, dass selbst die vermeintliche crème de la crème der Datenanalysten einem fundamentalen Fehler aufsaßen:
Der Bias, dem hier fast alle der Antwortenden aufgesessen sind, bezeichnet man als Lake Wobegon Effect: Die eigene Leistung wird als besonders, überdurchschnittlich eingeschätzt. Während also die meisten der Analysten davon ausgingen, dass es besonders sei, dass sie Tracking Daten benutzten, gaben fast alle an, dass sie sie einsetzten. Paradebeispiel für diese Art Überschätzung der eigenen Kompetenz ist, dass teils 90% einer Stichprobe angeben, sie wären überdurchschnittlich gute Autofahrer. Was ich hiermit zeigen mag ist, dass auch Experten Fehleinschätzungen unterliegen können. Heißt: Obacht!
Bias ABC: fundamentale Beispiele
Verzerrungen der Umwelt gibt es zuhauf, ich beschränke mich hier auf einige, die ich bei der Datenanalyse für fundamental erachte (für mich… ja, auch hier solltet ihr einen Bias vermuten):
Verzerrung | Bedeutung |
confirmation bias | die Tendenz, Fälle auszuwählen, die das eigene Argument stützen |
hindsight bias | die Tendenz, retrospektiv die Wahrscheinlichkeit eines bereits eingetretenen Ereignisses über zu bewerten |
selection bias | großes Thema, dazu auch hier unten ein paar Gedanken |
bias blind spot | die Tendenz, Verzerrungen nur bei anderen zu sehen, statt sie bei sich selbst zu bemerken |
salience bias | die Tendenz, sich eher mit beliebteren (kognitiv als auch emotional) Reizen zu befassen |
congruence bias | die Tendenz, seine eigene Hypothese mittels seiner Daten bestätigen zu wollen, statt alternative Thesen zu testen |
correspondence bias | die Tendenz, dispositionale Eigenschaften zu überschätzen und äußere Faktoren zu unterschätzen |
Diese Liste könnte man gerne weiter und weiter führen und am Ende stundenlange Vorlesungen füllen. Aber wie oben steht, muss ich einen Punkt ausführen:
Ich würde davon ausgehen, dass ihr, die hier noch mitlest, oft genug den klassischen NFL hot takes begegnet seid: läuft der Running Back 25 Mal, sind die Cowboys 25-1, wird der Quarterback 4,5 mal gesackt steht das Team bei 3-27 oder ähnliches. Jeder, der sich mit der Aussagekraft von total stats befasst hat, schmunzelt über derlei Quatsch. Grenzwerte sollte man nicht nach belieben setzen.
Daher muss man auch dann, wenn man tiefer in die Daten eintaucht, immer wieder schauen und vor allem begründen, wo man den Schnitt für seine Analyse setzt, welche Stichprobe man wählt, was man mit was warum vergleicht.
Die Beliebigkeit der Snaps – eine Problembeschreibung
Im Tutorial III habe ich am Ende auf zwei Variablen ausgewählt, welche Quarterbacks in die Grafik neben Kaepernick eingeflossen sind. Ich habe dabei versucht, diese möglichst objektiv über Rangfolgen einfließen zu lassen. Am Ende habe ich aber auch da bezüglich der Cuts für die jeweiligen Ränge quasi beliebige Grenzen gezogen: die oberen 50% für EPA und plays. Nimmt man es genau, hätte ich, um die bessere Hälfte der Backups einfließen zu lassen, die beiden Variablen inklusive einer Standardabweichung nach unten einschließen müssen. Um, grob gesagt, die Daten abzubilden, die man als durchschnittlich und darüber ansehen kann, also die im Bereich ab einer Standardabweichung unter dem Mittelwert liegen. Wie sähe das aus? So:

Zwei plays zählen in dieser Stichprobe schon als quasi durchschnittlicher Backup Quarterback bezüglich plays. Daher habe ich den Cut nach oben gesetzt. Verständlich, aber schon “unsauber”. Worauf ich hinaus will? Auch wenn wir tief in die Daten eintauchen, uns stundenlang Gedanken machen, uns durch Github, cran und Tutorials kämpfen, sollten wir am Ende wissen, wieso wir diesen oder jenen Grenzwert festlegen. Ich empfehle euch: setzt euch mit Normalverteilung, Standardabweichung und Konfidenzintervallen auseinander. Sonst könnte es sein, dass ihr nicht signifikant besser aus eurer Analyse heraus kommt als die establish the run Fraktion.
ggimage – Eine Einführung
Das package, mit dem ihr Grafiken zumeist bearbeiten werdet, heißt ggplot. Es bietet unzählige Möglichkeiten. Und es ist komplex. Daher werde ich euch heute nur ein paar Grundlagen zeigen. Florian wird in den folgenden Ausgaben mehr dazu schreiben. Ich bin gegen ihn ein ziemlicher Laie was die ästhetischen Möglichkeiten anbelangt, weswegen die Diagramme im folgenden bei Weitem nicht so schick sind wie die, die ihr auf Twitter und co. zu sehen bekommt. Wer sich schon einmal weiterführend mit dem Thema und den Möglichkeiten befassen will, dem sei Opensourcefootball von Sebastian Carl und Ben Baldwin ans Herz gelegt.
Hier zunächst noch der Code, um die packages und die Daten zu laden, die ihr braucht:
library(devtools)
library(dplyr)
library(dbplyr)
library(nflfastR)
library(DBI)
library(RSQLite)
library(stats)
library(tidyverse)
library(ggimage)
library(ggrepel)
library(ggpubr)
update_db()
connection <- dbConnect(SQLite(), "./pbp_db")
connection
pbp_db <- tbl(connection, "nflfastR_pbp")
Balkendiagramm
Wollt ihr euch einen groben Überblick über die Ausprägung eines Merkmals anschauen, sind Balkendiagramme eine einfache Visualisierung. Sehen wir uns zum Beispiel die air yards pro Team in dieser Saison an.
pbp_2020 <- pbp_db %>% filter(season == "2020") %>%
collect()
pbp_2020_air <- pbp_db %>%
filter(season == "2020" & posteam != "NA") %>%
group_by(posteam) %>%
summarise(m_airyards = mean(air_yards)) %>%
collect()
pbp_2020 sind alle play-by-play Daten dieser Saison, pbp_2020_air fasst die air yards pro Team in dieser Saison als Mittelwert zusammen. Ich möchte nun die mittleren air yards pro Team grafisch darstellen lassen und den Mittelwert über die Saison einzeichnen. Dafür benutze ich folgenden Code
airyards <- ggplot(pbp_2020_air, aes(x=posteam, y=m_airyards)) +
geom_bar(stat="identity", fill="blue", alpha=0.5) +
geom_hline(yintercept = mean(pbp_2020$air_yards, na.rm = TRUE), color = "red", linetype = "dashed")
airyards
Was sagt der Code aus? airyards habe ich den Plot genannt. Diesem weise ich generell mittels ggplot unseren oben definierten Datensatz pbp_2020_air zu, sowie auf der X-Achse die Teams im Ballbesitz, auf der Y-Achse die Mittelwerte der jeweiligen Airyards wie oben berechnet. Legt ihr hinter ggplot so Datenbereich, X und Y fest, gilt diese Festlegung für alle angehängten Befehle, wie etwa hier geom_bar.
Die Ergänzung stat=”identity” gibt hier an, dass statt der Anzahl die Ausprägung der abhängigen Variable (m_airyards) abgetragen wird. fill steuert die Farbe der Balken und alpha die Transparenz.
geom_hline zeichnet eine konstante, horizontale Linie beim Mittelwert der Air Yards aller Teams in dieser Saison ein. Der Zusatz na.rm = TRUE legt fest, dass fehlende Daten aus der Berechnung ausgeschlossen werden. So sieht das ganze dann aus:

Tortendiagramm
Tortendiagramme (pie charts) sind in Analytics Kreisen verpönt. Nicht ganz zu unrecht: viel zu oft werden sie falsch eingesetzt, Verzerrung droht. In Anbetracht dessen, wie oft sie jedoch in statistischen Auswertungen generell auftauchen, mag ich sie erwähnen.
Lediglich dann, wenn ihr Anteile aus einer Grundgesamtheit aufzeigen wollt, dürft ihr sie benutzen. Also dann, wenn ihr die Anteile eines Faktors an der Gesamtheit aller Faktoren der Stichprobe darstellen wollt. Als ein gutes Beispiel fiel mir – auch aus Fantasy-Sicht durchaus spannend – ein, den Workload der verschiedenen Rusher in einem Team grafisch darzustellen. Also wie oft bekommt ein Rusher im Verhältnis zu allen Laufspielzügen im Team den Ball.
Laufanteil <- pbp_db %>%
filter(season == 2020, rush == 1, play_type != "no_play") %>%
group_by(posteam, rusher_player_name) %>%
summarise(run_count = as.numeric(n())) %>%
mutate(run_team_count = sum(run_count),
run_share = run_count / run_team_count) %>%
arrange(-run_share) %>%
collect()
Ich habe zunächst alle running plays der saison 2020 heraus gefiltert, Penalties exkludiert und dann nach Team und Rusher gruppiert. Dann habe ich die Gesamtzahl der Runs pro Team zusammengefasst (run_team_count) und den Anteil je Rusher an den Laufspielzügen im Team errechnet (run_share). Anschließend habe ich noch mittels arrange(-run-share) absteigend nach dem Anteil der Laufversuche sortiert.
Es sei an dieser Stelle noch erwähnt, dass das Gruppieren nach rusher_player_name eigentlich ein No-Go im Umgang mit Daten ist. Immer dann, wenn ihr eine ID (hier rusher_player_id) zur Verfügung habt, solltet ihr nach dieser gruppieren und den Namen später dazu joinen. Ich verzichte an dieser Stelle darauf, da es in erster Linie um die Grafik geht, nicht um das Arbeiten und Transformieren von Daten.
Betrachten wir die Rusher der Green Bay Packers:
Laufanteil_GB <- Laufanteil %>% filter(posteam == "GB")
Laufanteil_GB

Die grafische Darstellung erhaltet ihr mittels folgendem Code:
runs <- ggplot(Laufanteil_GB, aes(x="", y=run_share, fill=rusher_player_name)) +
geom_bar(stat="identity", position = "stack") +
coord_polar(theta = "y", start=0)
runs

Streudiagramme, deren Vereinfachung und die Frage nach der Bias
Ich habe viel darüber nach gedacht, welche coolen Effekte ich hier aufzeigen könnte. Nach dem ich aber im Zuge der Spiele im jüngst beginnenden Herbst einige Aussagen über den Zusammenhang zwischen Passspiel und Temperatur am letzten Wochenende hören musste, betrachten wir einen recht banalen Zusammenhang: Hängt die Effizienz im passing game mit der Außentemperatur zusammen?
Rechnen wir zunächst Grad Fahrenheit in Grad Celsius um:
pbp_db <- pbp_db %>% mutate(Temperatur=(temp-32)*5/9)
Jetzt wähle ich nur die Passspielzüge aus, fasse deren epa über die Temperatur zusammen und bilde den jeweiligen Mittelwert, den ich am Ende mittels collect() aus der Datenbank abrufe.
Beispiel <- pbp_db %>% filter(pass == 1) %>% group_by(Temperatur) %>%
summarise(epa=mean(epa, na.rm = TRUE)) %>%
collect()
Ich möchte sowohl die Datenpunkte als auch deren Verlauf darstellen:
Temp_diag <- ggplot(Beispiel, aes(x = Temperatur, y = epa)) +
geom_point() +
geom_line()
Temp_diag
Im Code lege ich zunächst den Datensatz fest (Beispiel), dann die Grundlagen der Grafik mittels aes – hier zunächst die abhängige und unabhängige Variable (Temperatur und epa) und lasse mir ein Punkt- und Liniendiagramm zeitgleich anzeigen (geom_point() + geom_line()):

Das sieht sehr wild aus und zeigt auch wenig. Um eine grobe Aussage über Zusammenhänge optisch darzustellen, braucht obiger Code nur eine Zeile:
Temp_diag <- ggplot(Beispiel, aes(x = Temperatur, y = epa)) +
geom_point() +
geom_line() +
geom_smooth()
Temp_diag

geom_smooth fasst iterativ nicht nur den Wert von epa zusammen, sondern auch einen modellierten Vertrauensintervall. Generell können wir einfach Aussagen über den Zusammenhang der beiden Variablen aus der Grafik ablesen: er scheint fast 0. Aber wir erahnen in obiger Grafik, dass in den Extremwerten der Temperaturskala große Abweichungen vom modellierten Erwartungsbereich auftreten. Woran könnte das liegen? Welchem Bias sitze ich auf? Oder gibt es alternative Erklärungen? Kommentiert gern, überlegt und belegt gerne auch.
Wie es weiter geht
Ich werde weiterhin einige Problemfelder theoretischer Natur an- und besprechen. Zusammenhangsmaße und deren Qualität wird beim nächsten Mal ein wichtiges Thema. Im praktischen Teil überlasse ich zunächst jedoch Florian das Feld. ggplot kann viel. Sehr viel. Zu den Möglichkeiten gibt Florian euch einigen Input, den ich so nicht liefern kann.
Bis dahin der obligatorische Hinweis: bei Fragen fragt, versucht den Aufbau des Codes zu verstehen und habt Spaß.