Dieses erste Kapitel liefert einen Überblick über zahlreiche Grundfunktionen des Pakets Quanteda, die gleichzeitig die Basis der automatisierten Inhaltsanalyse mit R bilden. Über Quanteda hinaus werden im Verlauf dieser neunteiligen Einführung noch eine Reihe weiterer R-Bibliotheken verwendet, etwa für das überwachte maschinelle Lernen (Kapitel 5) und das Berechnen von Themenmodellen (Kapitel 6). In praktisch jeder Einheit relevant sind dabei die Pakete des tidyverse (vor allem ggplot, dplyr und stringr), durch die zahlreiche Funktionen wie Plotten, Textverarbeitung und Datenmanagement gegenüber den R-Basisfunktionen stark verbessert werden. Pakete für einzelne Teilbereiche, die erst später eine Rolle spielen werden, sind u.a. topicmodels und stm (Themenmodelle), RTextTools und keras (überwachtes maschinelles Lernen), spacyr und udpipe (POS-Tagging und Named-Entity-Erkennung) sowie googlenlp bzw. googleLanguageR (weitere Annotations- und Übersetzungfunktionen).

Die Basis der Analyse in diesem ersten Kapitel bildet einerseits eine Sammlung einfacher Beispielsätze, anhand derer sich die Grundfunktionen von Quanteda gut erläutern lassen, und andererseits die beliebten Detektivgeschichten von Sherlock Holmes. Das Sherlock Holmes-Korpus besteht aus zwölf Erzählungen, die in dem 1892 erschienenem Band The Adventures of Sherlock Holmes zusammengefasst sind, und die man gemeinfrei unter anderem durch das Internet Archive herunterladen kann. Die für diese Einführung verwendete Fassung wurde zunächst dem Internet Archive entnommen und dann in zwölf Einzeldateien aufgeteilt. Natürlich können die vorgestellten Methoden auf die anderen hier behandelten Korpora angewandt werden – das Beispiel dient nur dazu, sich langsam an quanteda und die Grundlagen der computergestützen Inhaltsanalyse zu gewöhnen ohne das dabei bestimmte Vorkenntnisse vorausgesetzt werden.

Sämtliche in dieser Einführung verwendeter Codebeispiele, Korpora und Lexika können hier heruntergeladen werden.

Installation und Laden der benötigten R-Bibliotheken

Zunächst werden die notwendigen Bibliotheken installiert (sofern noch nicht vorhanden) und anschließend geladen. Zudem wird vorbereitend die Theme-Einstellung für das Paket ggplot gesetzt (dies sorgt für hübschere Plots). Diesen Schritt wiederholen wir zu Beginn jedes Kapitels, daher wird auf ihn später nicht mehr weiter eingegangen. In einigen Kapiteln werden noch weiteren Pakete gelanden, etwa für eine erweiterte Farbpalette (RColorBrewer), Wortwolken (wordcloud) oder um URLs zu parsen (urltools).

if(!require("quanteda")) {install.packages("quanteda"); library("quanteda")}
if(!require("readtext")) {install.packages("readtext"); library("readtext")}
if(!require("tidyverse")) {install.packages("tidyverse"); library("tidyverse")}
if(!require("RColorBrewer")) {install.packages("RColorBrewer"); library("RColorBrewer")}
theme_set(theme_minimal())

Erste Gehversuche mit Quanteda

Wenn alle notwendigen Pakete erfolgreich geladen wurden, können wir einen ersten Gehversuch mit Quanteda unternehmen. Typischerweise wird ein Korpus (also eine Sammlung von Texten und zugehörigen Metadaten) erstellt, indem Dateien von der Festplatte oder aus dem Internet eingelesen werden, die dann den Korpusinhalt bilden. Im folgenden ersten Schritt halten wir es noch etwas einfacher und arbeiten mit einem synthethischen Beispiel. Dazu lesen wir lediglich drei Beispielsätze ein in R ein und erstellen daraus einen Korpus.

beispielsaetze <- c("Ein Hund kam in die Küche",
                    "In der Küche gibt es noch Kaffee",
                    "Im Kaffee fehlt noch die Milch")

Das Objekt beispielsaetze enthält aus Sicht von Quanteda drei Texte, aus denen sich mit der gleichnamigen Funktion problemlos ein Objekt vom Typ corpus erstellen lässt.

beispielkorpus <- corpus(beispielsaetze)
beispielkorpus
Corpus consisting of 3 documents.
text1 :
"Ein Hund kam in die Küche"

text2 :
"In der Küche gibt es noch Kaffee"

text3 :
"Im Kaffee fehlt noch die Milch"

Die Meldung bestätigt uns genau das – wir haben erfolgreich einen Korpus aus drei Texten erstellt. Derzeit enthält unser Korpus noch keinerlei Metadaten, also Angaben zu den im Korpus enthaltenen Texten. In der Terminologie von Quanteda werden Metadaten auch als Dokument-Variablen (docvars) bezeichnet.

Die in einem Korpus enthaltenen Texte lassen sich jedenzeit mit dem Befehl texts ausgeben.

texts(beispielkorpus)
                             text1                              text2 
       "Ein Hund kam in die Küche" "In der Küche gibt es noch Kaffee" 
                             text3 
  "Im Kaffee fehlt noch die Milch" 

Nun fügen wir unserem Korpus mit Hilfe des Befehls docvars eine Dokument-Variable hinzu. Sinn und Zweck dieser Variablen ist die (später noch wichtige) Zuordnung von Metadaten zu dem erfassten Textmaterial.

docvars(beispielkorpus, "Text_Autor") <- c("Paul", "Marie", "Paul")

Wieso ist die Erstellung eines Korpus und das Anlegen von Dokument-Variablen sinnvoll? Eine Zusammenfassung unseres Beispielkorpus gibt einen ersten Aufschluss.

summary(beispielkorpus)
Corpus consisting of 3 documents, showing 3 documents:

  Text Types Tokens Sentences Text_Autor
 text1     6      6         1       Paul
 text2     7      7         1      Marie
 text3     6      6         1       Paul

Die Zusammenfassung zeigt die im Korpus enthaltenden Texte, die jeweils einen einmaligen Bezeichner erhalten (text1, text2, text3). Weiterhin gibt sie Aufschluss über die Anzahl der Tokens (laufende Wörter), die Anzahl der Types (einmalige Wörter), und die Anzahl der Sätze pro Text. Schließlich wird noch die gerade von uns eingeführte Dokument-Variable Text_Autor widergegeben.

Auch wenn das zunächst nicht weiter wichtig erscheinen mag: Die Möglichkeit, eine große Anzahl von Texten aus unterschiedlichen Quellen zu einem Korpus-Objekt zusammenzufassen und dieses anschließend mit Metadaten zu versehen, ist ein ganz entscheidender Vorteil von Quanteda. Wir werden folgend mit deutlich größeren Korpora arbeiten, die alle nach diesem Prinzip erstellt wurden.

Einlesen der Daten und Anlegen des Sherlock Holmes-Korpus

Nachdem wir einen ersten Einblick in die Erstellung eines sehr simplen Quanteda-Korpus erhalten haben, können wir nun die Sherlock Holmes-Romane einlesen und daraus ebenfalls einen Korpus erstellen, nun aber in einer realistischen Größe.

Für das Einlesen der Plaintext-Dateien wird die Funktion readtext aus dem gleichnamigen Paket verwendet, durch die sich eine Reihe von Dateiformaten erfolgreich importieren lassen (u.a. TXT, PDF und Word). Grundsätzlich sich Plaintext–Daten (i.d.R. mit der Endung “.txt”) und Daten in Tabellenform (etwa im Format CSV oder auch als Excel–Datei) für readtext ohne größere Probleme lesbar, allerdings muss man beim Einlesen erklären, wie genau die einzelnen Datensätze von einander getrennt sind (bei Plaintext–Dateien wo nicht 1 Datei == 1 Text, was etwa bei Exporten aus Lexis Nexis der Fall sein kann), bzw. welche Felder die Primär– und welche Metadaten beinhalten (bei Tabellen). Eine gute Einführung zum Paket readtext findet sich hier.

daten.sherlock <- readtext("daten/sherlock/romane/*.txt") 
daten.sherlock$doc_id <- str_sub(daten.sherlock$doc_id, start = 4, end = -5)

In diesem Fall entspricht jede Datei einem Text (nicht wie zuvor, einen einzigen Satz), wodurch der Import sehr umkompliziert ausfällt. Zunächst laden wir nur Dateien mit der Endung “.txt” aus dem Verzeichnis daten/sherlock/romane. Dann ziehen wir die Namen der Romane aus den Dateinamen, um diese später in Plot–Beschriftungen verwenden zu können.

korpus <- corpus(daten.sherlock, docid_field = "doc_id") 
docvars(korpus, "Textnummer") <- 1:12
korpus
Corpus consisting of 12 documents and 1 docvar.
A Scandal in Bohemia :
"A Scandal in Bohemia   To Sherlock Holmes she is always the ..."

The Red-headed League :
"The Red-headed League   I had called upon my friend, Mr. She..."

A Case of Identity :
"A Case of Identity   "My dear fellow." said Sherlock Holmes ..."

The Boscombe Valley Mystery :
"The Boscombe Valley Mystery   We were seated at breakfast on..."

The Five Orange Pips :
"The Five Orange Pips   When I glance over my notes and recor..."

The Man with the Twisted Lip :
"The Man with the Twisted Lip   Isa Whitney, brother of the l..."

[ reached max_ndoc ... 6 more documents ]

Nun generieren wir wieder mit dem Befehl corpus ein Quanteda-Korpus-Objekt. Im Gegensatz zu unserem Beispielkorpus bilden in diesem Fall die mittels readtext zuvor eingelesenen Datein die Grundlage. Die Funktion corpus versteht mehrere Datenformate, also Character-Vektoren (unser erstes Beispiel) ebenso wie Data Frames (wie mittels readtext erstellt) oder Objekte aus dem Paket tm. Zweitens wird eine Dokument-Variable Textnummer generiert, die wir ebenfalls später noch gebrauchen können. Zum Schluss wird die Variable korpus aufgerufen, was uns die wichtigen Eckdaten zum Korpus sowie Metadaten zu den enthaltenden Texten zurückliefert.

In den folgenden Abschnitten werden häufig bereits vorbereitete Korpora geladen, d.h. der Befehl corpus wird hier nicht mehr explizit ausgeführt. Er ist aber im Vorfeld ausgeführt worden, um aus Textdatein auf der Festplatte oder Twitter-Daten in einem R-Data Frame einen Quanteda-Korpus zu erstellen.

Die Funktionen ndoc, ntoken, ntype und nsentence geben die Anzahl der Dokumente, Tokens, Types und Sätze aus. Diese Statistiken können bequem gemeinsam mit Metadaten auf Dokumentebene durch die Funktion summary erstellt werden. Bei den meisten Korpora, die hier verwendet werden, liegt ein solcher Data Frame mit Statistiken zu jedem Text bereits bei. Notwendig ist dies allerdings nicht. Will man auf Korpus–Metadaten zurückgreifen oder diese verändern, kann man dies jederzeit über den Befehl docvars tun.

korpus.stats <- summary(korpus)
korpus.stats$Text <- reorder(korpus.stats$Text, 1:12, order = T)
korpus.stats
Corpus consisting of 12 documents, showing 12 documents:

                                  Text Types Tokens Sentences Textnummer
                  A Scandal in Bohemia  2153  10475       674          1
                 The Red-headed League  2091  11073       577          2
                    A Case of Identity  1758   8456       399          3
           The Boscombe Valley Mystery  2103  11453       636          4
                  The Five Orange Pips  1929   8833       476          5
          The Man with the Twisted Lip  2178  11109       616          6
   The Adventure of the Blue Carbuncle  1930   9611       553          7
    The Adventure of the Speckled Band  2243  11737       634          8
 The Adventure of the Engineer's Thumb  1976   9960       512          9
   The Adventure of the Noble Bachelor  1950   9930       580         10
    The Adventure of the Beryl Coronet  1997  11620       626         11
   The Adventure of the Copper Beeches  2119  11962       621         12

Manchmal ist es bei der Erstellung von Korpus-Zusammenfassungen sinnvoll, das optionale Funktionsargument n = 1000000 zu verwenden, weil die Funktion summary ansonsten nur maximal 100 Texte zusammenfasst. In diesem Fall reicht das zwar aus, aber bei größeren Datensätzen ist diese Beschränkung eher unpraktisch. Technisch gesehen heißt diese Funktion summary.corpus und ist eine an Korpus-Objekte angepasste Variante der Basisfunktion summary, die auch sonst in R verwendet wird. Der Befehl reorder wird verwendet, um die Texte auch in Plots nach ihrer Reihenfolge in The Adventures of Sherlock Holmes zu sortieren, statt alphabetisch nach Titel.

Der Inhalt der Variable korpus.stats kann natürlich auch geplottet werden, um einen anschaulichen Eindruck von der Korpusbeschaffenheit zu geben. Die folgenden Zeilen liefern die Anzahl der Tokens (laufende Wörter), die Anzahl der Types (einmalige Wörter), und Sätze pro Roman zurück (vgl. dazu diese Einführung). Schließlich wird noch das Verhältnis von Typen zu Tokens (oder die sog. Typ-Token-Relation) geplottet.

Grundlage solcher Plots sind praktisch immer Data Frame-Objekte (also Tabellen), die Informationen über Korpora, Texte, Wörter, Themen usw. enthalten, welche sich visuell darstellen lassen. Im Rest dieser Einführung gehe ich nicht im Detail darauf ein, wie die jeweiligen Plots genau konstruiert werden, allerdings lassen sich die meisten Daten auch (etwas weniger ansprechend) mit der R-internen Funktion plot darstellen. Eine hilfreiche deutschsprachige Einführung in das Plotten mit ggplot2 findet sich hier. Viele der hier vorgestellten Plots stammen zudem direkt aus quanteda (beginnend mit textplot_).

ggplot(korpus.stats, aes(Text, Tokens, group = 1)) + 
  geom_line() + 
  geom_point() + 
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
  ggtitle("Tokens pro Roman")

ggplot(korpus.stats, aes(Text, Types, group = 1)) + 
  geom_line() + geom_point() + 
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
  ggtitle("Types pro Roman")