Dieses Jupyter Notebook erläutert einige Aspekte des Vergleichs von (Sub-)Korpora am Beispiel einer Sammlung von Romanen, die von weiblichen und männlichen englischen Autoren des 19. Jahrhunderts geschrieben wurden.
Das Notebook ist live unter http://mybinder.org/repo/christofs/jupyter und wurde im Juni 2016 von Christof Schöch erstellt.
Hintergrund zu dieser Übung ist der Vortrag "Spitzer über Racine, revisited". Dort wurde unter anderem die Häufigkeit von lexikalischen Mustern in den Tragödien Racines und in Tragödien zeitgenössischer Autoren verglichen.
Ganz ähnlich werden hier nun die Häufigkeiten von Lemmata in den Romanen weiblicher und männlicher englischer AutorInnen des 19. Jahrhunderts verglichen.
Die verwendete Textsammlung besteht aus 70 Romanen, von denen 35 von Autorinnen und 35 von Autoren verfasst wurden. Einige besonders lange Romane sind in mehrere Teile aufgeteilt worden, sodass insgesamt 120 Dokumente vorhanden sind. Die Textsammlung umfasst etwa 16.7 Millionen Tokens.
Das vorliegende Dokument ist ein Jupyter Notebook für Python. Es besteht aus Textfeldern und Code-Feldern. Die Code-Felder sind interaktiv, d.h. der enthaltene Code kann ausgeführt werden. Es können auch Veränderungen vorgenommen werden, die sich dann auf das Ergebnis auswirken.
Bevor es richtig losgehen kann, müssen die folgenden Erweiterungen für Python importiert (d.h., aktiviert) werden. Versuchen Sie gleich einmal, die erste Code-Zelle auszuführen.
In [ ]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
import seaborn as sns
import random
%matplotlib inline
Die für die folgenden Funktionen notwendigen Daten liegen bereits in einer Tabelle vor. Die Tabelle enthält für verschiedene Suchbegriffe die relativen Häufigkeiten in allen Texten der Sammlung.
Jede Zeile der Tabelle steht für einen Roman. Jede Spalte der Tabelle steht für einen Suchbegriff. In jeder Spalte steht die relative Häufigkeit eines Suchbegriffs für einen Roman. (Die Werte sind hier definiert als die absolute Häufigkeit geteilt durch die Textlänge mal 1000. Ein Wert beantwortet also die Frage: Wie oft kommt das Wort pro 1000 Tokens im Text vor?)
In [ ]:
AlleDaten = "AlleDaten.csv"
with open(AlleDaten, "r") as infile:
AlleDaten = pd.DataFrame.from_csv(infile, sep=",")
print(AlleDaten.iloc[0:5,0:5])
In [ ]:
Suchbegriffe = AlleDaten.columns.values[1:]
print(Suchbegriffe)
In [ ]:
GrpDaten = AlleDaten.groupby("gender")
DatenW = GrpDaten.get_group("w")
DatenM = GrpDaten.get_group("m")
print("\nDatenW\n", DatenW.iloc[0:5,0:5])
print("\nDatenM\n", DatenM.iloc[0:5,0:5])
Auf der Grundlage der vorliegenden Daten können wir nun die relativen Häufigkeiten für einen Begriff und für die weiblichen bzw. die männlichen Autoren aus der Tabelle extrahieren.
Im folgenden Code-Feld muss ein Suchbegriff eingetragen werden. Bilden Sie zu einem der Suchbegriffe eine Hypothese und notieren Sie diese in der folgenden Code-Zelle. Dann machen Sie mit der Übung weiter.
In [ ]:
# Tragen Sie hier den Suchbegriff ein, zu dem Sie eine Hypothese haben.
Suchbegriff = ""
# Schreiben Sie hier in knapper Form Ihre Hypothese auf.
Hypothese = ""
print("\nMeine Hypothese zu "+Suchbegriff+": "+Hypothese)
Jetzt nutzen wir die beiden Datentabellen und den Suchbegriff, um die entsprechenden Werte zu extrahieren.
In [ ]:
WerteW = DatenW.loc[:,Suchbegriff]
WerteM = DatenM.loc[:,Suchbegriff]
print("\nWerteW\n", sorted(list(WerteW)))
print("\nWerteM\n", sorted(list(WerteM)))
Die obigen Listen der Häufigkeiten sind, vorsichtig ausgedrückt, etwas unübersichtlich. Bestenfalls kann man, wenn man die Listen sortiert, die Spannbreite der Werte ablesen.
Zunächst visualisieren wir daher die beiden Verteilungen gemeinsam, um einen ersten Eindruck ihres Verhältnisses zueinander zu bekommen. Hierfür bieten sich sowohl Boxplots als auch Histogramme an.
In [ ]:
plt.figure(figsize=(10,5))
plt.boxplot([WerteW, WerteM], vert=False, patch_artist=True)
plt.title("Boxplot für: "+str(Suchbegriff))
plt.xlabel("Häufigkeiten pro 1000 Tokens")
plt.yticks([1, 2], ['W', 'M'])
plt.show()
In [ ]:
plt.figure(figsize=(10,5))
sns.distplot(WerteM, kde=True, bins=16, label="M").set(xlim=0)
sns.distplot(WerteW, kde=True, bins=16, label="W").set(xlim=0)
plt.title("Histogramm für: "+str(Suchbegriff))
plt.xlabel("Häufigkeiten pro 1000 Tokens")
plt.ylabel("Anteil der Romane")
plt.legend()
plt.show()
Verteilungen kann man durch bestimmte Kennwerte beschreiben. Für sehr viele Verteilungen sind der Mittelwert, der Median und die Standardabweichung wichtige Kennwerte.
Das ist auch für die hier vorliegenden Verteilungen nützlich. Allerdings sind sie durch diese Kennwerte nicht vollständig beschrieben.
In [ ]:
MittelW = np.mean(WerteW)
MittelM = np.mean(WerteM)
print("\nMittelwert W:", MittelW, "\nMittelwert M:", MittelM)
In [ ]:
MedianW = np.median(WerteW)
MedianM = np.median(WerteM)
print("\nMedian W:", MedianW)
print("Median M:", MedianM)
In [ ]:
StandardabweichungW = np.std(WerteW)
StandardabweichungM = np.std(WerteM)
print("\nStandardabweichung W:", StandardabweichungW)
print("Standardabweichung M:", StandardabweichungM)
Die im letzten Abschnitt ermittelten Werte unterscheiden sich, aber wie stark? Um dies zu entscheiden, können wir statistische Tests anwenden. Damit prüfen wir, ob sich die Verteilungen signifikant unterscheiden oder nicht.
Welcher Test ist geeignet? Das ist fast die wichtigste Frage und ein Thema für sich. In unserem Fall gehen wir von folgenden Voraussetzungen aus: Wir wissen, dass die Daten unterschiedliche Mittelwerte und Varianzen haben. Und wir vermuten, dass die Daten nicht normalverteilt sind. In einem solchen Fall ist der Wilcoxon rank-sum Test ein angemessener Test.
Der Wilcoxon-Test ermittelt u.a. einen p-Wert. Dieser gibt an, wie groß die Wahrscheinlichkeit ist, dass die beiden getesteten Verteilungen eigentlich der gleichen Verteilung entsprechen. Je kleiner der p-Wert, desto sicherer können wir sein, dass wir einen signifikanten Unterschied in der Häufigkeit eines Wortes bei männlichen und weiblichen Romanautoren gefunden haben.
Dokumentation: http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.stats.wilcoxon.html
Siehe auch: https://de.wikipedia.org/wiki/Wilcoxon-Vorzeichen-Rang-Test
Merke: Nur wenn der Grenzwert von p = 0,05 unterschritten wird, können wir die Null-Hypothese ablehnen und davon ausgehen, dass der beobachtete Unterschied signifikant ist. (Aber Vorsicht: dieser Grenzwert ist arbiträr.)
In [ ]:
Wilcoxon = stats.wilcoxon(WerteW, WerteM)
print("\nWilcoxon p-Wert für", str(Suchbegriff),":", Wilcoxon[1])
if Wilcoxon[1] < 0.05:
print("\nDieser Wert bedeutet, dass der Unterschied SIGNIFIKANT ist.")
else:
print("\nDieser Wert bedeutet, dass der Unterschied NICHT signifikant ist.")
Entspricht das Ergebnis Ihren Erwartungen bzw. Ihrer oben formulierten Hypothese?
[Mit Doppelklick aktivieren, mit Strg+Enter deaktivieren]