engender - Das Vokabular weiblicher und männlicher Autoren englischer Romane des 19. Jahrhunderts im Vergleich

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.

0. Einleitung

Das Thema dieser Lerneinheit

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.

Ziele der Lerneinheit

  • Erstens: An einem konkreten Beispiel selbst zu prüfen, ob weibliche und männliche RomanautorInnen in ihrem Sprachgebrauch unterscheiden.
  • Zweitens: Ein Verständnis dafür zu entwickeln, dass die Häufigkeiten von Wortformen oder Lemmata in den verschiedenen Texten einer Textsammlung als Verteilung verstanden werden können.
  • Drittens: Zu verstehen, welche Möglichkeiten dies eröffnet: Solche Verteilungen können visualisiert werden, ihre zentralen Eigenschaften können bestimmt werden, und sie können mit geeigneten statistischen Tests verglichen werden.
  • Viertens: Es geht darum, an konkreten Beispielen den Zusammenhang zwischen der Bedeutung eines Wortes, der Visualisierung seiner Verteilung und den statistischen Kennwerten zu erfahren.

Einige Hinweise zu den Jupyter Notebooks

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.

  • Eine Code-Zelle wird ausgeführt, indem man Strg+Enter drückt.
  • Die Code-Zellen bauen aufeinander auf, d.h. sie müssen nacheinander ausgeführt werden.
  • Um alle Code-Zellen ab einer bestimmten Stelle nacheinander auszuführen, klickt man im Menü auf "Cell", dann auf "Run All Below".
  • Um noch einmal neu zu beginnen und alle Änderungen zu löschen, klickt man im Menü auf "Cell", dann ganz unten auf "All Output" und dann auf "Clear".

1. Import statements

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

2. Start: Laden der Daten

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?)

Daten laden

Der folgende Code lädt die Datentabelle und zeigt einen kleinen Ausschnitt daraus an.

Tatsächlich ist die Tabelle natürlich viel größer. Sie hat 120 Zeilen, eine für jeden Roman, und je eine Spalte pro Suchwort.


In [ ]:
AlleDaten = "AlleDaten.csv"
with open(AlleDaten, "r") as infile: 
    AlleDaten = pd.DataFrame.from_csv(infile, sep=",")
    print(AlleDaten.iloc[0:5,0:5])

Genauerer Blick in die Daten

Um genau die Daten auswählen zu können, die uns interessieren, müssen wir wissen, für welche Suchbegriffe Daten vorliegen. Der folgende Code ruft die Liste der enthaltenen Suchbegriffe ab:


In [ ]:
Suchbegriffe = AlleDaten.columns.values[1:]
print(Suchbegriffe)

Aufteilung der Daten in zwei Gruppen

Wie Sie oben vielleicht schon gesehen haben liegt in der Datentabelle für jeden Roman außer dem Kürzel auch die Information vor, ob er von einem Mann oder einer Frau geschrieben wurde. Diese Information können wir nutzen, um die Daten in zwei Teile aufzuteilen.


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])

3. Auswählen eine Suchanfrage

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)))

4. Visualisierung als Verteilungen

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.

1. Boxplots


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()

2. Histogramm mit Dichteschätzung


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()

5. Statistische Eigenschaften der Verteilungen

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.

1. Mittelwerte


In [ ]:
MittelW = np.mean(WerteW)
MittelM = np.mean(WerteM)
print("\nMittelwert W:", MittelW, "\nMittelwert M:", MittelM)

2. Mediane


In [ ]:
MedianW = np.median(WerteW)
MedianM = np.median(WerteM)
print("\nMedian W:", MedianW)
print("Median M:", MedianM)

3. Standardabweichung


In [ ]:
StandardabweichungW = np.std(WerteW)
StandardabweichungM = np.std(WerteM)
print("\nStandardabweichung W:", StandardabweichungW)
print("Standardabweichung M:", StandardabweichungM)

6. Statistische Tests für den Unterschied der Verteilungen

1. Vorüberlegungen

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.

2. Der Wilcoxon Rank-Sum Test

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?

Notizen

Das folgende Textfeld können Sie nutzen, um sich Notizen zu machen.

[Mit Doppelklick aktivieren, mit Strg+Enter deaktivieren]