Blog

Text Mining – Part 3: Making-Of

Eine Sentiment-Analyse hat das Ziel, die Wahrnehmung (Polarität) eines Textes oder Tokens zu quantifizieren. Es wird demnach analysiert, ob der Text im Allgemeinen als positiv (Wörter wie z.B. “Glück”) oder negativ (z.B. “Verrat”) wahrgenommen wird oder eher neutral ist. Dies geschieht auf einer Skala von -1 bis 1, wobei -1 extrem negative und 1 extrem positive Begriffe widerspiegelt. 0 steht für Neutralität. Dabei gibt es verschiedene Abstufungen, beispielsweise wird das Wort “perfekt” von Menschen im Schnitt wesentlich positiver wahrgenommen als das Wort “zufrieden”, obwohl beide im positiven Bereich liegen.

Im zweiten Teil der Serie wurde beschrieben, wie die Wahlprogramme der sechs stärksten Parteien für ein Text-Mining vorbereitet wurden. Daraus resultierten Daten mit einem Wort (“Token”) pro Zeile. Um den Tokens der Wahlprogramme Sentiments zuweisen zu können, wird nun eine Art Wörterbuch importiert, das die Polarität für (viele, aber nicht alle) deutsche Wörter enthält. Vielen herzlichen Dank an die Uni Leipzig, die für diese Zwecke den Sentiment-Wortschatz (SentiWS) zur Verfügung stellt.

# Pakete laden
library("dplyr")
library("ggplot2")
library("magrittr")

Datenvorbereitung

Die Wörterbücher werden zunächst geladen und für die Weiterverarbeitung entsprechend aufgearbeitet.

# Wörter laden und vorbereiten
sent <- c(
  # positive Wörter
  readLines(paste0(FILEPATH, "SentiWS_v1.8c_Positive.txt"),
            encoding = "UTF-8"),
  # negative Wörter
  readLines(paste0(FILEPATH, "SentiWS_v1.8c_Negative.txt"),
            encoding = "UTF-8")
) %>% lapply(function(x) {
  # Extrahieren der einzelnen Spalten
  res <- strsplit(x, "\t", fixed = TRUE)[[1]]
  return(data.frame(words = res[1], value = res[2],
                    stringsAsFactors = FALSE))
}) %>%
  bind_rows %>% 
  mutate(words = gsub("\\|.*", "", words) %>% tolower,
         value = as.numeric(value)) %>% 
  # manche Wörter kommen doppelt vor, hier nehmen wir den mittleren Wert
  group_by(words) %>% summarise(value = mean(value)) %>% ungroup

Die Sentiments werden mittels eines Joins an die Tabelle mit den Tokens der Wahlprogramme angefügt. Für die Wörter in den Wahlprogrammen, für die kein Sentiment verfügbar ist, erhalten wir dadurch einen NA-Wert. Alle NAs werden entfernt.

sentTxt <- left_join(txt, sent, by = "words") %>% 
  mutate(value = as.numeric(value)) %>% 
  filter(!is.na(value))

Durchschnittliches Sentiment

Der Durchschnitt über alle Sentiments ergibt einen Indikator für die Gesamtwirkung der einzelnen Wahlprogramme. Dabei weist die AfD den negativsten Wert auf, knapp gefolgt von der Linken. Positive Durchschnittswerte sind nur für die beiden aktuellen Regierungsparteien zu beobachten:

sentTxt %>% 
  group_by(party) %>% 
  summarize(meanSent = mean(value))
## # A tibble: 6 x 2
##   party     meanSent
##   <chr>        <dbl>
## 1   AfD -0.053041256
## 2   CDU  0.028684718
## 3   FDP -0.014116642
## 4 Grüne -0.019918778
## 5 Linke -0.048160861
## 6   SPD  0.003802914

Häufigste positive und negative Wörter

Der folgende Plot zeigt die 15 positiven und negativen Wörter, die über alle Parteien hinweg am häufigsten verwendet werden.

sentTxt %<>% 
  mutate(sent = ifelse(value >= 0, "positive", "negative")) 

sentTxt %>% 
  count(words, sent, sort = TRUE) %>%
  ungroup %>% 
  group_by(sent) %>%
  top_n(15, n) %>%
  ungroup() %>%
  mutate(word = reorder(words, n)) %>%
  ggplot(aes(word, n, fill = sent)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sent, scales = "free_y") +
  labs(x = NULL, y = "Häufigkeit") +
  coord_flip()
Häufigste Wörter

Positivste und negativste Wörter nach Partei

Die folgenden beiden Grafiken zeigen die jeweils zehn Wörter mit dem höchsten und niedrigsten Sentiment für jede Partei.

# Funktion zum Plotten
plotSentParty <- function(dat) {
  dat %>% 
    ggplot(aes(words, value, fill = party)) +
    geom_col(show.legend = FALSE) +
    labs(x = NULL, y = "Sentiment") +
    facet_wrap(~party, ncol = 2, scales = "free_y") +
    coord_flip() +
    scale_fill_manual(breaks = names(partyColor), values = partyColor)
}

# positive Wörter nach Partei
sentTxt %>% 
  arrange(desc(value)) %>%
  mutate(words = factor(words, levels = rev(unique(words)))) %>% 
  group_by(party) %>% 
  filter(!duplicated(words)) %>% 
  top_n(10, value) %>% 
  ungroup %>% 
  plotSentParty
Sentiment nach Partei positiv
# Negative Wörter nach Partei
sentTxt %>% 
  arrange(value) %>%
  mutate(words = factor(words, levels = rev(unique(words)))) %>% 
  group_by(party) %>% 
  filter(!duplicated(words)) %>% 
  top_n(-10, value) %>% 
  ungroup %>% 
  plotSentParty
Sentiment nach Partei negativ

Quelle

R. Remus, U. Quasthoff & G. Heyer: SentiWS – a Publicly Available German-language Resource for Sentiment Analysis. In: Proceedings of the 7th International Language Ressources and Evaluation (LREC’10), pp. 1168-1171, 2010

Weitere Teile der Artikelreihe:

by Sarah Wagner

Text Mining – Part 1: Analyse der Wahlprogramme für die BTW 2017

by Sarah Wagner

Text Mining – Part 2: Making-Of