Python setzt für alles, was Natural Language Processing (NLP) und die Verarbeitung von Text angeht, den Gold-Standard.1 Es gibt für Python zahlreiche mächtige Module, die den Umgang mit Daten enorm vereinfachen. Das gilt für strukturierte, also für Computer besonders leicht zu verarbeitende Daten, wie für unstrukturierte. Die Sorte von Daten, mit denen es Historiker meist zutun haben, sind Textdaten. Sie gelten als unstrukturiert, weil ein Computer zunächst die einzelnen Zeichen, aus denen ein Text besteht, in Zahlen übersetzen muss, um mit ihnen arbeiten zu können. Auch hier stellt Python großartige Hilfsmittel zur Verfügung. Eines davon möchte ich in diesem Blog vorstellen: wordcloud
.
Wortwolken sind verführerisch.2 Sie verbinden für einen Text anscheinend einschlägige Wörter mit einer ästhetisch ansprechenden Darstellung. Oftmals nutzen sie unterschiedliche Farben und Größen. Mitunter verteilen sie die Wörter geschickt, um sie möglichst passgenau in eine vorgegebene Form zu integrieren. Diese Kombination aus semantischem Gehalt, also der Bedeutung der einzelnen Wörter, und ihrer facettenreichen Darstellungsästhetik birgt die Gefahr, Wortwolken misszuverstehen.
Für gewöhnlich folgen Wortwolken einigen einfachen Grundsätzen:
Aus alldem folgt: Wortwolken suggerieren zwar einen semantischen Zusammenhang zwischen ihrer Form und dem Inhalt des Textes, aus dem sie errechnet wurden. Wenn aber Algorithmen sie erstellt haben, geben sie genau genommen nur Auskunft über Worthäufigkeiten. Sie treffen keinerlei Aussagen darüber, worum es im Text inhaltlich geht. Denn die Bedeutung eines Textes erschließt sich nicht zuletzt darin, was er gerade nicht explizit macht.
Ein Beispiel dafür ist Kleists Nouvelle Die Marquise von O. Eine Wortwolke aus dem Text, den das Projekt Gutenberg zur Verfügung stellt, zeigt besonders prominent die Worte ‘Marquise’, ‘Mutter’, ‘Graf’, ‘Kommandant’, ‘Frau’ sowie zahlreiche Verben des Sagens. Dass der Text eigentlich von einer Vergewaltigung handelt, die er zwar nie ausdrücklich anspricht, sondern nur andeutet, geht aus der Wortwolke nicht hervor.
Nichtsdestoweniger erhält man durch die Wortwolke einen Eindruck vom Text. Man erfährt, um welche Personen er sich dreht, dass er von einer Familienangelegenheit handelt und dass die Kommunikation der Protagonisten von zentraler Bedeutung für das geschilderte Geschehen ist.
Hinsichtlich des Aussagewerts von Wortwolken ist also Vorsicht angebracht. Sie können zwar durchaus Auskunft über den Text geben. Aber sie bieten keine Interpretation des Inhalts, sondern machen quantifizierbare Merkmale sichtbar, die wiederum interpretiert werden müssen.
Wortwolken lassen sich auf vielfältige Weise erstellen. Beinahe jede moderne Programmiersprache bietet dazu Bibliotheken an. In Python ist es besonders einfach.
Als erstes bindet man die notwendigen Module ein. Wir verwenden wordcloud
von Andreas Mueller, um die nötigen Berechnungen vorzunehmen. Mit matplotlib
stellen wir die Wortwolke dar. Das Modul wordcloud
bietet auch eine Reihe von englischen Stopwords an. Möchte man deutsche Stopwords nutzen, empfehle ich das Module stop-words
von Alireza Savand. Da ich für diesen Beitrag die Wortwolke in einer Maske erstellen möchte, benötigen wir außerdem noch pillow
und numpy
.
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from wordcloud import WordCloud, STOPWORDS
Anschließend öffne ich die Text-Datei, aus der ich eine Wortwolke genieren möchte. Ich entscheide mich diesmal für Jane Asutens Pride and Prejudice, das auch über das Gutenberg-Projekt geladen werden kann. Als Maske nehme ich einen Scheerenschnitt von Austen, der über Wikipedia zu finden ist. Bevor ich die Grafik allerdings importieren kann, muss ich sie noch in das JPG-Format exportieren. Dabei hilft mir Gimp. Ich lade die Grafik und wandele sie mithilfe von numpy
in einem Schritt in einen Array um. Außerdem ergänze ich die englischen Stopwords um die Wörter ‘said’, ‘Mr’ und ‘Mrs’ und erstelle einen Verweis auf die Variable blacklist
.
with open('data/Auten_Pride_and_Prejudice.txt', 'r') as f:
text = f.read()
austen_mask = np.array(Image.open('data/JaneAustenSilhouette.jp.'))
blacklist = STOPWORDS.union({'said', 'Mr', 'Mrs'})
Jetzt bleibt nur noch, eine Instanz der Wortwolke zu erstellen. Ich möchte einen weißen Hintergrund, nutze austen_mask
als Maske und blacklist
als Stopwords. Danach übergebe ich der Instanz den eingelesenen Text, erstelle ein Bild von der Wortwolke, aber ohne unnötige Achsen und lasse es mir anzeigen.
wc = WordCloud(background_color='white', mask=austen_mask, stopwords=blacklist)
wc.generate(text)
plt.imshow(wc, interpolation="bilinear")
plt.axis('off')
plt.show()
Als Ergebnis erhalte ich dann die folgende Wortwolke:
Auch in diesem Fall sind die Namen der Protagonisten deutlich zu erkennen. Im Gegensatz zu Kleist Novelle treten Wörter wie ‘think’, ‘know’, ‘wish’ und ‘hope’, aber auch ‘shall’, ‘must’, ‘may’, ‘might’ und dergleichen in den Vordergrund. Die Häufigkeit dieser Wörter legt also die Vermutung nahe, bei Austens Text gehe es nicht zuletzt um die Reflexion gesellschaftlicher Konventionen und Zwänge. Ein Befund, der nicht überrascht.
Ein Jupyter Notebook mit dem kompletten Code ist auf Gisthub hinterlegt.
Ein hervorragender Einstieg in das Thema NLP und Python bieten das Natural Language Tool Kit (NLTK) und das Buch von Sarkar, Dipanjan: Text Analytics with Python. A Practical Real-World Approach to Gaining Actionable Insights from your Data, New York: Apress 2016. ↩