Direkt zum Hauptbereich

Schreibt Donald Trump seine Tweets selbst?

Ein konkreter und interessanter Nutzen der automatischen Textanalyse zeigt sich beim Twitter-Account TrumpOrNotBot. Basierend darauf, dass Trump früher ein Android-Handy benutzte von welchem aus er Tweets verschickte - während die langweiligeren Tweets von seinen Angestellten von einem iPhone aus versendet wurden - analysierte der Bot Trumps Schreibstil. Im Abgleich damit ordnet er nun jedem neuen Tweet eine Wahrscheinlichkeit zu, ob er von Trump persönlich komme.

Diese Klassifizierung habe ich im Folgenden benutzt, um einen möglichen Unterschied im Schreibstil zu identifizieren. Mit dem rtweet-package erhalten wir die letzten drei Tage. (Achtung! Dies hier ist ein sehr kleiner Datensatz; mit den folgenden Erkenntnissen ist nichts bewiesen, nur veranschaulicht.) Im nächsten Schritt habe ich alle Tweets mit einer Wahrscheinlichkeit über 50% als von Trump stammend betrachet; von den insgesamt 95 Tweets stammen somit 23 von Trump persönlich.

Die Uhrzeit der Trump-Tweets unterscheidet sich auch. Tatsächlich gibt es ja das Gerücht, dass sich Trump v.a. abends gerne mit Twitter beschäftigt.

Und es gibt Unterschiede im Schreibstill zwischen Trump und seinen Angestellten. Im Folgenden die Häufigkeit benutzter Begriffe:

Anhand dieser unterschiedlich verwendeten Begriffe lässt sich die statistische Ähnlichkeit zwischen den beiden Autorengruppen berechnen. Im Folgenden Schaubild sind die Begriffe nach ihrer Häufigkeit in Tweets von Trump (x-Achse) und seiner Angestellten (y-Achse) abgebildet; die Begriffe unter der roten Linie werden also eher von Trump benutzt. Insgesamt ergibt sich anhand der verwendeten Wörter eine signifikante Korrelation von ca. 0.4; heißt, dass die benutzten Wörter ähnlich sind (deswegen die Signifikanz), aber dann doch unterschiedlich (bei einem Wert von 1.0 wäre es identisch).

Dieser Umstand, dass die beiden Autorengruppen Wörter unterschiedlich häufig benutzen, lässt sich auch dafür benutzen, die - statistisch - jeweils wichtigsten Wörter zu identifizieren. Hier wird Zipf´s Law beschrieben, wonach von der Häufigkeit eines Worts auf seine Wichtigkeit rückgeschlossen werden kann. Im Folgenden die Wörter für die beiden Autorengruppen:

Auch die Sentimentwertanalyse habe ich schon häufiger vorgestellt; also ob ein Text eher positiv- oder negativ-konnotiert ist. Hier gibt es einen erheblichen Unterschied: Trumps eigene Tweets sind negativ geprägt, während sein Team positiv schreibt.

Auch die Emotionalität ist unterschiedlich. Manchen Begriffen ist eine Emotion zugeordnet; die gesamte Häufigkeit der Erwähnung ist im Folgenden durch die Anzahl der Tweets geteilt, um der unterschiedlichen Häufigkeit Rechnung zu tragen. Trump benutzt also häufiger emotional-konnotierte Begriffe, während sein Team evtl. auch nur sachliche Ankündigungen tweetet.

Zum Schluss können wir noch betrachten, ob es thematisch einen Unterschied gibt. Tatsächlich lassen sich genau zwei verschiedene Themen für die letzten drei Tage identifizieren, die auch jeweils v.a. von Trump oder v.a. von seinen Angestellten stammen; im Folgenden deren wichtigsten Wörter:

Diese kurze Untersuchung beweist zwar nichts, zeigt aber gut auf, wie man solch statistische Methoden der Textanalysen verwenden kann, um Autoren zu identifizieren. 


 Und hier noch der Code in RStudio
library(rtweet)
library(dplyr)
library(stringr)
library(ggplot2)
library(viridis)
library(tidytext)
library(gridExtra)
library(lubridate)
library(tidyr)
library(scales)
library(reshape)

  
# data prep
tweets <- get_timeline("TrumpOrNotBot", parse = TRUE, check = TRUE)

tweets_data <- tweets %>%
  select(text, quoted_text, quoted_created_at, quoted_favorite_count, quoted_retweet_count) %>%
  mutate(text = substring(text, 50,60)) %>%   #to narrow down
  mutate(text = as.numeric(gsub("\\D", "", text))) %>%  #to get only numbers
  mutate(author = ifelse(text >= 50, "Trump", "staff")) %>%
  mutate(text = NULL) %>%
  mutate(tweet_number = row_number()) %>%
  mutate(quoted_text = str_replace_all(quoted_text, "[^\x01-\x7F]", ""),
         quoted_text = str_replace_all(quoted_text, "\\.|[[:digit:]]+", ""),
         quoted_text = str_replace_all(quoted_text, "https|amp|t.co", ""))


# Plot 1 - zeitl. Verlauf der Tweets
tweets_number <- tweets_data %>%
  mutate(date = as.Date(quoted_created_at, format="%m/%d/%Y")) %>%
  filter(date >= "2020-08-12") %>%
  group_by(date) %>%
  count(date, author)
tweets_number %>%
  ggplot(aes(x=date, y=n)) +
  geom_bar(aes(fill=author), stat="identity", position=position_dodge(preserve = "single"), na.rm=TRUE) +
  scale_fill_viridis(discrete=TRUE, option="cividis", direction=-1) +
  theme_minimal() +
  labs(
    x = NULL, y = NULL,
    title = paste("Anzahl an Tweets"),
    subtitle = ("von TrumpOrNotBot analysierte Trump tweets"),
    caption = "Plot1")
tweets_number %>%
  group_by(author) %>%
  summarise(total_tweets = sum(n))


# Um wie viel Uhr getweetet?
tweets_number2 <- tweets_data %>%
  #mutate(time = as.POSIXct(strptime(.$quoted_created_at, "%H:%M:%S"))) %>%
  mutate(time = as.POSIXlt(.$quoted_created_at)$hour) %>%
  #mutate(date = as.Date(quoted_created_at, format="%h %m")) %>%
  group_by(time) %>%
  count(time, author)
tweets_number2 %>%
  ggplot(aes(x=author, y=time, fill=author)) +
  geom_boxplot(outlier.shape=8) +
  scale_fill_viridis(discrete=TRUE, option="cividis", direction=-1) +
  theme_minimal() +
  theme(legend.position="none") +
  labs(
    x=NULL, y=NULL,
    title = "Unrzeit der Trump Tweets",
    subtitle = "von TrumpOrNotBot analysierte Trump tweets",
    caption = "Plot 2")


# Plot 3 - häufigsten Begriffe
custom_stop_words <- bind_rows(tibble(word = c("realdonaldtrump", "tco", "ive", "pm", "jxoffxyzed", "ag", "im", "met"), 
                                      lexicon = c("custom")),
                               stop_words)
tweets_data_token <- tweets_data %>%  
  unnest_tokens(word, quoted_text) %>%
  anti_join(custom_stop_words, by = "word")
plot1 <- tweets_data_token %>%
  filter(author=="staff") %>%
  count(word, sort = TRUE) %>%
  slice(1:15) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(x = word, y = n, fill = word))+
  geom_col(show.legend = FALSE) +
  coord_flip() +
  theme_minimal() +
  scale_fill_viridis(discrete = TRUE, option="cividis") +
  labs(
    subtitle = ("Tweets not from Trump"),
    x = NULL, y = NULL,
    caption = " ")
plot2 <- tweets_data_token %>%
  filter(author == "Trump") %>%
  count(word, sort = TRUE) %>%
  slice(1:15) %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(x = word, y = n, fill = word))+
  geom_col(show.legend = FALSE) +
  coord_flip() +
  theme_minimal() +
  scale_fill_viridis(discrete = TRUE, option="cividis") +
  labs(
    subtitle = ("Tweets from Trump"),
    x = NULL, y = NULL,
    caption = "Plot 3")
grid.arrange(plot1, plot2, ncol=2, nrow=1, top = "häufigste Begriffe der Tweets")


# Plot 4: Vergleich der häufigsten Wörter
Korr <- cor.test(frequency$Trump, frequency$staff, use="complete.obs") # Statist. Ähnlichkeit zwischen den beiden Autoren nach Worthäufigkeit
frequency <- tweets_data_token %>% 
  group_by(author) %>% 
  count(word, sort = TRUE) %>% 
  left_join(tweets_data_token %>% 
              group_by(author) %>% 
              summarise(total = n())) %>%
  mutate(freq = n/total) %>%
  select(author, word, freq) %>% 
  spread(author, freq) %>%
  arrange(Trump, staff)
frequency %>%
  ggplot(aes(Trump, staff)) +
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.25, height = 0.25) +
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
  scale_x_log10(labels = percent_format()) +
  scale_y_log10(labels = percent_format()) +
  geom_abline(color = "red") +
  theme(plot.title = element_text(size = 5, hjust = 0.5)) +
  theme_minimal() +
  labs(
    title = paste("Vergleich der häufigsten Wörter"),
    subtitle = paste("Korrelation:", round(Korr$estimate,4), " & p-Value:", format.pval(Korr$p.value, digits = 4, nsmall = 3,  eps = 0.001)),
    caption = "Plot4")


# Plot 5 - wichtige Wörter (nach Zipf´s Law), welche die beiden Autorengruppen im Vergleich ausmachen
tweets_data_token %>%
  count(author, word, sort = TRUE) %>%
  ungroup() %>%
  bind_tf_idf(word, author, n) %>%
  arrange(desc(tf_idf)) %>%
  mutate(word = factor(word, levels = rev(unique(word)))) %>% 
  group_by(author) %>% 
  arrange(desc(tf_idf)) %>%
  slice(1:15) %>%
  ungroup() %>%
  ggplot(aes(word, tf_idf, fill = author)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "tf-idf") +
  scale_fill_viridis(discrete = TRUE, option="cividis", direction=-1) +
  facet_wrap(~author, ncol = 2, scales = "free") +
  coord_flip() +
  labs(
    x = NULL, y = NULL,
    title = paste("Im Vergleich charakteristische, wichtige Wörter"),
    subtitle = "von TrumpOrNotBot analysierte Trump tweets",
    caption = "Plot 5")


# Plot 6 - Sentimentwerte
dat_sentiment1 <- tweets_data_token %>%
  group_by(author) %>%
  inner_join(get_sentiments("bing"), by="word") %>%
  count(sentiment) %>%
  spread(sentiment, n, fill = 0) %>%
  mutate(sentiment_bing = positive - negative) %>%
  ungroup()
dat_sentiment2 <- tweets_data_token %>%
  group_by(author) %>%
  inner_join(get_sentiments("afinn"), by="word") %>%
  summarise(sentiment_afinn = sum(value)) %>%
  ungroup()
dat_sentiment <- left_join(dat_sentiment1, dat_sentiment2, by=c("author"))
dat_sentiment %>%
  select(author, sentiment_bing, sentiment_afinn) %>%
  as.data.frame() %>%
  melt(., id.vars=c("author")) %>%
  ggplot(aes(x = variable, y = value, fill = author))+
  geom_bar(aes(fill=author), stat="identity", position=position_dodge(preserve = "single"), na.rm=TRUE) +
  scale_fill_viridis(discrete=TRUE, option="cividis", direction=-1) +
  theme_minimal() +
  labs(
    x = NULL, y = NULL,
    title = paste("Sentimentwerte nach Autor"),
    subtitle = ("von TrumpOrNotBot analysierte Trump tweets"),
    caption = "Plot6")


# Plot 7 - Emotionen
dat_sentiment3 <- tweets_data_token %>%
  group_by(author) %>%
  inner_join(get_sentiments("nrc"), by="word") %>%
  count(sentiment) %>%
  spread(sentiment, n, fill = 0) %>%
  ungroup() %>%
  as.data.frame() %>%
  melt(., id.vars=c("author")) %>%
  mutate(value = ifelse(.$author == "Trump", .$value / 23 , .$value / 72)) # Anzahl an Tweets
dat_sentiment3 %>%
  ggplot(aes(x = variable, y = value, fill = author))+
  geom_bar(aes(fill=author), stat="identity", position=position_dodge(preserve = "single"), na.rm=TRUE) +
  scale_fill_viridis(discrete=TRUE, option="cividis", direction=-1) +
  theme_minimal() +
  labs(
    x = NULL, y = NULL,
    title = paste("die Häufigkeit von emotionalen Begriffe pro Tweet"),
    subtitle = ("die Anzahl der Emotionen insgesamt geteilt durch die Anzahl an Tweets"),
    caption = "Plot 7")
  

# Plot 8: zwei gemeinsame Themen in beiden Texten
texts_DTM <- tweets_data_token %>%
  count(author, word) %>%
  cast_dtm(author, word, n) %>%
  LDA(., k=2, control = list(seed = 1234))
topics_prob <- tidy(topics, matrix = "beta") #probability of that term being generated from that topic for each line
topic_terms <- topics_prob %>%
  group_by(topic) %>%
  top_n(10, beta) %>% #hier anpassen
  ungroup() %>%
  arrange(topic, -beta)
topic_terms %>%
  mutate(term = reorder_within(term, beta, topic)) %>%
  ggplot(aes(term, beta, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  scale_fill_viridis(discrete=TRUE, option="cividis", direction=-1) +
  coord_flip() +
  scale_x_reordered() +
  theme_minimal() +
  labs(
    x = NULL, y = NULL,
    title = paste("Themen in Trump Tweets"),
    subtitle = ("statistisch gesehen stammt Thema 1 v.a. von den Angestellten und Thema 2 v.a. von Trump persönlich"),
    caption = "Plot 8")
# gamma ist der Anteil an Wörtern, aus welchen jeweils ein Thema generiert wurde
# d.h. je mehr ein PDF ein Thema ausmacht, desto höher das Gamma
# -> thematischer Unterschied, wenn jeweils ein PDF zu einem Topic beiträgt
topic_documents <- tidy(topics, matrix = "gamma")
topic_documents


Die nrc-Bibliothek zur Sentimentanalyse wird hier beschrieben:
Crowdsourcing a Word-Emotion Association Lexicon, Saif Mohammad and Peter Turney, Computational Intelligence, 29 (3), 436-465, 2013.
Emotions Evoked by Common Words and Phrases: Using Mechanical Turk to Create an Emotion Lexicon, Saif Mohammad and Peter Turney, In Proceedings of the NAACL-HLT 2010 Workshop on Computational Approaches to Analysis and Generation of Emotion in Text, June 2010, LA, California.

Photo by Carlos Herrero from Pexels

Beliebte Posts aus diesem Blog

Was ist fremd?

brandy74 "Malstunde" Some rights reserved. www.piqs.de Der Begriff Fremdheit wird benutzt zur Charakterisierung einer Beziehung. Immer muss etwas bekannt sein um es auch als fremd zu bezeichnen; andernfalls kann es nicht beschrieben werden. Wissenschaftlich wird die Fremdheit oft auch als die Gleichzeitigkeit von Nähe und Entferntheit, von Verbundenheit und Getrenntheit charakterisiert. Wer demnach etwas als fremd bezeichnet, unterscheidet die Welt an dieser Stelle in ein Innen und ein Außen. Das Fremde sei jenseits einer einer imaginären Grenze. Diese Grenzen können unterschiedlich lokalisiert werden. Bei der kulturellen Fremdheit werden andere kulturelle Verhaltensweisen und Ansichten identifiziert und als fremd bezeichnet. Bei der sozialen Fremdheit ist der Fremde hingegen Teil der eigenen Gesellschaft, der eigenen Gemeinschaft. Durch die Zuschreibung der sozialen Fremdheit wird er aus dem eigenen Bereich, also dem eigenen sozialen Milieu, exkludiert. Drückt sich

Sentiment-Analyse von deutschen Texten in R

Eine Sentiment-Analyse funktioniert im Grunde wiefolgt: die einzelnen Wörter werden eines Textes werden mit bestimmten Bibliotheken abgeglichen und eine Einteilung in "positiv/negativ" oder ein hinterlegter Sentiment-Wert abgegriffen. Die Summe dieser Werte ergibt schließlich den Sentiment-Score des ganzen Texts. Für englische Texte sind in R bereits Bibliotheken hinterlegt (z.B. im Package tidytext ). Für deutsche Texte habe ich auf meiner Recherche die Bibliothek  SentiWS  der Universität Leipzig gefunden. Die rund 16.000 positiven und 18.000 negativen Wörter sind mit einer Wertspanne von -1 zu 1 hinterlegt. Das Problem ist, dass diese in zwei Textdateien kommen, deren Format erst aufbereitet werden muss. So sieht die Bibliothek beim Einlesen aus: Mit folgendem Code habe ich mir die Bibliothek operationalisiert: library(dplyr) # SentiWS - Dateien hier runterladen: https://wortschatz.uni-leipzig.de/en/download # a) negative Wörter # die Textdatei einlesen negat

Migration und Bevölkerungsentwicklung: Solidarität und Selbsthilfe

Aus: Neue Potenziale - zur Lage der Nation in Deutschland , Juni 2014,  Berlin-Institut für Bevölkerung und Entwicklung Vor ein paar Wochen war ich auf einem sehr spannenden Vortrag am ifo-Institut in München von Herrn Dr. Klingholz, Direktor des Berlin Instituts für Bevölkerung und Entwicklung. Der Vortrag widmete sich einerseits der Zusammensetzung und dem Bildungs- wie Integrationsgrad deutscher Migranten und andererseits der zukünftigen Bevölkerungsentwicklung in Teilen der Welt und deren Auswirkungen auf die Migration in Europa, bzw. Deutschland. Polarisierend Unterteilt man die Migranten(1) nach Gruppen hinsichtlich ihrer Herkunftsländer, so zeigt sich oft eine starke Polarisierung des Bildungsgrades. Beispiel Rumänien und Polen. Zwar ist der Anteil der Migranten aus Rumänien und Polen ohne Bildungsabschluss wesentlich höher als der Anteil der Einheimischen. Umgekehrt ist der Anteil an Akademikern bei Migranten aus Rumänien und Polen höher als bei Einheimischen. Auch

die Hot-Dog-Ökonomie

Diego Torres Silvestre " Ice Creams, Hot Dogs & Pretzels" Some rights reserved. www.piqs.de Man stelle sich eine Wirtschaft vor, in der nur zwei Güter hergestellt würden: Würstchen und Brötchen. Konsumenten würden Hotdogs kaufen; also jeweils ein Brötchen mit einer Wurst. Die Fertigung geschieht durch Menschenhand. So fing Paul Krugman 1997 einen Artikel für das Online-Magazine Slate an, in welchem er den Zusammenhang von Technologie, Jobs und Kapitalismus erklären will. Er fährt fort, dass in dieser Wirtschaft 120 Millionen Arbeiter beschäftigt sind, was einer Vollbeschäftigung entspreche. Zur Herstellung einer Wurst oder eines Brötchens benötige es zwei Arbeitstage. Die 60 Millionen Angestellten in der Brötchenproduktion und genauso viele in der Wurstfabrikationen produzieren demnach täglich 30 Millionen Brötchen und Würste. Angenommen es komme eine verbesserte Technologie auf, mit deren Hilfe ein Arbeiter zur Herstellung einer Wurst nur noch einen Tag

die schöne Welt von Red Bull

Till Krech "wroooom" Some rights reserved. www.piqs.de Red Bull – vom Marktführer für Energiegetränke zum kommenden Medienimperium? Das Magazin Fast Company vergab in der Liste „The World´s 50 Most Innovative Companies“ den 29. Platz an Red Bull für genau diese Entwicklung. Gebündelt unter dem Dach der Red Bull Media House GmbH besitzt der Konzern mittlerweile verschiedene Medienbeteiligungen und Neugründungen. Kritiker bezeichnen es als eine gewaltige Marketingmaschine. Rund ein Drittel des Umsatzes wird für die Pflege des Marktauftritts ausgegeben. Eine firmeninterne Nachrichtenagentur sammelt Inhalte zu einen der vielen weltweiten aufsehenerregenden Red-Bull-Ereignisse, um sie externen Medien gebündelt und aufbereitet zur Verfügung zu stellen. Über eigene Medien werden die Konsumenten sogar direkt erreicht. Das 2007 gegründete Hochglanzmagazin "Red Bulletin" hat bereits eine Auflage von 5 Millionen Heften erreicht und wird mehrspraching in zwö

Verspargelung der Landschaft

FZ 18: "Mount Idarkopf" www.piqs.de Some Rights reserved. Vielleicht ist es, weil ich erst 22 Jahre alt bin. Vielleicht weil es bei meiner Heimatstadt schon seit mehr als zehn Jahren ein Windrad gibt. Aber das Argument einer Verspargelung der Landschaft durch Windräder zählt für mich nicht. Ich komme aus Baden-Württemberg. Insofern verfolgt mich das Argument der Verspargelung der Landschaft durch den ehemaligen baden-württembergischen Ministerpräsidenten Erwin Teufel fast genauso lange wie das Windrad vor meiner Haustür. Das Argument wird immer wieder von jenen hervorgebracht, welche gegen die Aufstellung von Windrädern sind. Die einen fürchten um die Landschaft, andere finden sie einfach nicht schön und noch andere bringen es nur als Vorwand. Besonders die Nähe zur Atomwirtschaft fällt einem bei der hießigen CDU auf. In Baden-Württemberg ist der Fall bei den Windrädern vielleicht ein bisschen spezieller. Wenn man hier die Windenergie effizient nutzen will, so