/
RedesSociales.Rmd
758 lines (685 loc) · 39.2 KB
/
RedesSociales.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
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
---
title: "Análisis de Redes Sociales Twitter"
author: "<center>[Parametría](http://parametria.com.mx/)</center>"
date: "<center>`r format(Sys.time(), '%d/%m/%Y')`</center>"
github: "Parametria/COVID-19-Opinion"
logo: "logo_gallery.png"
mail: lorenzoln@parametria.com.mx
lang: es
output:
html_document:
collapsed: no
css: '04_resources/style.css'
df_print: paged
include:
#after_body: '04_resources/footer.html'
in_header: '04_resources/header.html'
number_sections: no
theme: lumen
toc: yes
toc_depth: 1
toc_float: yes
pdf_document:
toc: no
toc_depth: '1'
keep_tex: false
extra_dependencies: ["xcolor", "booktabs", "makecell"]
params:
view_pdf: !r knitr::is_latex_output()
view_html: !r knitr::is_html_output()
---
```{block eval=FALSE, include=params$view_html}
<center>
<div class="pill-nav">
<a href='https://Parametria.github.io/COVID-19-Opinion/'><b>Encuesta COVID-19 Parametría</b></a>
<a class="active" href='https://Parametria.github.io/COVID-19-Opinion/RedesSociales.html'><b>Redes Sociales</b></a>
</div>
<br>
<b>
Si quiere descargar el reporte completo en pdf haga click [AQUÍ](https://Parametria.github.io/COVID-19-Opinion/RedesSociales.pdf)
</b>
<br>
<br>
Descargue el archivo RMarkdown para crear este reporte aquí
[Rmarkdown](https://github.com/Parametria/COVID-19-Opinion/blob/master/RedesSociales.Rmd)
</center>
<br>
```
***
El análisis de redes sociales es una buena herramienta para conocer y entender la opinión pública usuaria de redes sociales. Cabe señalar que, dado el contexto socioeconómico en nuestro país y la brecha tecnológica característica de los países en desarrollo, este grupo recoge características específicas que merece un tratamiento diferenciado de los trabajos demoscópicos realizados en vivienda o por teléfono.
<br>
\newline
En México solo 70 por ciento de la población tiene acceso a internet (80 millones de mexicanos). Este segmento es el más urbano, más escolarizado y con mayores recursos económicos. Por ello, no es representativo de la opinión pública en su conjunto. Por su perfil es muy probable que este segmento represente solo a la clase media. Sin embargo estos segmentos pueden anticipar frecuentemente lo que será un tema de conversación social más general en el futuro.
<br>
\newline
Del total de usuarios de internet casi 90% usa redes sociales; un poco más del 85% usa WhatsApp, ligeramente por abajo está Facebook (84%). Youtube lo utiliza sóoo una tercera parte de los usuarios de Internet (31%), Instagram ligeramente menos (27%). Finalmente, Twitter sólo 10% de los usuarios de internet.
<br>
\newline
Entre el público que accede a redes sociales la mayor parte de ellos dedican su tiempo a temas como deportes o espectáculos (alrededor de 85%). Es solo una minoría los que emplean las redes sociales para debatir los temas de vida pública. Twitter es probablemente la red social con mayor frecuencia de opiniones en vida pública. Por ello, nuestro análisis se enfoca en esta red social.
<br>
\newline
El presente trabajo se realiza con interfaces basadas en la API de Twitter, donde se filtra- en tiempo real- los tweets de interés para crear una base de datos propia analizable. A partir de este análisis se identifican los topics, hashtags y utilizadores más importantes para el seguimiento. Este análisis se basa en un scrapping personal de tweets en México que hacen mención de COVID-19.
\newpage
<br>
```{r echo=F, background="white", message = FALSE, warning = FALSE}
# extrafont::loadfonts(device="win")
require(rvest, quietly = T)
## remotes::install_github("wilkelab/ggtext")
require(ggtext, quietly = T)
## devtools::install_github("hadley/emo")
require(emo, quietly = T)
require(hrbrthemes, quietly = T)
require(tidytext, quietly = T)
require(emojifont)
require(ggrepel, quietly = T)
require(stringi, quietly = T)
require(ggthemes, quietly = T)
require(lubridate, quietly = T)
require(plotly, quietly = T)
require(RColorBrewer, quietly = T)
#require(hrbthemes, quietly = T)
require(tidyverse, quietly = T)
require(htmlwidgets, quietly = T)
require(knitr, quietly = T)
require(translateR, quietly = T)
require(htmltools, quietly = T)
load.emojifont("OpenSansEmoji.ttf")
load.emojifont("EmojiOne.ttf")
caption <- "Elaboración propia con datos de Twitter | <a href='https://twitter.com/Parametria'>@parametria</a>"
caption1 <- "Elaboración propia con datos de Twitter | Parametría"
saveWidgetFix <- function (widget,file,...) {
## A wrapper to saveWidget which compensates for arguable BUG in
## saveWidget which requires `file` to be in current working
## directory.
wd<-getwd()
on.exit(setwd(wd))
outDir<-dirname(file)
file<-basename(file)
setwd(outDir);
saveWidget(widget,file=file,...)
}
rm_words <-
function(string, words) {
stopifnot(is.character(string), is.character(words))
spltted <- strsplit(string, " ", fixed = TRUE) # fixed = TRUE for speedup
vapply(spltted, function(x) paste(x[!tolower(x) %in% words], collapse = " "), character(1))
}
# Aplicar str_wrap a objetos dentro de otras funciones (como scale_x_discrete(labels=equis))
strwrap_obj <- function(x) {
str_wrap(x, width = 10)
}
BigramTokenizer <-
function(x) {
unlist(lapply(ngrams(words(x), 2), paste, collapse = " "), use.names = FALSE)
}
if(!file.exists("01_datos/tweets.rds")){
tweets <- readRDS("01_datos/covid_20MAY20.rds") %>%
as_tibble() %>%
distinct(id_str, .keep_all = T)%>%
mutate(
hashtags = stri_extract_all(text, regex = "#[[:alnum:]_]+"),
ats = stri_extract_all(text, regex = "@[[:alnum:]_]+"),
emojis = stri_extract_all_charclass(text, "\\p{EMOJI}"),
texto = stri_replace_all(text, regex = "#[[:alnum:]_]+", ""),
texto = stri_replace_all(texto, regex = "@[[:alnum:]_]+", ""),
texto = stri_replace_all_charclass(texto, "\\p{EMOJI}", ""),
texto = gsub(texto, pattern = " ", replacement = "_"),
texto = gsub(texto, pattern = "[[:space:]]", replacement = ""),
texto = gsub(texto, pattern = "_", replacement = " "),
texto = gsub(texto, pattern = "ª", replacement = ""),
texto = gsub(texto, pattern = "[0-9]+|[[:punct:]]|\\(.*\\)", replacement = ""),
texto = gsub(texto, pattern = "https[^[:space:]]*", replacement = ""),
texto = gsub(texto, pattern = "[^[:alpha:][:space:]]*", replacement = ""),
texto = gsub(texto, pattern = "[[:punct:]]", replacement = ""),
texto = gsub(texto, pattern = " ", replacement = " "),
texto = trimws(texto),
texto = str_replace_all(texto, pattern = "rt",
replacement = ""),
fecha = as.POSIXct(created,
format = "%Y-%m-%d %T") - hours(7),
RT = stri_detect(text, fixed = "RT"),
replied_TO = ifelse(RT, stri_extract_first(text, regex = "@[[:alnum:]_]+"), "")
) %>% filter(fecha>as.POSIXct("2020-03-30", format = "%Y-%m-%d"))
saveRDS(tweets, "01_datos/tweets.rds")
} else {
tweets <- readRDS("01_datos/tweets.rds")
}
```
# <b>Tiempo</b>
***
```{r echo=F, message=F, warning=F}
minday <- format(min(tweets$fecha), "%d")
minmonth <- format(min(tweets$fecha), "%m")
maxday <- format(max(tweets$fecha), "%d")
maxmonth <- format(max(tweets$fecha), "%m")
months <- list(
"01"="enero",
"02"="febrero",
"03"="marzo",
"04"="abril",
"05"="mayo",
"06"="junio",
"07"="julio",
"08"="agosto",
"09"="septiembre",
"10"="octubre",
"11"="noviembre",
"12"="diciembre"
)
if(maxmonth == minmonth){
fectoprint <- paste(minday, "al", maxday,"de", months[minmonth])
} else {
fectoprint <- paste(minday,"de",months[minmonth] ,"al", maxday,"de", months[maxmonth])
}
```
Los datos sobre tweets hasta `r max(tweets$fecha)` tienen `r nrow(tweets)` tweets.
El gráfico de tiempo presenta el número de tweets que mencionan el #COVID19MX. Estos tweets se muestran según la hora de su publicación desde el `r fectoprint` de `r format(min(tweets$fecha),"%Y")`.
Es importante remarcar que los tweets tienen ciclos normales de creación determinado por las horas de descanso, así como la escalada esperada en los fines de semana. Este ejercicio permite evaluar la relevancia de un tema particular para la opinión pública usuaria de redes sociales y, subsecuentemente, entender su relación con eventos observados en un periodo de tiempo.
En la actualización de este reporte se incluyeron los tuits a partir del día 22 de mayo de 2020. Durante este periodo, se observa que entre los días 03 y el 04 de junio y 11 al 12 de junio hay un incremento en el número de tweets acumulados —sobre todo en el último periodo mencionado.
El primer punto, correspondiente a los días 03 y 04 de junio coincide con el anuncio de 1,092 defunciones al conteo de muertes por COVID-19. Si bien, estas defunciones no se contabilizaron en un solo día, éste fue el mayor incremento registrado a lo largo de la pandemia. Además, durante este periodo se superaron los 100 mil casos confirmados.
El segundo periodo, entre el 11 y 12 de junio podría tener varias explicaciones e implicaciones. Estos fueron los días previos a que se realizara una reapertura parcial de ciertas actividades económicas. Respecto a la comunicación oficial, las contradicciones entre el vocero de la pandemia, el subsecretario López-Gatell, y el presidente López Obrador pudieron haber contribuido a la confusión de la población: mientras el primero insistía en que las medidas de sana distancia y el quédate en casa debían continuar a pesar de la reapertura, el segundo hacía un llamado a que no se tuviera miedo de salir a la calle. En el plano local, el 12 de junio fue el día en el que se anunció la reapertura escalonada de algunas actividades en la capital del país —a pesar de que esta entidad concentraba la mayor proporción de casos activos y se encontraba en semáforo rojo. Las principales medidas anunciadas fueron la finalización del programa Hoy No Circula extendido, la reapertura total de estaciones de Metro y Metrobús y la reincorporación de las industrias manufacturera y aquellas relacionadas con actividades de construcción, minería y producción automotriz.
Otro elemento que vale la pena remarcar es que la tendencia respecto a este tema previo al 10 de junio tendía a la baja. Sin embargo, a partir de este día la discusión se reactivó a partir de un estímulo ocasionado por varios comunicados de autoridades públicas.
```{block eval=FALSE, include=params$view_html}
La imagen inferior que acompaña el gráfico permite elegir la temporalidad específica de su interés.
```
<br>
```{r out.width="100%", fig.height=7, fig.align='center', echo=F, background="white", message = FALSE, warning = FALSE, fig.cap="Línea de Tiempo de Tweets sobre COVID-19 en México - Twitter"}
titulo <- "Frecuencia de tweets que mencionan al #COVID19mx"
subtitulo <- "Tweets agrupados por hora del 31 de marzo al 20 de abril de 2020"
tweets_sum <- tweets %>%
group_by(month = month(fecha),day = day(fecha), hour = hour(fecha)) %>%
summarise(`Número de Tweets` = n(),
retweets = sum(as.numeric(retweet_count), na.rm = T ))%>%
mutate(Fecha= as.POSIXct(paste0(month," " ,day, " ", hour), format = "%m %d %H"))
ur <- ggplot(data = tweets_sum,
aes(size = `Número de Tweets`,
y = `Número de Tweets`,
color = `Número de Tweets`,
x = Fecha)) +
geom_smooth(method = "loess",
show.legend = F,
colour="black") +
geom_point() +
scale_color_continuous(NULL, NULL, NULL)+
scale_size(NULL, NULL, NULL)+
scale_x_datetime(date_breaks = "7 day", date_labels = "%b/%d")+
theme_ipsum(grid="Y") +
labs(title=str_wrap(titulo, width = 80),
subtitle = str_wrap(subtitulo, width = 80),
y="Número de tweets",
x="",
caption=caption1)+
theme(plot.title = element_text(size = 35),
plot.subtitle = element_text(size = 25),
plot.caption = element_text(size = 20),
axis.title.x = element_text(size = 20),
axis.text.y = element_text(size = 20),
axis.text.x = element_text(angle = 90))
ggsave("03_graficas/linea_tiempo_tweets.pdf", plot = ur,
width = 15, height = 10, dpi = 100)
ur <- ggplot(data = tweets %>%
group_by(month = month(fecha),day = day(fecha), hour = hour(fecha)) %>%
summarise(`Número de Tweets` = n(),
retweets = sum(as.numeric(retweet_count), na.rm = T ))%>%
mutate(Fecha= as.POSIXct(paste0(month," " ,day, " ", hour), format = "%m %d %H")),
aes(size = `Número de Tweets`,
y = `Número de Tweets`,
color = `Número de Tweets`,
x = Fecha)) +
geom_point() +
scale_color_continuous(NULL, NULL, NULL)+
scale_size(NULL, NULL, NULL)+
scale_x_datetime(date_breaks = "1 day", date_labels = "%b/%d %I:00%p")+
theme_ipsum(grid="Y") +
labs(title=str_wrap(titulo, width = 80),
subtitle = str_wrap(subtitulo, width = 80),
y="Número de tweets",
x="",
caption=caption1) +
theme(plot.title = element_text(size = 20),
plot.subtitle = element_text(size = 15),
plot.caption = element_text(size = 10),
axis.title.x = element_text(size = 10),
axis.text.y = element_text(size = 10),
axis.text.x = element_text(angle = 45))
if(params$view_html) {
plotly <- ggplotly(ur+theme(axis.text.x = element_text(size=8)),height=600,
tooltip = c("x", "y"),
dynamicTicks = TRUE
)%>%
rangeslider() %>%
layout(title = list(text = "",
y = 1.1),
hovermode = "x",
tickvalues ="",
annotations = list(x = 1, y = 0,
text = caption,
showarrow = F,
xref='paper',
yref='paper',
xanchor='right',
yanchor='auto',
xshift=0,
yshift=0,
font=list(size=15, color="red")))
saveWidgetFix(plotly,libdir = "graph_dependencies", selfcontained = F, file="03_graficas/linea_tiempo_tweets.html")
div(plotly, class="myplot", align = "center")
} else if(params$view_pdf ){
include_graphics(path ="03_graficas/linea_tiempo_tweets.png", auto_pdf = T)
}
```
```{block eval=FALSE, include=params$view_html}
[Link a esta gráfica](https://Parametria.github.io/COVID-19-Opinion/03_graficas/linea_tiempo_tweets.html)
```
```{block eval=FALSE, include=params$view_pdf}
[\underline{Link a esta gráfica}](https://Parametria.github.io/COVID-19-Opinion/03_graficas/linea_tiempo_sentimiento_tweets.png)
\newpage
```
# <b>Hashtags, Menciones y Temas</b> {.tabset .tabset-fade .tabset-pills}
***
Las nubes de palabras de hashtags, menciones y temas presentan las palabras y alusiones con mayor número de menciones en los tweets publicados del `r fectoprint`, relacionados al #COVID19MX. El tamaño de cada palabra en la nube alude a la mayor o menor frecuencia en que fue mencionada en las publicaciones.
<br>
```{block eval=FALSE, include=params$view_pdf}
Este ejercicio permite conocer los verbatismos más asociados con el tema de análisis y, con ello, analizar los constructos sociales que se crean en redes sociales respecto al mismo. Asimismo, esta información permite identificar aquellas figuras públicas o autoridades, así como movimientos detrás de los hashtags, a los que más alude la población usuaria de redes sociales con el fin de comunicar su interés particular en un tema.
```
<br>
```{r out.width="100%", fig.height=7, fig.align='center', echo=F, include=params$view_pdf, background="white", message = FALSE, warning = FALSE, fig.cap="Hashtags #, Arrobas y Temas - Twitter"}
require(kableExtra, quietly = T)
options(knitr.table.format = "latex")
dt <- readRDS("01_datos/tols.rds")
kable(dt, booktabs = T, col.names = c("Hashtags","Tweets","Temas", "Tweets", "Arrobas", "Tweets"))
```
```{r out.width="100%", fig.height=7, fig.align='center', echo=F, include=params$view_html, background="white", message = FALSE, warning = FALSE}
DT::datatable(dt,
class= "cell-border stripe",
rownames = F,
colnames=c("Hashtags","Tweets","Temas", "Tweets", "Arrobas", "Tweets"),
filter = "top",
extensions = "Buttons",
width = "100%",
options = list(dom="Bfrtip",
pageLength = 10,scrollX = T,
buttons = c("copy", "excel"),
autowidth = T)
)%>%
DT::formatStyle(columns = c(1:8), fontSize = '8pt')%>%DT::formatStyle(columns = 8, fontSize = '4pt')
```
```{block eval=FALSE, include=params$view_pdf}
\newpage
```
## <b>Temas</b>
***
```{block eval=FALSE, include=params$view_pdf}
La actualización de esta gráfica indica que las palabras relacionadas con las estrategias gubernamentales para prevenir la propagación del virus —tales como Sana Distancia— siguen vigentes al día de hoy. La palabra "fase" se incorpora al top 10, debido a la entrada a la fase 3 en el país.
```
```{r out.width="100%",fig.height=6, fig.align='center', echo=F, background="white", message = FALSE, warning = FALSE, fig.cap="Menciones más comunes en Tweets sobre COVID-19 en México - Twitter"}
include_graphics(path ="03_graficas/WC_topicos_twitter.png", auto_pdf = T)
```
```{block eval=FALSE, include=params$view_html}
[Link a esta gráfica](https://Parametria.github.io/COVID-19-Opinion/03_graficas/WC_topicos_twitter.html)
```
```{block eval=FALSE, include=params$view_pdf}
\newpage
```
## <b>Hashtags</b>
***
<b>Definición:</b> <i>Es una etiqueta de metadatos precedida de un carácter especial (normalmente un numeral #) con el fin de ser identificada de forma rápida. Se usa en servicios web tales como Twitter, Telegram, FriendFeed, Facebook, Google+, Instagram, Weibo para señalar o hacer hincapié en un tema sobre el que gira cierta conversación.</i>
\newline
<br>
```{block eval=FALSE, include=params$view_pdf}
Para este periodo de tiempo, la discusión pública en Twitter es dominada por aquellos hashtags relacionados con las recomendaciones gubernamentales para contener el contagio del virus (1. #QuedateEnCasa, 2. #SusanaDistancia). Sin embargo, el primer hashtag ya cuatruplica en número de menciones al segundo. La insistencia de que las personas no abandonen sus hogares, a pesar de la reapertura, para evitar la propagación del virus se ha convertido en la principal estrategia de comunicación política. En este tema destaca que Chimalhuacán se posicione como el tercer hashtag más mencionado, debido a un brote de COVID-19 en el Hospital Materno Infantil del municipio.
```
```{r out.width="100%",fig.height=6, fig.align='center', echo=F, background="white", message = FALSE, warning = FALSE, fig.cap="Menciones más comunes en Tweets sobre COVID-19 en México - Twitter"}
include_graphics(path ="03_graficas/WC_hashtags_twitter.png", auto_pdf = T)
```
```{block eval=FALSE, include=params$view_html}
[Link a esta gráfica](https://Parametria.github.io/COVID-19-Opinion/03_graficas/WC_hashtags_twitter.html)
```
```{block eval=FALSE, include=params$view_pdf}
\newpage
```
## <b>Menciones</b>
***
<b>Definición:</b> <i>Es una etiqueta de metadatos precedida de un carácter especial (normalmente una arroba @) con el fin de relacionar a un usuario específico en una conversación y notificarlo de la misma. Se usa en servicios web tales como Twitter, Telegram, FriendFeed, Facebook, Google+, Instagram, Weibo para establecer relación entre una conversación y un usuario o para marcar la respuesta hacia un mensaje específico de un usuario.</i>
\newline
<br>
```{block eval=FALSE, include=params$view_pdf}
Resulta interesante observar que la discusión pública en redes se relaciona fuertemente con las autoridades correspondientes. Para el periodo de tiempo analizado, el subsecretario Hugo López Gatell se coloca en la primera posición con 122 mil menciones. Ni siquiera el presidente, quien se posiciona en segundo lugar con un total de 72 mil, se le acerca en magnitud. Esta amplia socialización como el principal responsable durante la pandemia, podría jugarle para bien o para mal al subsecretario, dependiendo de los resultados que las y los ciudadanos perciban cuando la emergencia pase.
```
```{r out.width="100%",fig.height=6, fig.align='center', echo=F, background="white", message = FALSE, warning = FALSE, fig.cap="Menciones más comunes en Tweets sobre COVID-19 en México - Twitter"}
include_graphics(path ="03_graficas/WC_ats_twitter.png", auto_pdf = T)
```
```{block eval=FALSE, include=params$view_html}
[Link a esta gráfica](https://Parametria.github.io/COVID-19-Opinion/03_graficas/WC_menciones_twitter.html)
```
```{block eval=FALSE, include=params$view_pdf}
[\underline{Link a esta gráfica}](https://Parametria.github.io/COVID-19-Opinion/03_graficas/WC_menciones_twitter.pdf)
```
\newpage
# <b>Análisis de Sentimiento</b>
***
El gráfico de Análisis de sentimiento presenta un promedio de la orientación positiva o negativa de los tweets publicados del `r fectoprint`, relacionados al #COVID19MX.
Esta gráfica podría clasificarse en tres etapas: la incertidumbre de los primeros días, el optimismo generado por la promesa de una nueva normalidad, y la desilusión porque esta nueva normalidad no cumplió con las expectativas esperadas. Al día de hoy, vivir en durante una pandemia global se ha normalizado, pero esto no implica que necesariamente lo asumamos con gusto. Nuestros hábitos han cambiado, como lo muestra nuestra última medición telefónica nacional, pero la nostalgia de la vida antes de pandemia es algo que, al menos en el sentimiento de las y los tweeteros, se puede observar.
\newline
<br>
```{block eval=FALSE, include=params$view_html}
Con un sistema automatizado se asigna un puntaje a cada palabra en los tweets de la base de datos, lo cual obtiene un puntaje general por tweet. Con estos puntajes se obtiene un promedio de puntuación de los tweets por hora, y subsecuentemente construir el “sentimiento promedio” del día. El puntaje se posiciona en una escala de números reales (-∞, +∞), donde los valores positivos significan un “sentimiento promedio positivo”, los números negativos significan un “sentimiento promedio negativo” y el número cero significa un “sentimiento promedio neutral”.
<br>
Cada círculo en el gráfico representa un día y su tamaño representa el volumen de tweets encontrados. La imagen inferior que acompaña el gráfico permite elegir la temporalidad específica de su interés.
<br>
```
```{block eval=FALSE, include=params$view_pdf}
El texto de cada tweet puede ser positivo una vez que menciona palabras con este carácter, tales como: bueno, recuperación, cura, entre otras.
\newline
Por el contrario, el texto de cada tweet puede ser negativo una vez que menciona palabras con este carácter, tales como: malo, muerte, tristeza, entre otros. Este sistema automatizado permite asignar un puntaje para cada palabra en los tweets de la base de datos, lo cual obtiene un puntaje general por tweet. Con estos puntajes se obtiene un promedio de puntuación de los tweets por hora, y subsecuentemente construir el “sentimiento promedio” del día.
\newline
El puntaje se posiciona en una escala de números reales $(-\infty, +\infty)$, donde los valores positivos significan un “sentimiento promedio positivo”, los números negativos significan un “sentimiento promedio negativo” y el número cero significa un “sentimiento promedio neutral”. Cada círculo en el gráfico representa un día y su tamaño representa el volumen de tweets encontrados.
```
<br>
```{r echo=F, message = FALSE, warning = FALSE}
afinn <- readRDS("01_datos/DiccionarioUNAfin.rds")%>%
filter(!is.na(mean)) %>% rename(Puntuacion = mean)
desc <- afinn %>% distinct(root, .keep_all = T) %>%
select(-Palabra)%>%arrange(desc(Puntuacion))
asce <- afinn %>% distinct(root, .keep_all = T)%>%
select(-Palabra)%>%arrange(Puntuacion)
```
```{r out.width="100%", fig.height=8, fig.align='center', echo=F, background="white", message = FALSE, warning = FALSE, fig.cap="Análisis de Sentimiento en Tweets sobre COVID-19 en México - Twitter"}
general_afinn <- tweets%>%select(id_str, texto, fecha)%>%
mutate(id = 1:length(texto),
id = formatC(id, width = 3, format="d", flag="0")) %>%
unnest_tokens(input = "texto", output = "Palabra") %>%
inner_join(afinn,., by = "Palabra") %>%
#mutate(Tipo = ifelse(Puntuacion > 0, "Positiva", "Negativa")) %>%
group_by(month = month(fecha), day = day(fecha), hour = hour(fecha), id) %>%
summarise(puntuacion = sum(Puntuacion)) %>%
group_by(month, day, hour) %>%
summarise(puntuacion = mean(puntuacion),
n = n()) %>%
mutate(puntuacion = ifelse(is.na(puntuacion), 0, puntuacion),
colour = ifelse(puntuacion>0,"positivo",
ifelse(puntuacion<0,"negativo","neutral")),
alpha = abs(puntuacion))%>%
ungroup()%>%
mutate(Fecha = as.POSIXct(paste0(month, " " ,day, " ", hour), format = "%m %d %H"),
`Número de tweets` = n)
titulo <- "Análisis de sentimiento en tweets de COVID19"
subtitulo1 <- "Cada círculo representa un día; el tamaño del círculo indica la cantidad de tweets encontrados por día. Una puntuación mayor a cero representa un sentimiento promedio positivo; una menor, un negativo."
subtitulo <- "Cada círculo representa un día; el tamaño del círculo indica la cantidad de tweets encontrados por día. <br>Una puntuación mayor a cero representa un sentimiento promedio positivo; una menor, un negativo."
ur <- ggplot(general_afinn ,
aes(x = Fecha,
y = puntuacion,
col = colour,
size=`Número de tweets`)) +
geom_point()+
scale_x_datetime(date_breaks = "3 day", date_labels = "%b/%d %I%p")+
scale_color_manual(values = c("#FC4E07", "grey", "#00AFBB")) +
scale_size_continuous("Sentimiento",
guide = guide_legend(override.aes = list(colour = "#E7B800"))) +
guides(color = F) +
xlab("") + ylab("Puntuación") +
labs(title = str_wrap(titulo, width = 90),
subtitle= str_wrap(subtitulo1, width = 90),
caption=caption1,
x="Tiempo",
y="Sentimiento") +
theme_ipsum() +
scale_fill_distiller("",palette="Spectral") +
theme(plot.title = element_text(size = 25),
plot.subtitle = element_text(size = 15),
plot.caption = element_text(size = 12),
legend.key.size = unit(1, "cm"),
legend.title = element_text(size = 10),
legend.text = element_text(size = 10),
axis.title.x = element_text(size = 15),
axis.text.y = element_text(size = 15),
axis.text.x = element_text(angle = 90))
ggsave(filename = "03_graficas/linea_tiempo_sentimiento_tweets.pdf",plot = ur + geom_smooth(method="loess", show.legend = F, colour="black") + geom_point(),
width = 15, height = 10, dpi = 100)
if(is_html_output()) {
plotly <- plotly::ggplotly(ur+theme(axis.text.x = element_text(size=8)),height=600,
tooltip = c("x", "y", "size"),
dynamicTicks = TRUE
)%>%
plotly::rangeslider() %>%
plotly::layout(title = "",
hovermode = "x",
tickvalues ="",
legend = list(orientation = 'v', x=1, y=.5),
annotations = list(x = 1, y = 0,
text = caption,
showarrow = F,
xref='paper',
yref='paper',
xanchor='right',
yanchor='auto',
xshift=0,
yshift=0,
font=list(size=15, color="red")))
saveWidgetFix(plotly,selfcontained = F, file="03_graficas/linea_tiempo_sentimiento_tweets.html")
div(plotly, class="myplot", align = "center")
} else if(is_latex_output()) {
include_graphics(path ="03_graficas/linea_tiempo_sentimiento_tweets.pdf")
}
```
```{block eval=FALSE, include=params$view_html}
[Link a esta gráfica](https://Parametria.github.io/COVID-19-Opinion/03_graficas/linea_tiempo_sentimiento_tweets.html)
```
```{block eval=FALSE, include=params$view_pdf}
[\underline{Link a esta gráfica}](https://Parametria.github.io/COVID-19-Opinion/03_graficas/linea_tiempo_sentimiento_tweets.png)
```
\newpage
## Diez palabras positivas y 10 negativas
Ejemplo de Diccionario para Sentimiento
```{r out.width="100%", fig.height=7, fig.align='center', echo=F, include=params$view_pdf, background="white", message = FALSE, warning = FALSE, fig.cap="Hashtags #, Arrobas y Temas - Twitter"}
dt <- bind_cols(head(asce%>%mutate(Puntuacion = round(Puntuacion, 1))%>%filter(root != "coño", root != "polla"), 10), head(desc%>%mutate(Puntuacion = round(Puntuacion, 1)), 10))
kable(dt, booktabs = T, col.names = c("Negativas","Puntuación","Positivas", "Puntuación"))
```
```{r out.width="100%", fig.height=7, fig.align='center', echo=F, include=params$view_html, background="white", message = FALSE, warning = FALSE}
DT::datatable(dt,
class= "cell-border stripe",
rownames = F,
colnames=c("Negativas","Puntuación","Positivas", "Puntuación"),
filter = "top",
extensions = "Buttons",
width = "100%",
options = list(dom="Bfrtip",
pageLength = 10,scrollX = T,
buttons = c("copy", "excel"),
autowidth = T)
)%>%
DT::formatStyle(columns = c(0:3), fontSize = '8pt')
```
\newpage
# <b>¡Emojis!</b>
***
```{block eval=FALSE, include=params$view_pdf}
Debido a la propia extensión de un tweet, una forma no estructurada de expresión reside en las figuras que pueden acompañar el texto. Esta información se muestra en el gráfico de Emojis.
```
Este gráfico presenta el número de veces en que el emoji específico fue usado en los tweets publicados del `r fectoprint`, relacionados al #COVID19MX.
```{block eval=FALSE, include=params$view_pdf}
Este ejercicio es un primer acercamiento a entender la expresión no estructurada como un termómetro de la opinión pública en redes sociales en un tema específico.
```
```{r out.width="100%",fig.height=6, fig.align='center', echo=F, background="white", message = FALSE, warning = FALSE, fig.cap="Emojis más comunes en Tweets sobre COVID-19 en México - Twitter"}
`%notin%` <- Negate(`%in%`)
if(!file.exists("01_datos/emojis.rds")){
emojis <- tweets %>% select(id_str, emojis)%>%unnest(cols = emojis)%>%
drop_na() %>%group_by(emojis) %>% summarise(n = n()) %>% arrange(desc(n))%>%
filter(is.na(as.numeric(emojis)),!str_detect(emojis, "#"),emojis %notin% c("*","!!","‼"))
emoji_to_link <- function(x) {
paste0("https://emojipedia.org/emoji/",x) %>%
read_html() %>%
html_nodes("tr td a") %>%
.[1] %>%
html_attr("href") %>%
paste0("https://emojipedia.org/", .) %>%
read_html() %>%
html_node('div[class="vendor-image"] img') %>%
html_attr("src")
}
emoji_to_description <- function(x) {
paste0("https://emojipedia.org/emoji/",x) %>%
read_html() %>%
html_nodes("tr td a") %>%
.[1] %>%
html_attr("href") %>%
paste0("https://emojipedia.org/", .) %>%
read_html() %>%
html_node('section[class="description"]') %>%
html_text()
}
link_to_img <- function(x, size = 25) {
paste0("<img src='", x, "' width='", size, "'/>")
}
thead_emojis <- head(emojis, 30)%>%
mutate(ems1 = stri_extract_all(emojis, regex = "\\p{Emoji_Presentation}"),
ems1 = map(ems1, unique),
len = as.double(map(ems1, length)),
len = ifelse(is.na(ems1), NA_real_, len),
ems2 = stri_extract_all(emojis, regex = "\\p{Emoji_Modifier}"),
ems3 = stri_extract_all(emojis, regex = "\\p{Emoji_Modifier_Base}"),
ems4 = stri_extract_all(emojis, regex = "\\p{Emoji_Component}"),
ems4 = ifelse(is.na(ems1), emojis, NA_character_),
count= stri_count(emojis, regex = "\\p{Emoji}"),
count1=stri_count(emojis, regex = "\\p{Emoji_Presentation}"),
count2=stri_count(emojis, regex = "\\p{Emoji_Modifier}"),
count3=stri_count(emojis, regex = "\\p{Emoji_Modifier_Base}"),
count4=stri_count(emojis, regex = "\\p{Emoji_Component}"),
countif = case_when(
count==1 | len==1| count1 == 1| count2 == 1| count3 == 1| count4 == 1 ~ as.integer(1),
!is.na(ems4) ~ stri_count(emojis, regex="\\p{Emoji}"),
T ~ as.integer(count)
),
finalems = ifelse(count==1, emojis, NA),
finalems = ifelse(ifelse(is.na(len), F, len == 1), ems1, finalems),
finalems = ifelse(countif>1, emojis, finalems),
finalems = ifelse(is.na(finalems), ifelse(countif==1, ems3, finalems), finalems),
)%>%
transmute(family = ifelse(is.na(ems4), "OpenSansEmoji" , "EmojiOne"),
emojis = finalems,
n = n)
thead_emojis <- thead_emojis %>%
mutate(url = map_chr(emojis, slowly(~plyr::try_default(emoji_to_link(.x), NA, quiet = T), rate_delay(1))),
descr = map_chr(emojis, slowly(~plyr::try_default(emoji_to_description(.x), NA, quiet = T), rate_delay(1))),
label = link_to_img(url))
thead_emojis <- thead_emojis%>%
mutate(emojis = ifelse(is.na(url),stri_extract_all(emojis, regex = "\\p{Emoji_Presentation}"), emojis))%>%
unnest(emojis)%>%
mutate(url = ifelse(is.na(url),map_chr(emojis, slowly(~plyr::try_default(emoji_to_link(.x), NA), rate_delay(1))), url),
descr = ifelse(is.na(descr),map_chr(emojis, slowly(~plyr::try_default(emoji_to_description(.x), NA), rate_delay(1))), descr),
label = link_to_img(url),
`Número de Tweets`=n
)
key <- jsonlite::read_json("apikey/contraseñaTranslateEmojis.json")
thead_emojis <- translate(dataset = thead_emojis,content.field = "descr", google.api.key = key$key, source.lang = "en", target.lang = "es") %>%
mutate(
descr = stri_replace_last(translatedContent, fixed = " Copie y pegue este emoji: Copiar", ""),
descr = gsub('"', '', descr)
) %>% select(-translatedContent)
# %>%
# mutate(
# descr = stri_replace_all(descr, fixed = "á", replacement = "á"),
# descr = stri_replace_all(descr, fixed = "é", replacement = "é"),
# descr = stri_replace_all(descr, fixed = "ó", replacement = "ó"),
# descr = stri_replace_all(descr, fixed = "ú", replacement = "ú"),
# descr = stri_replace_all(descr, fixed = "ñ", replacement = "ñ"),
# descr = stri_replace_all(descr, fixed = "Ã<8d>", replacement = "Í"),
# descr = stri_replace_all(descr, fixed = "Ã", replacement = "í"),
# descr = stri_replace_all(descr, fixed = "–", replacement = "-"),
# descr = stri_replace_all(descr, fixed = "°", replacement = "º"),
# descr = stri_replace_all(descr, fixed = "📄 ", replacement = ""),
#
#
# )
saveRDS(thead_emojis, "01_datos/emojis.rds")
} else {
thead_emojis <- readRDS("01_datos/emojis.rds")
thead_emojis2 <- read_csv2("01_datos/emojis.csv")
thead_emojis$descr <- thead_emojis2$descr
thead_emojis <- thead_emojis%>%
mutate(
descr = stri_replace_all(descr, regex= "\\<U\\+[:alnum:]+\\>", "")
)
}
data_emojis <- thead_emojis%>%group_by(emojis)%>%summarise_all(~ifelse(is.numeric(.), sum(.), max(.)))
titulo <- "Emojis más usados en menciones de COVID-19 en Twitter"
ur1 <- ggplot(head(data_emojis,20), aes(x=reorder(emojis,desc(`Número de Tweets`) ), y=`Número de Tweets`, fill = emojis, label = label, family = family)) +
geom_bar(stat = "identity") +
geom_richtext(label.color = NA, label.padding = grid::unit(rep(0, 4), "pt"), size = 5, vjust = 1) +
scale_fill_discrete(NULL, NULL, NULL)+
labs(title=str_wrap(titulo, width = 70),
y="Número de tweets",
x="",
caption=caption1) +
theme_minimal()+
#scale_x_discrete(labels = emojis) +
theme(axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank())
ggsave(filename = "03_graficas/04_emojis_twitter.pdf",plot = ur1,
width = 15, height = 10, dpi = 100)
subtitulo <- paste0("#COVID19mx Tweets del 15 de marzo al ", as.Date(max(tweets$fecha)))
ur2 <- ggplot(head(data_emojis,20),
aes(x=reorder(emojis,desc(`Número de Tweets`)),
y=`Número de Tweets`,
fill = emojis,
label = emojis,
text = str_wrap(descr,30),
family = "SEGUIEMJ")) +
geom_bar(stat = "identity") +
geom_text(size = 5, vjust = 1) +
guides(fill = F)+
scale_fill_discrete(NULL, NULL, NULL)+
labs(title=str_wrap(titulo, width = 70),
y="Número de tweets",
x="") +
theme_minimal()+
#scale_x_discrete(labels = emojis) +
theme(axis.title.x=element_blank(),
axis.text.x=element_blank(),
axis.ticks.x=element_blank(),
legend.position = "none")
if(is_html_output()) {
plotly <- plotly::ggplotly(ur2+theme(axis.text.x = element_text(size=8)),height=600, tooltip = c("label", "y", "text"))%>%
plotly::layout(title = "",
annotations = list(x = 1, y = 0,
text = caption,
showarrow = F,
xref='paper',
yref='paper',
xanchor='right',
yanchor='auto',
xshift=0,
yshift=0,
font=list(size=15, color="red")))
saveWidgetFix(plotly,libdir = "graph_dependencies", selfcontained = F, file="03_graficas/Emojis_twitter.html")
div(plotly, class="myplot", align = "center")
} else if(is_latex_output()) {
include_graphics(path ="03_graficas/04_emojis_twitter.pdf")
}
```
```{block eval=FALSE, include=params$view_html}
[Link a esta gráfica](https://Parametria.github.io/COVID-19-Opinion/03_graficas/Emojis_twitter.html)
```
```{block eval=FALSE, include=params$view_pdf}
[\underline{Link a esta gráfica}](https://Parametria.github.io/COVID-19-Opinion/03_graficas/Emojis_twitter.pdf)
\newpage
```
```{block eval=FALSE, include=params$view_html}
# Info
***
Revise el acervo histórico de reportes [aquí](https://github.com/Parametria/COVID-19-Opinion/archivo_historico).
<br>
Consulta el artículo publicado en Milenio por nuestro director, Francisco Abundis
[aquí](https://www.milenio.com/opinion/francisco-abundis/columna-francisco-abundis/redes-sociales-en-epoca-de-pandemia)
<br>
Descargue el archivo RMarkdown para crear este reporte aquí
[Rmarkdown](https://github.com/Parametria/COVID-19-Opinion/)
<br>
También puedes descargar el tema de este Rmd [aquí](https://holtzy.github.io/Pimp-my-rmd/).
```
```{block eval=FALSE, include=params$view_pdf}
Descargue los archivo RMarkdown para crear este reporte aquí [\underline{Rmarkdown}](https://github.com/Parametria/COVID-19-Opinion).
Consulta el artículo publicado en Milenio por nuestro director, Francisco Abundis
[\underline{aquí}](https://www.milenio.com/opinion/francisco-abundis/columna-francisco-abundis/redes-sociales-en-epoca-de-pandemia)
```