-
Notifications
You must be signed in to change notification settings - Fork 0
/
trump_tweet_analysis.Rmd
496 lines (435 loc) · 19.3 KB
/
trump_tweet_analysis.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
---
title: "Trump's Tweetanalyse"
author: "Thomas de Beus"
date: "`r format(Sys.time(), '%d %B, %Y')`"
output:
html_document:
css: style.css
toc: TRUE
toc_depth: 5
theme: journal
code_folding: hide
number_sections: true
---
<style>
#TOC {
position: fixed;
left: 0;
top: 10;
width: 350px;
height: 100%;
overflow:auto;
}
</style>
![](http://i.cdn.cnn.com/cnn/interactive/2017/politics/trump-tweets/media/trump-tweets-hdr-02.jpg)
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, message=FALSE, warning=FALSE)
```
# Waarom een tweetanalyse?
Een eerste jaar Donald J. Trump in het witte huis. Misschien wel mede mogelijk gemaakt door zijn karakteristieke uitlatingen op Twitter. In de aanloop van de verkiezingen zijn de tweets van de huidige president van de V.S. een krachtig middel gebleken. En nog steeds bereikt hij zijn miljoenen kiezers zonder de traditionele media nodig te hebben. Trump zelf verschijnt dan ook relatief weinig voor de camera, in kranten of andere news sites.
Om beter vat te krijgen op hoe Trump zijn favoriete medium inzet nemen we zijn tweets onder de loep. **Doormiddel van een tekstanalyse hopen we een aantal vragen te beantwoorden**.
Vragen zoals:
* Welke Tweets zijn daadwerkelijk van Trump afkomstig?
* Hoeveel tweets stuurt Trump gemiddeld per dag?
* Wanneer op de dag tweet hij met name? s'Avonds, s'ochtends of s'middags?
* Welke tweets worden het meest geretweet en geliked?
* Welke woorden gebruikt Trump het meest?
# Data verkrijgen
De geïmporteerde dataset is van [Trump Twitter Archive](http://www.trumptwitterarchive.com/archive). Dit is een betere bron dan de Twitter API welke niet altijd alle tweets geeft. Bovendien slaat de Trump Twitter Archive ook de meeste verwijderde tweets op.
Als we alle tweets filteren op diegene waarvan wij denken dat ze afkomstig zijn van Trump bestaat de dataset uit bijna **7000** tweets vanaf 05-02-2013, vijf en half jaar geleden.
Trump Twitter Archive word elk uur geupdate. Als wij de code in onder de R-script op deze pagina 'runnen' dan worden alle data tabellen en grafieken als vanzelf geupdate. De twitter dataset bevat de volgende *zeven* variabelen:
* `text`: tekst van de tweets.
* `created_at`: datum en tijd van tweet in "GMT"
* `source`: op welk apparaat of met welke software de tweet is gepost
* `retweet_count`: aantal 'retweets'
* `favourite_count`: aantal 'vind-ik-leuks'
* `is_retweet`: of de tweet een retweet is of niet
* `id_str`: uniek karakter van tweet
```{r acquire, results='hide'}
# Loading the used libraries
library(tidyverse)
library(lubridate)
library(tidytext)
library(DT)
library(scales)
library(hrbrthemes)
library(cowplot)
library(rvest)
library(ggthemes)
# url Trump Twitter Archive
url <- 'http://www.trumptwitterarchive.com/data/realdonaldtrump/%s.json'
# Retrieve all trump's tweets and create dataset with converted `created_at` character dates
original_df <- map(2009:2017, ~sprintf(url, .x)) %>%
map_df(jsonlite::fromJSON, simplifyDataFrame = TRUE) %>%
mutate(created_at = parse_date_time(created_at, "a b! d! H!:M!:S! z!* Y!")) %>%
tbl_df()
# If above doesn't work download data on website then:
# original_df <- read.csv("filename.csv", quote = "", comment = "")
```
## Klaarmaken voor analyse: Wanneer zijn de tweets afkomstig van Trump zelf?
Het uiteindelijke doel is om een dataset te maken met unieke Trump tweets. Omdat data scientist David Robinson in zijn analyse, [Text analysis of Trump's tweets confirms he writes only the (angrier) Android half](http://varianceexplained.org/r/trump-tweets/), er vrijwel zeker van is dat Trump destijds een Android toestel gebruikte [passen anderen machine learning toe](http://didtrumptweetit.com/).
Robinson concludeert een jaar later in een follow-up: [Trump's Android and iPhone tweets, one year later](http://varianceexplained.org/r/trump-followup/) dat Trump vrijwel altijd tweet:
* zonder links, hashtags of afbeeldingen
* afkomstig van een Android toestel, tot hij een iPhone kocht, hoogst waarschijnlijk rond 25-03-2017.
Wij zullen dat in deze analyse overnemen door de computer op tweets te laten filteren waarvan de `text` hashtags ("#") en/of links ("http") bevatten. Afbeeldingen worden tegenwoordig niet meer in de text van de tweets meegenomen. Ook nemen we de tweets die vanaf andere toestellen afkomstig zijn niet mee.
Het spreekt voor zich dat we alleen originele tweets meenemen dus geen retweets. Bovendien zullen gelijk al filteren op de tweets die vanaf zijn inauguratie zijn verstuurd (20-101-2017).
```{r prepare, results='hide'}
# Subset data on high probability that Trump himself is actually tweeting and add
all_trump_tweets <- original_df %>%
rename(retweets = retweet_count,
favorites = favorite_count) %>%
filter(is_retweet != "true",
source == "Twitter for iPhone" |
source == "Twitter for Android",
!grepl("http|#|RT|@realdonaldtrump|@realDonaldTrump", text)) %>%
rowid_to_column("ID") %>%
select(ID, text, created_at, retweets, favorites, source)
# Filter out all iPhone tweets before 25-03-2017
all_trump_tweets <- all_trump_tweets %>%
filter((created_at < "2017-03-15" & source != "Twitter for iPhone") | created_at >= "2017-03-15") %>%
arrange(desc(created_at))
# Get rid of emoji characters R doesn't like them
all_trump_tweets$text <- gsub("[^\x01-\x7F]", "", all_trump_tweets$text)
# Remove redundant "amp"
all_trump_tweets$text <- gsub("amp", "", all_trump_tweets$text)
# Remove numbers
all_trump_tweets$text <- gsub("[0-9]+", "", all_trump_tweets$text)
# Convert GMT time to Eastern US time want Trump tweet daarvanuit het meest en originele tijden zijn GMT (Greenwich Time)
all_trump_tweets$created_at <- with_tz(all_trump_tweets$created_at, tzone = "US/Eastern")
# Create dataset since inauguration
president_tweets <- all_trump_tweets %>%
filter(created_at > "2017-01-20")
# Create dataset since inauguration
candidate_tweets <- all_trump_tweets %>%
filter(created_at > "2015-06-16")
# Html widget interactive table in rmarkdown report with only the four useful columns
datatable(head(president_tweets[,c(2,5,6,1,4)], n = nrow(president_tweets)), options = list(pageLength = 5))
```
# Data analyseren
```{r tweet-analysis-top-10}
# Top 10 retweets
most_rt <- president_tweets %>%
arrange(desc(retweets))
# Html widget interactive table in rmarkdown report
datatable(head(most_rt[,c(2, 4, 5, 3)], n = nrow(most_rt)), options = list(pageLength = 5))
```
## Text analyse per woord
Welke woorden gebruikt Trump het meest in zijn tweets?
### Vanaf Inauguratie
```{r tweet-analysis-per-word}
# Create new dataset `tweets_fy_words` (tweets first year words)
tweets_words <- president_tweets %>%
unnest(text) %>% # Unnest gets rid of lists in text column
unnest_tokens(word, text)
# Remove stop words and numbers, which aren't useful
tweets_words <- tweets_words %>%
anti_join(stop_words) # Because in text is 'space' coded as "amp&"
# Sort on most used words
most_used_words <- tweets_words %>%
count(word, sort = TRUE) %>%
mutate(times_used = n,
word = reorder(word, times_used)) %>%
select(word, times_used)
# Convert type word column to charcter for better handeling
most_used_words$word <- as.character(most_used_words$word)
# Sort on times_used
most_used_words <- most_used_words %>%
arrange(desc(times_used))
# Plot in bar chart
plot_most_used_words <- most_used_words %>%
top_n(20) %>%
ggplot(aes(x = reorder(word, times_used), times_used)) +
geom_segment(aes(x = reorder(word, times_used), xend = word, y = 0,yend = times_used),
colour = "#737373") +
geom_point(colour = "#03A9F4",
size = 3) +
coord_flip() +
theme_minimal() +
theme(panel.grid.major.y = element_blank(),
plot.background = element_blank(),
plot.title = element_text(face = "bold",
size = 18)) +
labs(title = "De 20 meest gebruikte woorden in Trump's tweets",
subtitle = "Vanaf zijn inauguratie op 20-01-2017.",
x = NULL,
y = "Aantal keer gebruikt")
# Plot
plot_most_used_words
# Html widget interactive table in rmarkdown report
datatable(head(most_used_words, n = nrow(most_used_words)), options = list(pageLength = 5))
```
## Welke woordparen gebruikt Trump het vaakst?
```{r tweet-analysis-word-pairs}
candidate_bigrams <- candidate_tweets %>%
unnest_tokens(bigram , text, token = "ngrams", n = 2) %>%
separate(bigram, c("word1", "word2"), sep = " ") %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word) %>%
unite(bigram, word1, word2, sep = " ")
president_bigrams <- candidate_bigrams %>%
filter(created_at > "2017-01-20") %>%
group_by(bigram) %>%
mutate(count = n())
head(president_bigrams %>%
mutate(count = n()) %>%
distinct(bigram, count) %>%
arrange(desc(count)),20) %>%
ggplot(aes(x = reorder(bigram, count), count)) +
geom_segment(aes(x = reorder(bigram, count), xend = bigram, y = 0,yend = count),
colour = "#737373") +
geom_point(colour = "#03A9F4",
size = 3) +
coord_flip() +
theme_minimal() +
theme(panel.grid.major.y = element_blank(),
plot.background = element_blank(),
plot.title = element_text(face = "bold",
size = 18)) +
labs(title = "De 20 meest gebruikte woorden in Trump's tweets",
subtitle = "Vanaf zijn inauguratie op 20-01-2017.",
x = NULL,
y = "Aantal keer gebruikt")
```
## Wanneer op de dag verstuurt Trump zijn tweets?
* Trump vanaf toen hij president was
* Trump vanaf dat hij zich officieel kandidaat stelde
* Trump van vóór zijn kandidaatstelling
```{r tweet-analysis-when-1, fig.height=5}
# Create dataframe with weekdays
weekday_tweets <- president_tweets %>%
mutate(hour_of_day = hour(created_at),
weekday = strftime(created_at, "%a")) %>%
group_by(weekday, hour_of_day) %>%
summarize(count = n()) %>%
mutate(percentage = count / sum(count))
# Order weekdays on Monday first
weekday_tweets$weekday <- factor(weekday_tweets$weekday, levels = c("Mon",
"Tue", "Wed", "Thu", "Fri", "Sat", "Sun"))
# Plot distributions
weekday_tweets %>%
ggplot(aes(hour_of_day, count)) +
geom_col(fill = "#03A9F4") +
scale_x_continuous(breaks = seq(0,23,4)) +
theme_minimal() +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
plot.title = element_text(face = "bold",
size = 18)) +
labs(title = "Een week in tweets van president Trump",
subtitle = "Tweets per uur op de dag sinds zijn inauguratie.",
x = "Uur van de dag",
y = "Aantal tweets") +
facet_wrap(~weekday)
```
```{r, tweet-analysis-when-2, fig.height=11}
# -------------------President
# Create datframe that groups hours a day and the nr of tweets during his presidency and add percentage column
tweet_time_president <- all_trump_tweets %>%
mutate(hour_of_day = hour(created_at)) %>%
filter(created_at > "2017-01-20") %>% # Consists of 1.235 tweets
group_by(hour_of_day) %>%
summarize(count = n()) %>%
mutate(percentage = count / sum(count))
# Plot a bar chart of a day
plot_tweet_time_president <- tweet_time_president %>%
ggplot(aes(hour_of_day, percentage)) +
geom_col(fill = "#03A9F4") +
theme_minimal() +
scale_y_continuous(labels = percent_format(),
limits=c(0,0.2)) +
scale_x_continuous(breaks = seq(0,23,2)) +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
plot.title = element_text(face = "bold",
size = 18)) +
labs(title = "President Trump",
subtitle = "Bestaat uit 1.235 tweets.",
x = "Uur van de dag",
y = "% van tweets")
# -------------------Candidate
# Same calculation than `tweet_time_president` but filterd before inauguration and after announcing official canidatcy
tweet_time_candidate <- all_trump_tweets %>%
mutate(hour_of_day = hour(created_at)) %>%
filter(created_at < "2017-01-20" &
created_at > "2015-06-16") %>% # Consists of 2.493 tweets
group_by(hour_of_day) %>%
summarize(count = n()) %>%
mutate(percentage = count / sum(count))
# Plotting same barchart
plot_tweet_time_candidate <- tweet_time_candidate %>%
ggplot(aes(hour_of_day, percentage)) +
geom_col(fill = "#03A9F4") +
theme_minimal() +
scale_y_continuous(labels = percent_format(),
limits=c(0,0.2)) +
scale_x_continuous(breaks = seq(0,23,2)) +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
plot.title = element_text(face = "bold",
size = 18)) +
labs(title = "Kandidaat Trump",
subtitle = "Bestaat uit 2.493 tweets.",
x = "Uur van de dag",
y = "% van tweets")
# --------------------Before Politics
# Same calculation but filterd before announcing official canidatcy
tweet_time_before_politics <- all_trump_tweets %>%
mutate(hour_of_day = hour(created_at)) %>%
filter(created_at < "2015-06-16") %>% # Consists of 3.141 tweets
group_by(hour_of_day) %>%
summarize(count = n()) %>%
mutate(percentage = count / sum(count))
# Plotting same barchart
plot_tweet_time_before_politics <- tweet_time_before_politics %>%
ggplot(aes(hour_of_day, percentage)) +
geom_col(fill = "#03A9F4") +
theme_minimal() +
scale_y_continuous(labels = percent_format(),
limits=c(0,0.2)) +
scale_x_continuous(breaks = seq(0,23,2)) +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
plot.title = element_text(face = "bold",
size = 18)) +
labs(title = "Trump vóór officiële politieke carriére ",
subtitle = "Bestaat uit 3.141 tweets.",
x = "Uur van de dag",
y = "% van tweets")
# Place al three plot's in grid
plot_grid(plot_tweet_time_president, plot_tweet_time_candidate, plot_tweet_time_before_politics, ncol = 1, align = 'v')
```
## Wanneer heeft Trump welke woordparen gebruikt
```{r tweet-analysis-bubble-1, fig.height = 2.12}
# Bubble time line
bubble_plot_1 <- candidate_bigrams %>%
filter(created_at > "2017-01-01",
grepl("fake news|fake media", bigram, ignore.case = TRUE)) %>%
ggplot(aes(x = created_at, y = 0)) +
geom_point(aes(size = retweets),
alpha = 0.1,
colour = "#B71C1C") +
scale_size(range = c(0,15)) +
scale_x_datetime(date_labels = "%b", date_breaks = "1 month") +
theme_minimal() +
theme(legend.position = "none",
plot.background = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_text(hjust = -0.5),
panel.grid = element_blank(),
panel.grid.major.x = element_line(colour = "#BDBDBD",
linetype = "dotted")) +
labs(title = "Wanneer Trump 'Fake News/Media' in zijn tweets gebruikt",
x = "",
y = "")
bubble_plot_1
```
```{r tweet-analysis-barcode-1, fig.height = 3}
# Barcode with fixed size
barcode_1 <- candidate_bigrams %>%
filter(created_at > "2017-01-01",
bigram %in% c("fake news",
"tax cuts")) %>%
ggplot(aes(x = created_at, y = 0, colour = bigram)) +
geom_point(shape = 124,
size = 15,
alpha = 0.5) +
scale_x_datetime(date_labels = "%b", date_breaks = "1 month") +
theme_minimal() +
theme(plot.background = element_blank(),
axis.text.y = element_blank(),
axis.text.x = element_text(hjust = -0.5),
panel.grid = element_blank(),
panel.grid.major.x = element_line(colour = "#BDBDBD",
linetype = "dotted")) +
labs(title = "Wanneer Trump 'Fake News' en 'Tax Cuts' in zijn tweets gebruikte",
x = "",
y = "")
barcode_1
```
```{r tweet-analysis-barcode-2, fig.width=5.3, fig.height=9.3}
plot_colours3 <- c("#b7002e", "#005cb7", "#baa400")
# Barcode where size is according number of retweets
barcode_2 <- candidate_bigrams %>%
filter(created_at > "2017-01-01",
bigram %in% c("fake news",
"north korea",
"tax cuts")) %>%
ggplot(aes(x = created_at, y = 0, colour = bigram, size = retweets)) +
geom_point(shape = 95,
alpha = 0.4) +
scale_size(range = c(1, 35)) +
scale_x_datetime(date_labels = "%b",
date_breaks = "1 month") +
scale_colour_manual(values = plot_colours3) +
coord_flip() +
theme_minimal() +
theme(legend.position = "none",
plot.background = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_text(vjust = -2.5),
panel.grid = element_blank(),
panel.grid.major.y = element_line(colour = "#BDBDBD",
linetype = "dotted")) +
labs(title = "Wanneer Trump zijn meest gebruikte\nwoordparen in zijn tweets gebruikt",
subtitle = "Elk streepje is een tweet. De groter het streepje\ndes te meer retweets.",
x = "",
y = "")
barcode_2
```
```{r tweet-analysis-barcode-3, fig.height=4.2}
plot_colours4 <- c("#ba001f", "#001fb7", "#b79900", "#00b799")
# Barcode where size is according to number of retweets and multiple words on their own line
barcode_3 <- candidate_bigrams %>%
filter(created_at > "2017-01-01",
bigram %in% c("fake news",
"north korea",
"tax cuts",
"failing nytimes")) %>%
ggplot(aes(x = created_at, y = bigram, colour = bigram, size = retweets)) +
geom_point(shape = 124,
alpha = 0.6) +
scale_size(range = c(1, 10)) +
scale_x_datetime(date_labels = "%b", date_breaks = "1 month") +
scale_colour_manual(values = plot_colours4) +
theme_minimal() +
theme(legend.position = "none",
plot.background = element_blank(),
axis.text.x = element_text(hjust = -0.5),
panel.grid = element_blank(),
panel.grid.major.x = element_line(colour = "#BDBDBD",
linetype = "dotted")) +
labs(title = "Wanneer Trump zijn meest gebruikte woordparen in zijn tweets gebruikt",
subtitle = "Elk streepje is een tweet. De groter het streepje des te meer retweets.",
x = "",
y = "")
barcode_3
```
```{r tweet-analysis-bubble-2, fig.height=4.2}
plot_colours4 <- c("#001fb7", "#ba001f", "#b79900", "#00b799")
# Bubble plot where size is according to number of retweets and multiple words on their own line
candidate_bigrams %>%
mutate(bigram = as.factor(bigram)) %>%
filter(created_at > "2017-01-01",
bigram %in% c("fake news",
"north korea",
"tax cuts",
"failing nytimes")) %>%
ggplot(aes(x = created_at, y = bigram, fill = bigram, size = retweets)) +
geom_point(shape = 21,
alpha = 0.3,
stroke = F) +
scale_size(range = c(1, 12)) +
scale_x_datetime(date_labels = "%b", date_breaks = "1 month") +
scale_fill_manual(values = plot_colours4) +
theme_minimal() +
theme(legend.position = "none",
plot.background = element_blank(),
axis.text.x = element_text(hjust = -0.5),
panel.grid = element_blank(),
panel.grid.major.x = element_line(colour = "#BDBDBD",
linetype = "dotted")) +
labs(title = "Wanneer Trump zijn meest gebruikte woordparen in zijn tweets gebruikt",
subtitle = "Elke bubbel is een tweet. De groter de bubbel des te meer retweets.",
x = "",
y = "")
```