library(tidyverse)
library(tidytext)
library(tictoc) # Zeitmessung
library(syuzhet) # Sentimentanalyse
germeval02
Aufgabe
Führen Sie eine Sentiment-Analyse durch. Verwenden Sie verschiedene Verfahren.
Nutzen Sie die GermEval-2018-Daten.
Die Daten sind unter CC-BY-4.0 lizensiert. Author: Wiegand, Michael (Spoken Language Systems, Saarland University (2010-2018), Leibniz Institute for the German Language (since 2019)),
Die Daten sind auch über das R-Paket PradaData zu beziehen.
Hinweise:
- Orientieren Sie sich im Übrigen an den allgemeinen Hinweisen des Datenwerks.
Lösung
Setup
Daten
Nutzen Sie diesen Text-Datensatz, bevor Sie den größeren germeval
-Datensatz verwenden:
<- c("Abbau, Abbruch ist jetzt", "Test heute", "Abbruch morgen perfekt", "Abmachung lore ipsum", "boese ja", "böse nein", "hallo ?! hallo.", "gut schlecht")
text
<- c(2, 0, 2, 1, 1, 1, 0, 2)
n_emo
<-
test_text tibble(id = 1:length(text),
text = text,
n_emo = n_emo)
test_text
# A tibble: 8 × 3
id text n_emo
<int> <chr> <dbl>
1 1 Abbau, Abbruch ist jetzt 2
2 2 Test heute 0
3 3 Abbruch morgen perfekt 2
4 4 Abmachung lore ipsum 1
5 5 boese ja 1
6 6 böse nein 1
7 7 hallo ?! hallo. 0
8 8 gut schlecht 2
Wörterbücher laden:
data(sentiws, package = "pradadata")
#data(schimpfwoerter, package = "pradadata")
$word <- tolower(sentiws$word) # wichtig! sentiws
GermEval-Datensatz:
data(germeval_train, package = "pradadata")
<- as_tibble(germeval_train) # schöneres Print am Bildschirm germeval_train
Sentimentanalyse im Test-Datensatz
Sentimentanalyse mit Regex
Die Funktion count_lexicon
stammt aus {prada}
.
Tipp: Mit ?count_lexicon
sehen Sie den Quelltext (jeder Funktion).
|>
test_text mutate(n_emowords = map_int(text, prada::count_lexicon, sentiws$word))
# A tibble: 8 × 4
id text n_emo n_emowords
<int> <chr> <dbl> <int>
1 1 Abbau, Abbruch ist jetzt 2 2
2 2 Test heute 0 0
3 3 Abbruch morgen perfekt 2 2
4 4 Abmachung lore ipsum 1 1
5 5 boese ja 1 0
6 6 böse nein 1 1
7 7 hallo ?! hallo. 0 0
8 8 gut schlecht 2 2
tic()
|>
germeval_train mutate(n_emowords = map_int(text, ~ prada::count_lexicon(.x, sentiws$word))) |>
head()
# A tibble: 6 × 5
id text c1 c2 n_emowords
<int> <chr> <chr> <chr> <int>
1 1 @corinnamilborn Liebe Corinna, wir würden dich g… OTHER OTHER 2
2 2 @Martin28a Sie haben ja auch Recht. Unser Tweet … OTHER OTHER 2
3 3 @ahrens_theo fröhlicher gruß aus der schönsten s… OTHER OTHER 0
4 4 @dushanwegner Amis hätten alles und jeden gewähl… OTHER OTHER 1
5 5 @spdde kein verläßlicher Verhandlungspartner. Na… OFFE… INSU… 1
6 6 @Dirki_M Ja, aber wo widersprechen die Zahlen de… OTHER OTHER 4
toc()
61.739 sec elapsed
Puh! Viel zu langsam.
Sentimentanalyse mit unnest_tokens
Probieren wir es mit unnest_tokens
:
Jaa,… aber die Strings ohne Treffer werden ignoriert.
|>
test_text unnest_tokens(word, text) |>
right_join(sentiws |> select(word)) |>
count(id)
# A tibble: 6 × 2
id n
<int> <int>
1 1 2
2 3 2
3 4 1
4 6 1
5 8 2
6 NA 3461
Probieren wir es so:
#' Count words in a lexicon
#'
#' Counts how many of the words of the character vector `text` are
#' found in a lexicon `lex`
#' `text` is transformed via tolower.
#'
#' @param text corpus, character vector
#'
#' @return number of hits per element of the corpus
#' @export
#'
#' @examples
#' count_lex(my_text, my_lex)
<- function(text) {
count_lex
stopifnot(class(text) == "character")
<- tibble(text = tolower(text),
doc id = 1:length(text))
<-
doc1 |>
doc ::unnest_tokens(word, text) |>
tidytext::inner_join(sentiws |> dplyr::select(word), by = "word") |>
dplyrcount(id)
<-
doc2 |>
doc1 ::full_join(doc |> select(id), by = "id")
dplyr
$n <- ifelse(is.na(doc2$n), 0,doc2$n)
doc2
<- doc2 |> dplyr::arrange(id)
doc2
|> pull(n)
doc2 }
Mit dem Paket box
kann man Funktionen, die nicht in Paketen stehen, importieren.
count_lex(test_text$text)
[1] 2 0 2 1 0 1 0 2
Als neue Spalte im Datensatz:
|>
test_text mutate(n_emowords = count_lex(text))
# A tibble: 8 × 4
id text n_emo n_emowords
<int> <chr> <dbl> <dbl>
1 1 Abbau, Abbruch ist jetzt 2 2
2 2 Test heute 0 0
3 3 Abbruch morgen perfekt 2 2
4 4 Abmachung lore ipsum 1 1
5 5 boese ja 1 0
6 6 böse nein 1 1
7 7 hallo ?! hallo. 0 0
8 8 gut schlecht 2 2
Sentimentanalyse mit {syuzhet}
Mit dem Lexicon nrc
get_nrc_sentiment(test_text$text, language = "german")
anger anticipation disgust fear joy sadness surprise trust negative positive
1 0 0 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0
3 0 3 0 0 1 0 0 1 0 1
4 0 0 0 0 0 0 0 0 0 0
5 0 0 0 0 0 0 0 0 0 1
6 0 0 0 0 0 0 0 0 1 0
7 0 0 0 0 0 0 0 0 0 0
8 2 1 2 2 1 3 1 1 4 1
Tja, nicht so viele Treffer …
In der Zusammenfassung:
get_nrc_values(text, language = "german")
# A tibble: 1 × 10
anger anticipation disgust fear joy negative positive sadness surprise
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 0 0 0 0 0 0 0 0 0
# ℹ 1 more variable: trust <dbl>
Tja, leider keine Treffer. Merkwürdig.
get_sentiment(text,
method = "nrc",
language = "german")
[1] 0 0 1 0 1 -1 0 -3
Naja, ok.
Mit einem eigenen Lexikon
Beispiel vom Autor des Pakets:
<- "I love when I see something beautiful. I hate it when ugly feelings creep into my head."
my_text <- get_sentences(my_text)
char_v <- "custom"
method <- data.frame(word=c("love", "hate", "beautiful", "ugly"), value=c(1,-1,1, -1))
custom_lexicon <- get_sentiment(char_v, method = method, lexicon = custom_lexicon)
my_custom_values my_custom_values
[1] 2 -2
get_sentiment(text,
method = "custom",
lexicon = sentiws)
[1] -0.0628 0.0000 0.7251 0.0040 0.0000 0.0000 0.0000 -0.3990
Sentimentanalyse im GermEval-Datensatz
Test
tic()
<-
sentiments get_sentiment(germeval_train$text,
method = "custom",
lexicon = sentiws)
toc()
6.76 sec elapsed
length(sentiments)
[1] 5009
head(sentiments)
[1] 0.1025 -0.3426 0.0000 0.0040 -0.0048 -0.3460
Die Geschwindigkeit scheint deutlich besser zu sein, als bei den Regex-Ansätzen.
Als Spalte in die Tabelle
tic()
<-
d |>
germeval_train mutate(n_emo = get_sentiment(germeval_train$text,
method = "custom",
lexicon = sentiws))
toc()
6.635 sec elapsed
head(d)
# A tibble: 6 × 5
id text c1 c2 n_emo
<int> <chr> <chr> <chr> <dbl>
1 1 @corinnamilborn Liebe Corinna, wir würden dich gern… OTHER OTHER 0.103
2 2 @Martin28a Sie haben ja auch Recht. Unser Tweet war… OTHER OTHER -0.343
3 3 @ahrens_theo fröhlicher gruß aus der schönsten stad… OTHER OTHER 0
4 4 @dushanwegner Amis hätten alles und jeden gewählt..… OTHER OTHER 0.004
5 5 @spdde kein verläßlicher Verhandlungspartner. Nachk… OFFE… INSU… -0.0048
6 6 @Dirki_M Ja, aber wo widersprechen die Zahlen denn … OTHER OTHER -0.346
Fazit
syuzhet bietet den besten Ansatz unterm Strich (von den hier vorgestellten Methoden) für eine Sentimentanalyse in deutscher Sprache.
Insgesamt ist die Sentimentanalyse relativ rechenintensiv.
Categories:
- textmining
- tidymodels
- germeval
- sentiment
- string