quanteda
: Tokens and Document-Feature-MatrixIm letzten Tutorial haben wir das Einlesen von Textdokumenten, Dokumentenvariablen und das Erstellen eines Textkorpus besprochen. In diesem Tuorial besprechen wir zwei zentrale Aspekte der quantitativen Textanalyse: Tokens und Document-Feature-Matricen.
Damit wir dort beginnen wo das letzte Tutorium aufgehört hat, geht der folgende Code alle Schritte von letzter Woche im Schnelldurchlauf durch:
# Die notwendigen Pakete laden
library(tidyverse)
library(quanteda)
library(quanteda.textplots)
library(quanteda.textstats)
library(readtext)
library(ggplot2)
# Texte einlesen
daten_bverfg <- readtext("/Users/PhMeyer/Seafile/Seafile/Meine Bibliothek/Lehre/Seminare Uni Hannover/12 SoSe 2021/Quantitative Textanalyse/data/bverfg_15-19",
docvarsfrom = "filenames", docvarnames = c("date","docket_nr"))
# Meta-Daten erstellen und in der richtigen Form speichern
daten_bverfg$year <- substr(daten_bverfg$date, 1,4)
daten_bverfg$year <- as.numeric(daten_bverfg$year)
daten_bverfg$month <- substr(daten_bverfg$date, 5,6)
daten_bverfg$month <- as.numeric(daten_bverfg$month)
daten_bverfg$senat <- substr(daten_bverfg$docket_nr, 1,1)
daten_bverfg$senat <- as.numeric(daten_bverfg$senat)
daten_bverfg$proceeding <- substr(daten_bverfg$docket_nr, 2,4)
# Korpus erstellen
gerichts_korpus <- corpus(daten_bverfg,docid_field = "doc_id")
# Korpusdaten identifizieren
korpus_stats <- summary(gerichts_korpus, n = 2000)
# Korpusdaten zu unserem Entscheidungsdatensatz hinzufügen
daten_bverfg$types <- korpus_stats$Types
daten_bverfg$tokens <- korpus_stats$Tokens
daten_bverfg$sentences <- korpus_stats$Sentences
# Unnötige Elemente aus dem Environment löschen
rm(korpus_stats)
Die Funktion rm()
haben wir bisher noch nicht besprochen. Ihre einzige Funktion ist das Entfernen von Werten, Funktionen oder Datensätzen aus dem Environment (bei RStudio
das Fenster oben rechts). Wie immer: falls ihr mehr erfahren wollt, dann nutzt ?rm
oder googelt die Funktion.
Bei einem Satz, Absatz oder einem ganzen Textdokument besteht die Aufgabe der Tokenisierung darin, die Textsequenzen zu zerlegen und dabei Aspekte wie z. B. Satzzeichen oder Füllwörter zu entfernen. Wir können diese Sequenzen entweder in einzelne Wörter, in Wortpaare, in dreier Gruppen (n-grams) oder auch in ganze Sätze zerlegen.
Mit der Tokenisierung wird der Prozess beschrieben, bei dem wir Texte in von uns definierte Sequenzen von Wörtern aufspalten. Sequenzen mit mehr als einem Wort werden in der Textanalyse N-Gram
genannt (N ist hier ganz klassisch der Platzhalter für eine Zahl). In quanteda
können wir mit der tokens
-Funktion einen Korpus tokenisieren. Die Funktion beinhaltet weitere Argumente um bestimmte nicht brauchbare Textaspekte (wie Interpunktion, Leerzeichen (whitespace) oder Füllwörter) zu entfernen.
Wenden wir die tokens
-Funktion auf unseren Gerichtskorpus an und schauen uns danach die Tokens einer speziellen Entscheidung an:
entscheidungs_tokens <- tokens(gerichts_korpus)
head(entscheidungs_tokens[["20150108_2bvr241913.txt"]])
## [1] "BUNDESVERFASSUNGSGERICHT" "-"
## [3] "2" "BvR"
## [5] "2419" "/"
Das ist alles noch wenig aussagekräftig sind. Das können wir natürlich ändern... aber dazu später mehr.
Per default splittet die tokens
-Funktion die Texte in einzelne Wörter auf. In den meisten Fällen brauchen wir jedoch mehr als ein Wort um eine aussagekräftige Analyse zu realisieren. Über die Funktion tokens_ngrams
lassen sich Texte in N-Grams aufspalten. In den folgenden zwei Beispiele werden wir 1) Bigramme produzieren (zwei Wörter) und 2) alle Sequenzen von einem, zwei oder drei Begriffen extrahieren.
entscheidungs_tokens_ngrams <- tokens_ngrams(entscheidungs_tokens, n = 2)
head(entscheidungs_tokens_ngrams[["20150108_2bvr241913.txt"]])
## [1] "BUNDESVERFASSUNGSGERICHT_-" "-_2"
## [3] "2_BvR" "BvR_2419"
## [5] "2419_/" "/_13"
entscheidungs_tokens_ngrams <- tokens_ngrams(entscheidungs_tokens, n = 1:3)
head(entscheidungs_tokens[["20150108_2bvr241913.txt"]])
## [1] "BUNDESVERFASSUNGSGERICHT" "-"
## [3] "2" "BvR"
## [5] "2419" "/"
Bei der Tokenisierung können wir aber auch bestimmte Begriffe entfernen:
entscheidungs_tokens <- tokens_remove(entscheidungs_tokens, c("BUNDESVERFASSUNGSGERICHT", "BvR", "BvF", "Karlsruhe", "Richter"))
head(entscheidungs_tokens[["20150108_2bvr241913.txt"]])
## [1] "-" "2" "2419" "/" "13" "-"
Wir haben jetzt einzelne Wörter nach der Tokenisierung entfernt. Deutlich einfacher und auch üblicher ist es aber, bestimmte Wörter, Satzzeichen, Zahlen und Sonderzeichen während der Tokinisierung zu entfernen. Das Entfernen dieser Elemente ist ein zentraler Bestandteil einer jeden quantitativen Textanalyse. Durch dieses sogenannte preprocessing entfernen wir unnötige oder sinnlose Textelemente, ermitteln die Worthäufigkeit und brechen so die Syntax auf. Dadurch erstellen wir einen sogenannten "Bag of Words".
Im folgenden Code entfernen wir alle Zahlen (remove_numbers = TRUE), Sonderzeichen (remove_symbols = TRUE), Leerzeichen (removeSeparators = TRUE) und die Interpunktion (remove_punct = TRUE). Danach transformieren wir mittels tokens_tolower
alle Großbuchstaben in Kleinbuchstaben. In einem letzten Schritt nutzen wir tokens_remove
und entfernen die Wörter "bundesverfassungsgericht", "richter", "bvr","herrn","Entscheidung","art" (Art für Gesetzesartikel),"gg" (Grundgesetz), "karlsruhe" sowie alle weiteren gängigen deutschen Stopp-/Füllwörter (der, die, das, wer, wie, was...).
entscheidungs_tokens <- tokens(gerichts_korpus, remove_numbers = TRUE, remove_punct = TRUE, remove_symbols = TRUE, removeSeparators = TRUE)
entscheidungs_tokens <- tokens_tolower(entscheidungs_tokens)
entscheidungs_tokens <- tokens_remove(entscheidungs_tokens, stopwords("german"))
entscheidungs_tokens <- tokens_remove(entscheidungs_tokens, c("bundesverfassungsgericht", "richter", "bvr","herrn","Entscheidung","art","gg", "karlsruhe", "dass", "beschluss", "rn","satz","sei"))
head(entscheidungs_tokens[["20150108_2bvr241913.txt"]])
## [1] "verfahren" "verfassungsbeschwerde" "s"
## [4] "bevollmächtigter" "rechtsanwalt" "gerald"
Obwohl Tokens für einige Methoden wichtig sind, werden wir in den meisten Fällen mit Dokument-Feature-Matrizen arbeiten. Die Tokenisierung wird implizit angewandt, sobald wir eine Dokument-Feature-Matrize (DFM, s.u.) erstellen.
Das Standardformat für die Darstellung eines Bag-of-Words ist eine Dokument-Feature-Matrix (DFM). DFMs werden praktisch in jedem Textanalyseprojekt verwendet. Meist ist das Erstellen einer DFM der zweite Schritt, gleich nach dem Anlegen eines Korpus. DFMs sind Matrizen, deren Zeilen die Texte und deren Spalten die Worthäufigkeiten enhalten. Wir verwenden die dfm
-Funktion von quanteda
um eine DFM von unserem Gerichtskorpus zu erstellen. Dabei verwenden wir die selben Argumente, welche wir bereits bei der tokens
-Funktion genutzt haben:
court_stopswords <- c("bundesverfassungsgericht", "richter", "bvr","herrn","Entscheidung"
,"art","gg", "karlsruhe", "dass", "beschluss", "rn","satz","sei"
, "bverfge","verfassungsbeschwerde", "beschwerdeführer","beschwerdeführerin") # hier legen wir eine list von wörter an, welche wir von vornherein entfernt haben wollen
entscheidungs_tokens <- tokens(gerichts_korpus, remove_punct=T
, remove_symbols = TRUE, removeSeparators = TRUE) %>%
tokens_remove(pattern = c(court_stopswords,stopwords("german"))) %>%
tokens_tolower()
entscheidungs_dfm <- dfm(entscheidungs_tokens)
entscheidungs_dfm
## Document-feature matrix of: 1,616 documents, 99,104 features (99.37% sparse) and 6 docvars.
## features
## docs 2 2419 13 verfahren s bevollmächtigter rechtsanwalt
## 20150108_2bvr241913.txt 11 1 8 1 1 1 1
## 20150112_2bvq005314.txt 7 0 0 1 1 0 0
## 20150113_1bvr047214.txt 1 0 3 1 1 0 0
## 20150113_1bvr332314.txt 5 0 1 2 2 0 0
## 20150113_2bve000113.txt 12 0 5 3 0 1 0
## 20150113_2bvr239514.txt 19 0 2 4 1 0 0
## features
## docs gerald goecke hamburger
## 20150108_2bvr241913.txt 1 1 1
## 20150112_2bvq005314.txt 0 0 0
## 20150113_1bvr047214.txt 0 0 0
## 20150113_1bvr332314.txt 0 0 0
## 20150113_2bve000113.txt 0 0 0
## 20150113_2bvr239514.txt 0 0 0
## [ reached max_ndoc ... 1,610 more documents, reached max_nfeat ... 99,094 more features ]
Bei DFMs funktioniert einiges analog zu einem Textkorpus. Wir können die Funktionen ndoc
oder nfeat
anwenden:
ndoc(entscheidungs_dfm)
## [1] 1616
nfeat(entscheidungs_dfm)
## [1] 99104
Wir können die Namen der Dokumente und Features auslesen (hier nur die ersten (head
) bzw. die letzten (tail
) zehn Dokumente):
head(docnames(entscheidungs_dfm)) # Dokumentennamen
## [1] "20150108_2bvr241913.txt" "20150112_2bvq005314.txt"
## [3] "20150113_1bvr047214.txt" "20150113_1bvr332314.txt"
## [5] "20150113_2bve000113.txt" "20150113_2bvr239514.txt"
tail(featnames(entscheidungs_dfm)) # Features
## [1] "argentiniens" "comercial" "nación" "ley" "26.994"
## [6] "übergriffs"
Wollen wir einen Überblick über unsere DFM haben, dann können wir eine Tabelle erstellen. Hierbei wird uns auch direkt die sparsity unserer DFM angezeigt. Die sparsity beschreibt den Anteil der Nullen in einer DFM (Eine DFM misst die Haufigkeit eines Worts in den Korpusdokumenten). Anders gesagt, eine hohe sparsity bedeutet, dass die Texte viele genuine Wörter benützen, sodass dass viele der in den Texten vorkommenden Wörter nur in einigen wenigen Texten gleichzeitig vorkommen. Einfach ausgedrück: Bei einem Korpus von drei Texten kommt das Wort "Ball"" kommt nur in Text 1 vor, aber nicht in Text 2 und Text 3. In der DFM würden so für das Wort "Ball" als eine 1 (das Vorkommen in Text 1) und zwei Mal die 0 (aufgrund der Nichtexistenz des Wortes in den Texten 2 & 3) angezeigt.
entscheidungs_dfm # hier sehen wir, dass unsere DFM 1,616 Dokumente und 92,572 Features (i.d.R. Wörter, aber meist auch Sonderzeichen die quanteda nicht erkannt hat) hat und 99.4% sparse ist
## Document-feature matrix of: 1,616 documents, 99,104 features (99.37% sparse) and 6 docvars.
## features
## docs 2 2419 13 verfahren s bevollmächtigter rechtsanwalt
## 20150108_2bvr241913.txt 11 1 8 1 1 1 1
## 20150112_2bvq005314.txt 7 0 0 1 1 0 0
## 20150113_1bvr047214.txt 1 0 3 1 1 0 0
## 20150113_1bvr332314.txt 5 0 1 2 2 0 0
## 20150113_2bve000113.txt 12 0 5 3 0 1 0
## 20150113_2bvr239514.txt 19 0 2 4 1 0 0
## features
## docs gerald goecke hamburger
## 20150108_2bvr241913.txt 1 1 1
## 20150112_2bvq005314.txt 0 0 0
## 20150113_1bvr047214.txt 0 0 0
## 20150113_1bvr332314.txt 0 0 0
## 20150113_2bve000113.txt 0 0 0
## 20150113_2bvr239514.txt 0 0 0
## [ reached max_ndoc ... 1,610 more documents, reached max_nfeat ... 99,094 more features ]
# Für unsere Tabelle können wir z.B. nur 8 Dokumente und 20 Features auswählen und anzeigen lassen:
head(entscheidungs_dfm, n = 8, nf = 20)
## Document-feature matrix of: 8 documents, 99,104 features (99.45% sparse) and 6 docvars.
## features
## docs 2 2419 13 verfahren s bevollmächtigter rechtsanwalt
## 20150108_2bvr241913.txt 11 1 8 1 1 1 1
## 20150112_2bvq005314.txt 7 0 0 1 1 0 0
## 20150113_1bvr047214.txt 1 0 3 1 1 0 0
## 20150113_1bvr332314.txt 5 0 1 2 2 0 0
## 20150113_2bve000113.txt 12 0 5 3 0 1 0
## 20150113_2bvr239514.txt 19 0 2 4 1 0 0
## features
## docs gerald goecke hamburger
## 20150108_2bvr241913.txt 1 1 1
## 20150112_2bvq005314.txt 0 0 0
## 20150113_1bvr047214.txt 0 0 0
## 20150113_1bvr332314.txt 0 0 0
## 20150113_2bve000113.txt 0 0 0
## 20150113_2bvr239514.txt 0 0 0
## [ reached max_ndoc ... 2 more documents, reached max_nfeat ... 99,094 more features ]
Viele der 92,572 Features in der DFM sind nicht sehr informativ. Für Bag-of-Words-Analysen ist es vorteilhaft, diese Wörter zu entfernen. Das wird in den meisten Fällen die die Ergebnisse verbessern.
Wir können die Funktion dfm_trim
verwenden um viele der uninformativen Features zu entfernen. Im Beispiel unten entfernen wir alle Terme die eine Häufigkeit (d. h. der Summenwert der Spalte in der DFM) von unter 30% haben. Wir entfernen also alle Wörter die in weniger als 30% aller Texte auftauchen.
entscheidungs_dfm <- dfm_trim(entscheidungs_dfm, min_termfreq = 30) # mit "max_termfreq" könnten wir auch eine Obergrenze festlegen, aber das findet ihr am besten mit ?dfm_trim selber heraus
entscheidungs_dfm
## Document-feature matrix of: 1,616 documents, 8,318 features (94.24% sparse) and 6 docvars.
## features
## docs 2 13 verfahren s bevollmächtigter rechtsanwalt 75
## 20150108_2bvr241913.txt 11 8 1 1 1 1 1
## 20150112_2bvq005314.txt 7 0 1 1 0 0 0
## 20150113_1bvr047214.txt 1 3 1 1 0 0 0
## 20150113_1bvr332314.txt 5 1 2 2 0 0 0
## 20150113_2bve000113.txt 12 5 3 0 1 0 0
## 20150113_2bvr239514.txt 19 2 4 1 0 0 0
## features
## docs kiel a landgerichts
## 20150108_2bvr241913.txt 8 2 2
## 20150112_2bvq005314.txt 0 0 0
## 20150113_1bvr047214.txt 0 1 0
## 20150113_1bvr332314.txt 0 0 0
## 20150113_2bve000113.txt 0 0 0
## 20150113_2bvr239514.txt 0 0 1
## [ reached max_ndoc ... 1,610 more documents, reached max_nfeat ... 8,308 more features ]
quanteda
beinhaltet noch einige weitere Funktionen: topfeatures
zählt Features in der gesamten DFM und gibt uns die Top-10 Features. textstat_frequency
gibt uns die Worthäufigkeiten und ordnet die Features danach wie oft sie vorkommen (grundsätzlich ist textstat_frequency
zu bevorzugen).
topfeatures(entscheidungs_dfm)
## 1 2 abs vgl 3 4 s f 5 bverfgg
## 50731 38838 38071 20429 20428 10313 10001 9807 7701 6873
frequency <- textstat_frequency(entscheidungs_dfm)
head(frequency)
## feature frequency rank docfreq group
## 1 1 50731 1 1610 all
## 2 2 38838 2 1615 all
## 3 abs 38071 3 1520 all
## 4 vgl 20429 4 1436 all
## 5 3 20428 5 1553 all
## 6 4 10313 6 1441 all
Wie wir sehen, sind Texte von Verfassungsgerichten vielleicht nicht das beste Anschauungsmaterial ;) (sie benötigen noch viel mehr Aufarbeitung und vor allem viel mehr Stoppwort-Entferung (z.b. die juristischen Abkürzungen)).
Mit dfm_sort
können wir DFMs nach Dokument- und Feature-Frequenzen sortieren:
head(dfm_sort(entscheidungs_dfm, decreasing = TRUE, margin = "both"), n = 12, nf = 10)
## Document-feature matrix of: 12 documents, 8,318 features (64.78% sparse) and 6 docvars.
## features
## docs 1 2 abs vgl 3 4 s f 5 bverfgg
## 20170117_2bvb000113.txt 515 488 363 321 198 117 273 87 102 29
## 20190730_2bvr168514.txt 730 445 826 288 286 201 240 162 141 15
## 20180919_2bvf000115.txt 645 531 502 345 309 122 336 169 103 4
## 20160420_1bvr096609.txt 652 406 561 237 188 135 29 131 117 6
## 20161206_1bvr282111.txt 420 167 216 136 189 53 65 113 21 2
## 20171107_2bve000211.txt 255 154 107 164 103 73 45 86 55 12
## [ reached max_ndoc ... 6 more documents, reached max_nfeat ... 8,308 more features ]
Mit dfm_select
lassen sich Wörter gezielt auswählen:
dfm_select(entscheidungs_dfm, pattern = "euro*") # * gibt an, dass wir alle Wörter mit euro als Anfangssequenz suchen
## Document-feature matrix of: 1,616 documents, 10 features (94.04% sparse) and 6 docvars.
## features
## docs europa euro europäischen europäische
## 20150108_2bvr241913.txt 1 0 0 0
## 20150112_2bvq005314.txt 0 0 0 0
## 20150113_1bvr047214.txt 0 0 0 0
## 20150113_1bvr332314.txt 0 1 0 0
## 20150113_2bve000113.txt 2 0 3 0
## 20150113_2bvr239514.txt 0 0 0 0
## features
## docs europarechtlichen europäisches europäischer european
## 20150108_2bvr241913.txt 0 0 0 0
## 20150112_2bvq005314.txt 0 0 0 0
## 20150113_1bvr047214.txt 0 0 0 0
## 20150113_1bvr332314.txt 0 0 0 0
## 20150113_2bve000113.txt 0 0 0 0
## 20150113_2bvr239514.txt 0 0 0 0
## features
## docs eurosystem eurosystems
## 20150108_2bvr241913.txt 0 0
## 20150112_2bvq005314.txt 0 0
## 20150113_1bvr047214.txt 0 0
## 20150113_1bvr332314.txt 0 0
## 20150113_2bve000113.txt 0 0
## 20150113_2bvr239514.txt 0 0
## [ reached max_ndoc ... 1,610 more documents ]
dfm_select(entscheidungs_dfm, pattern = "bund*")
## Document-feature matrix of: 1,616 documents, 59 features (95.37% sparse) and 6 docvars.
## features
## docs bundesverfassungsgerichts bundesgerichtshof
## 20150108_2bvr241913.txt 2 1
## 20150112_2bvq005314.txt 1 0
## 20150113_1bvr047214.txt 1 0
## 20150113_1bvr332314.txt 1 0
## 20150113_2bve000113.txt 3 0
## 20150113_2bvr239514.txt 2 0
## features
## docs bundestages bund bundes bundestag bundesvorsitzenden
## 20150108_2bvr241913.txt 0 0 0 0 0
## 20150112_2bvq005314.txt 0 0 0 0 0
## 20150113_1bvr047214.txt 0 0 0 0 0
## 20150113_1bvr332314.txt 0 0 0 0 0
## 20150113_2bve000113.txt 2 1 1 3 5
## 20150113_2bvr239514.txt 0 0 0 0 0
## features
## docs bundesrat bundesregierung bundeskanzlerin
## 20150108_2bvr241913.txt 0 0 0
## 20150112_2bvq005314.txt 0 0 0
## 20150113_1bvr047214.txt 0 0 0
## 20150113_1bvr332314.txt 0 0 0
## 20150113_2bve000113.txt 1 1 1
## 20150113_2bvr239514.txt 0 0 0
## [ reached max_ndoc ... 1,610 more documents, reached max_nfeat ... 49 more features ]
Die Funktion dfm_wordstem
reduziert alle Wörter auf ihre Wortstämme:
dfm_wordstem(entscheidungs_dfm, language = "german")
## Document-feature matrix of: 1,616 documents, 5,644 features (92.59% sparse) and 6 docvars.
## features
## docs 2 13 verfahr s bevollmachtigt rechtsanwalt 75 kiel a
## 20150108_2bvr241913.txt 11 8 1 1 1 1 1 8 2
## 20150112_2bvq005314.txt 7 0 1 1 0 0 0 0 0
## 20150113_1bvr047214.txt 1 3 1 1 1 0 0 0 1
## 20150113_1bvr332314.txt 5 1 2 2 0 2 0 0 0
## 20150113_2bve000113.txt 12 5 3 0 1 0 0 0 0
## 20150113_2bvr239514.txt 19 2 4 1 0 0 0 0 0
## features
## docs landgericht
## 20150108_2bvr241913.txt 3
## 20150112_2bvq005314.txt 0
## 20150113_1bvr047214.txt 0
## 20150113_1bvr332314.txt 0
## 20150113_2bve000113.txt 0
## 20150113_2bvr239514.txt 3
## [ reached max_ndoc ... 1,610 more documents, reached max_nfeat ... 5,634 more features ]
Das Stemming ist im Allgemeinen eine tolle Möglichkeit zur weiteren Informationsreduktion. Für die deutsche Sprache ist es jedoch weniger wirkungsvoll, hier bieten sich andere Verfahren wie zum Beispiel lemmatization an.
Wir können DFMs auch nach relativen Worthäufigkeiten gewichten. Die TF-IDF (term frequency - inverse document frequency) zum Beispiel ist ein statistisches Maß, das bewertet, wie relevant ein Wort für ein Dokument in einer Sammlung von Dokumenten ist. Das geschieht durch die Multiplikation zweier Metriken: 1) wie oft ein Wort in einem Dokument vorkommt und 2) die inverse Dokumenthäufigkeit des Wortes über alle Dokumente hinweg.
entscheidungs_dfm_weighted <- dfm_tfidf(entscheidungs_dfm)
topfeatures(entscheidungs_dfm_weighted)
## antragsgegnerin europäischen nr gesetzgeber
## 2932.732 2203.311 2192.305 2072.679
## sgb urteil beschwerdeführers seien
## 1994.748 1643.206 1607.167 1591.095
## 2011 2017
## 1539.390 1505.244
Wie ihr seht, erzeugt die Gewichtung aussagekräftigere Resultate
Natürlich lassen sich DFMs auch visualisieren. Am wohl bekanntesten sind word clouds, die ihr ja bereits kennengelernt habt:
textplot_wordcloud(entscheidungs_dfm, max_words = 100, scale = c(5,1))
Natürlich ist auch hier ein Vergleich von Dokumenten interessanter. Im folgenden Beispiel nehmen wir die ersten vier Entscheidungstexte. Die Wortgröße zeigt nicht die absolute Häufigkeit in den Dokumenten an, sondern den gewichteten TF-IDF-Wert.
textplot_wordcloud(entscheidungs_dfm[1:4,], color = brewer.pal(4, "Set1"), comparison = T)
Nachdem ihr jetzt die Grundlagen der QTA, also Corpus, Tokens und DFM kennengelernt habt, werden wir ab der nächsten Woche mit der eigentlichen Textanalyse.