Datenaufbereitung#

In diesem Notebook werden die Daten aus Webscrapping und der Sentimentanalyse für die Analyse vorbereitetet und angereichert.

import json
import pandas as pd
import numpy as np 
from pymongo import MongoClient
from scipy.stats import ttest_ind
from difflib import SequenceMatcher

#import plotly.express as px
#import plotly.figure_factory as ff
#from plotly.offline import init_notebook_mode
#init_notebook_mode() # To show plotly plots when notebook is exported to html
mongodb_pass = json.load(open('API_Data.json'))['mongoDB_pass'] # password mongodb user
client = MongoClient(mongodb_pass)
db = client.gc_nfl
mycoll = db.gc_games
pd.options.display.max_colwidth = 1000

Hole Daten von MongoDB#

Dafür werden zu nächste die Daten aus mongodb abgerufen und in einen Dataframe umgewandelt. Die explorative Datenanalyse wird am Beispiel der Season 2021/2022 gezeigt, da diese Season bereits abgeschlossen ist.

pipeline = [
        {'$match':{'year':'2021'}},
        {'$project':{
                '_id':0
        }}
        ]

x = mycoll.aggregate(pipeline)
df0 = pd.DataFrame.from_dict(x)

Vorbereitung Entitäten#

Für den Erkenntnisgewinn sind die Enitäten der Kommentare interessant, welche Mannschaftsnamen enthalten. Die Mannschaftsnamen der NFL-Teams bestehen aus zwei Bestandteilen: einen Namen des Heimatsort (z.B. Tampa Bay) und einem Teamnamen (z.B. Buccaneers). Ziel der Data Preperation ist es so viele Enitäten, wie möglich als Mannschaftsnamen zu identifizieren. Dafür müssen Synonyme wie z.B. Spitznamen oder nur der Heimatort als Mannschaftsnamen und Rechtschreibfehler erkannt werden.

df0.head(5)
team1 score1 team2 score2 year week videoID comment entity salience score magnitude
0 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated players 0.800480 0.0 0.0
1 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated NFL 0.136442 0.0 0.0
2 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated Tampa Bay Buccaneers 0.063078 0.0 0.0
3 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc #🐰💀 vs #🤠🦆 #🐰 1.000000 0.1 0.1
4 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc OMG!! I really thought we were gonna beat the goat!! Such an amazing game!! game 0.528493 0.9 0.9

Berücksichtigung von Rechtschreibungsfehler#

Insofern die Entitäten mit Rechtschreibfehler von Google NLP erkannt wurden, sollen diese mit Hilfe des SequenceMatchers und Casefold zu einem “team_by_entity”-Wert zugeordnet werden.

df0['entity'] = df0['entity'].apply(lambda x: x.casefold())

Der SequenceMatcher berechent einen Ähnlichkeitswert von zwei Strings. Für das Projekt wird der Grenzwert auf 0.8 festgelegt. Mit der Funktion match_sequence() können die Teamnamen mit den Enität aus den Kommentaren abgeglichen werden. Liegt Ähnlichkeit vor, werden die Teamnamen der Enitäten korrigiert.

myStr1 = "Titans"
myStr2 = "tittans"

SequenceMatcher(a=myStr1.casefold(),b=myStr2.casefold()).ratio()
0.9230769230769231
def match_sequence(string1, string2):
    '''
    Input: string1 -> Entität
           string2 -> Textstelle im Kommentar
    '''
    value = string2
    if (SequenceMatcher(a = string1.casefold(), b = string2.casefold()).ratio() > 0.8):
        value = string1
    return value
df0['entity']= df0.apply(lambda x: match_sequence(x['team1'], x['entity']), axis=1)
df0['entity']= df0.apply(lambda x: match_sequence(x['team2'], x['entity']), axis=1)

Mit der Anwendung des SequenceMatcher konnten 965 weitere Enitäten zugeordnet werden.

Erstellungen eines Synonym-Wörterbuchs#

Die Datenbasis des Synonym-Wörterbuchs ist eine Tabelle aus Github, welche die vollständigen Mannschaftsnamen enthält.

nfl_teams = pd.read_csv('https://gist.githubusercontent.com/cnizzardini/13d0a072adb35a0d5817/raw/f315c97c7677845668a9c26e9093d0d550533b00/nfl_teams.csv')
nfl_teams['Name_1'] = nfl_teams['Name'].apply(lambda x : x.split()[-1])
nfl_teams['Name_2'] = nfl_teams['Name'].apply(lambda x : " ".join(x.split()[0:-1]))
nfl_teams.head()
ID Name Abbreviation Conference Division Name_1 Name_2
0 1 Arizona Cardinals ARI NFC West Cardinals Arizona
1 2 Atlanta Falcons ATL NFC South Falcons Atlanta
2 3 Baltimore Ravens BAL AFC North Ravens Baltimore
3 4 Buffalo Bills BUF AFC East Bills Buffalo
4 5 Carolina Panthers CAR NFC South Panthers Carolina

Mit den Spalten ‘Name_1’ und ‘Name_2’ wird der vollständige Mannschaftsname in den Teamname und den Heimatort getrennt, um die ersten Synonyme zu erhalten. Der Teamname dient in den nachfolgenden Betrachtungen als zentrale Entität (“team_by_entity”) für den Mannschaftsnamen. Alle anderen Synonyme werden dem Teamnamen zugeordnetet.

Nachfolgend wird die Tabelle mit den gebildeten Entitäten verschlankt. Die Werte für “team_by_entity” werden mit dem zugehörigen Synonymen kombiniert und unterhalb der bestehenden Tabelle angefügt. Schließlich werden die Spaltennamen umbenannt.

df_name2 = nfl_teams[['Name_1','Name_2']].copy(deep=True)
df_name1 = nfl_teams[['Name_1']].copy(deep=True)
df_name1['Name'] = df_name1['Name_1']
nfl_teams = nfl_teams.merge(df_name2.rename(columns={'Name_2':'Name'}),how='outer')
nfl_teams = nfl_teams.merge(df_name1,how='outer')
nfl_teams.drop(['ID','Abbreviation', 'Conference','Division','Name_2'], axis=1, inplace=True)
nfl_teams.rename(columns={'Name':'entity','Name_1':'team_by_entity'}, inplace=True)
vikings = nfl_teams[nfl_teams['team_by_entity'] == "Packers"] 
vikings
entity team_by_entity
11 Green Bay Packers Packers
43 Green Bay Packers
75 Packers Packers

Für die Mannschaft “Packers” ist “Cheeseheads” ein sehr gängiger Spitzname. Solche Entitäten sollen ebenfalls berücksichtigt werden. Mit der nachfolgenden Funktion können derartige Synonyme hinzugeügt werden. Exemplarisch wird dies für drei Teams durchgeführt.

def append_pairs(df, entity, team_by_entity, single=True):
    '''
    takes: pandas dataframe, entity and team_by_entity, trigger for multiple or single values
    if single=False the input for entity and team_by_entity has to be a list.
    returns a pandas dataframe object that includes old and new data.
    '''
    if single:
        new_pair = {
                    'entity':[entity],
                    'team_by_entity':[team_by_entity]
                }
    else:
        new_pair = {
                    'entity':entity,
                    'team_by_entity':team_by_entity
                }   
    return pd.concat([df,pd.DataFrame(new_pair)])
nfl_teams = append_pairs(nfl_teams, "Cheeseheads", "Packers", single=True)
nfl_teams = append_pairs(nfl_teams, "Redskins", "Commanders", single=True)
nfl_teams = append_pairs(nfl_teams, "Bucs", "Buccaneers", single=True)

Es ist denkbar, die Synonyme noch deutlich weiter zu ergänzen, z.B. mit den Namen der Quaterbacks. Dem Team “Buccaneers” könnte so weitere 1700 Kommentare mit der Entität “Brady” zugeordnet werden.

Anwendung des Synonym-Wörterbuchs#

Das Synonym-Wörterbuch wird im folgenden auf den gesamten Dataframe angewendet.

df0['entity'] = df0['entity'].apply(lambda x: x.casefold())
df0['team_by_entity'] = None
for index, row in nfl_teams.iterrows():
    df0['team_by_entity'] = np.where(df0['entity'] == row['entity'].casefold(),row['team_by_entity'], df0['team_by_entity'])
df0['team_by_entity'].unique()
array([None, 'Buccaneers', 'Cowboys', 'Patriots', 'Jets', 'Rams',
       'Broncos', '49ers', 'Eagles', 'Chiefs', 'Saints', 'Falcons',
       'Steelers', 'Ravens', 'Browns', 'Dolphins', 'Texans', 'Lions',
       'Commanders', 'Jaguars', 'Raiders', 'Giants', 'Bills', 'Packers',
       'Titans', 'Colts', 'Vikings', 'Panthers', 'Cardinals', 'Bears',
       'Chargers', 'Bengals', 'Seahawks'], dtype=object)
df0['team_by_entity'].isna().sum()
330066

Die Dokumente, welche keiner “team_by_enity” zugeordnet werden konnten, werden entfernt.

df0.dropna(subset='team_by_entity').head()
team1 score1 team2 score2 year week videoID comment entity salience score magnitude team_by_entity
2 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated tampa bay buccaneers 0.063078 0.0 0.0 Buccaneers
12 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc Though Dallas has nothing to be ashamed of, this game was stripped from them. That was definitely a push-off by Godwin. Outside of that, it was an awesome game to watch. dallas 0.295658 0.0 0.0 Cowboys
30 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc I’m very lost Tom brad 50mil contract best in the league barely get hurts. But go to Dallas 160mil for dak couldn’t clutch a last year game and get hurt a lot df🤔going on here the goat get paid less but a person who get hurt a lot get paid more with no rings🤨 dallas 0.021111 -0.1 0.1 Cowboys
36 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc Brady the "goat" barely very barely beating the cowboys.... I'd even go as far as to say struggled to beat them, barely beat them.... the goat.... ok sure... 2 points... :/ cowboys 0.089547 0.1 0.1 Cowboys
50 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc Can someone tell me how to get the Patriots game and watch it on my phone thank you patriots 0.073656 0.2 0.2 Patriots

Ebenfalls werden die Dokumente entfernt, welche ein Teamnamen enthalten, welcher nicht für das betrachtete Spiel relevant ist und damit nicht die Stimmung der spielenden Mannschaften repräsentieren, z.B. ein Kommentar zu den Cowboys im Spiel der Packers gegen Buccaners.

Anreichung des DataFrames#

df0['team_by_entity'] = np.where((df0['team_by_entity'] == df0['team1']) | (df0['team_by_entity'] == df0['team2']), df0['team_by_entity'], None )

Im nächsten Schritt werden den Größen ‘Score’ und ‘Magnitude’ ein “Sentiment” zugeordnet. Dafür werden Schwellenwerte für negativ, neutral und positiv festgelegt.

senitment

score

magnitude

positive

> 0.1

> 0.1

negative

> -0.1

>0.1

neutral

<= abs(0.1)

<= 0.1

Um an dieser Stelle den “Floating-point error” zu umgehen, werden die Fließkommazahlen mit 10 multipliziert und in einen Integer umgewandelt, sodass sie ohne Fehler verglichen werden können.

df0['sentiment'] = "" 
series_score = (df0['score']*10).astype(int)
series_mag = (df0['magnitude']*10).astype(int)
df0['sentiment'] = np.where((abs(series_score) <= 1) & (series_mag <= 1), 'neutral', df0['sentiment'] )
df0['sentiment'] = np.where((series_score <= -1) & (series_mag > 1), 'negative', df0['sentiment'] )
df0['sentiment'] = np.where((series_score >= 1) & (series_mag  > 1), 'positive', df0['sentiment'] )
df0.head(3)
team1 score1 team2 score2 year week videoID comment entity salience score magnitude team_by_entity sentiment
0 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated players 0.800480 0.0 0.0 None neutral
1 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated nfl 0.136442 0.0 0.0 None neutral
2 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated tampa bay buccaneers 0.063078 0.0 0.0 Buccaneers neutral

Für die Analyse werden zwei weitere Spalten erzeugt. Die Spalte “win_for_entity” enthält die Attribute “win”, “draw” oder “defeat”, je nachdem ob die Entität aus der Zeile gewonnen oder verloren hat. Die Spalte “winner” enthält jeweils die Mannschaft, welche gewonnen hat.

df0['score1'] = df0['score1'].astype('int')
df0['score2'] = df0['score2'].astype('int')
df0['win_for_entity'] = 'draw'
df0['winner'] = np.where(df0['score1']>df0['score2'],df0['team1'],df0['team2'])
df0['win_for_entity'] = np.where(df0['winner']==df0['team_by_entity'],'win','defeat')
df0.head(3)
team1 score1 team2 score2 year week videoID comment entity salience score magnitude team_by_entity sentiment win_for_entity winner
0 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated players 0.800480 0.0 0.0 None neutral defeat Buccaneers
1 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated nfl 0.136442 0.0 0.0 None neutral defeat Buccaneers
2 Buccaneers 31 Cowboys 29 2021 Week 1 HzkUcSd3Utc They made all the bad NFL players sign as Tampa Bay Buccaneers sometimes they get heated tampa bay buccaneers 0.063078 0.0 0.0 Buccaneers neutral win Buccaneers

Aggregation#

Die bisherige Bearbeitung dient als Grundlage, um für die Analyse ein Tabelle mit Kennzahlen pro Mannschaft und Spiel zu bilden.

Die aggregierte Analyse-Tabelle soll folgenden Aufbau haben:

  • Mannschaft

  • Spielwoche

  • Ausgang: Sieg/Niederlage/Unentschieden

  • Durchschnittlicher Sentiment Score

  • Anzahl Kommentare mit positiven Sentiment

  • Anzahl Kommentare mit negativen Sentiment

  • Anzahl Kommentare mit neutralen Sentiment

Zum Zählen der Sentimentwerte wurde einen Funktion geschrieben, um Fehler zu vermeiden falls eine Kategorie (positive, negative, neutral) nicht vorkommt.

def count_sentiment(inp_series, sentiment_value): 
    try: 
        count = inp_series.value_counts()[sentiment_value]
    except: 
        count = 0
    return count

Die Aggragtion erfolgt über die Spalten “team_by_entity” und “week” mit den Funktionen:

  • first, zum Bestimmen des Ausgangs

  • mean, zur Bildung des durchschnittlichen Sentiment Scores

  • count_sentiment, zum Zählen der positiven, negativen und neutralen Kommentare

summary= df0.groupby(['team_by_entity','week']).agg(
                                   outcome=('win_for_entity', 'first'),
                                   avg_score=('score', 'mean'),
                                   count_neg=('sentiment', lambda x: count_sentiment(x, 'negative')),
                                   count_pos    =('sentiment',  lambda x: count_sentiment(x, 'positive')),
                                   count_neutral=('sentiment', lambda x: count_sentiment(x, 'neutral')))
summary = summary.reset_index()

Abschließen wird die Spielwoche in einen numerischen Wert umgewandelt.

summary['week'] = summary['week'].apply(lambda x: int(x.split()[1]))
summary = summary.sort_values('week')
summary
team_by_entity week outcome avg_score count_neg count_pos count_neutral
0 49ers 1 win 0.046951 29 47 85
68 Broncos 1 win 0.232381 14 117 77
85 Browns 1 defeat 0.128121 125 359 386
102 Buccaneers 1 win 0.084448 87 178 281
119 Cardinals 1 win 0.193962 20 119 122
... ... ... ... ... ... ... ...
42 Bengals 18 defeat 0.017647 3 4 10
364 Panthers 18 defeat 0.325000 0 2 2
25 Bears 18 defeat -0.125000 2 1 5
467 Seahawks 18 win 0.079167 1 7 15
177 Colts 18 defeat -0.042105 7 7 5

543 rows × 7 columns

Das Ergebniss wird in einer CSV-Datei gespeichert und im anschließenden Notebook zur Datenanalyse genutzt.

summary.to_csv("summary_2021.csv")