En este tutorial vamos a aprender lo basico del manejo de texto con la librería stringR
.
El propósito de esta sesión es el capacitar al interesado en las herramientas que provee R para el manejo de texto en general y para sacar el máximo aprovechamiento de sus bases obtenidas por las consultas de la API-EMIS del LNPP en particular.
En esta sesión se muestra un ejemplo la manipulación necesaria para la limpieza y la exploración de una consulta de noticias mediante la API antes mencionada.
La base de datos consiste en una busqueda de noticias relacionadas con lluvias e inundaciones en la Ciudad de México a lo largo de los años de registro.
NOTA: En la presente página no se muestran bases de datos ni hay forma de descargarlos. Sin embargo, en caso de que haya alguna queja sobre la presente página favor de avisar al autor. El propósito de este trabajo es meramente educativo.
Instalamos librerías y verificamos que se tenga el mismo Locale
en todas las computadoras de la sala.
library(pacman)
p_load(rebus, stringr, tidyverse, htmltools, readr)
# Librerias
# library(rebus)
# library(stringr)
# library(tidyverse)
# Fijamos el Locale
# CHECAR QUE TODOS TENGAN ESTO!!
Sys.setlocale("LC_ALL", 'en_US.UTF-8')
## [1] "en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8"
default_locale() # Verificar que sean los mismos!
## <locale>
## Numbers: 123,456.78
## Formats: %AD / %AT
## Timezone: UTC
## Encoding: UTF-8
## <date_names>
## Days: Sunday (Sun), Monday (Mon), Tuesday (Tue), Wednesday (Wed),
## Thursday (Thu), Friday (Fri), Saturday (Sat)
## Months: January (Jan), February (Feb), March (Mar), April (Apr), May
## (May), June (Jun), July (Jul), August (Aug), September
## (Sep), October (Oct), November (Nov), December (Dec)
## AM/PM: AM/PM
# <locale>
# Numbers: 123,456.78
# Formats: %AD / %AT
# Timezone: UTC
# Encoding: UTF-8
# <date_names>
# Days: Sunday (Sun), Monday (Mon), Tuesday (Tue), Wednesday (Wed), Thursday (Thu), Friday (Fri),
# Saturday (Sat)
# Months: January (Jan), February (Feb), March (Mar), April (Apr), May (May), June (Jun), July
# (Jul), August (Aug), September (Sep), October (Oct), November (Nov), December
# (Dec)
# AM/PM: AM/PM
La base cuenta con las siguientes variables:
X1
, contador (no sirve de nada)
fecha
, la fecha en que se publicó el artículo
periodico
, el periódico en el que se publicó la nota
format
, el formato en el que se encuentra la información (HTML, página web)
title
, el título de la nota
abstract
, un resumen de la nota periodística
body
, el texto de la nota periodística en formato HTML
pages
, el número de páginas en el que se encuentra la nota periodistica
size
, el numero de palabras que tiene cada nota
texto
, el texto plano de la nota
A continuación, exploraremos la base de datos:
# 1. Leemos los datos con readr
datos <- readr::read_csv("Noticias_inundaciones.csv") # Podemos ver el tipo de Variable
## Parsed with column specification:
## cols(
## fecha = col_datetime(format = ""),
## periodico = col_character(),
## id = col_double(),
## format = col_character(),
## title = col_character(),
## abstract = col_character(),
## body = col_character(),
## pages = col_double(),
## size = col_double(),
## texto = col_character()
## )
head(datos) # Primeros 10 registros
## # A tibble: 6 x 10
## fecha periodico id format title abstract body pages
## <dttm> <chr> <dbl> <chr> <chr> <chr> <chr> <dbl>
## 1 1998-12-07 00:00:00 El Unive… 1.28e7 HTML CARB… "El gob… "<!U… 1
## 2 1998-10-15 00:00:00 El Unive… 1.25e7 HTML CARD… "POR JO… "<!U… 1
## 3 1998-10-05 00:00:00 El Unive… 1.29e7 HTML SALC… "Para e… "<!U… 1
## 4 1998-10-04 00:00:00 El Unive… 1.24e7 HTML CARD… "Las ca… "<!U… 1
## 5 1998-09-30 00:00:00 El Unive… 1.28e7 HTML CARD… "El jef… "<!U… 1
## 6 1998-09-29 00:00:00 El Unive… 1.29e7 HTML LLUV… "El gob… "<!U… 1
## # … with 2 more variables: size <dbl>, texto <chr>
names(datos) # Nombre de las variables
## [1] "fecha" "periodico" "id" "format" "title"
## [6] "abstract" "body" "pages" "size" "texto"
dim(datos) # Dimensiones de la tabla
## [1] 2521 10
# 2. Vemos los datos
View(datos)
# EXPLORACION DE LOS DATOS!
# 3. Elaboramos una funcion exploratoria y exploramos las variables
niveles <- function(x) levels(as.factor(x)) # Funcion Propia :9
niveles(datos$periodico) # 13 periodicos
## [1] "DPA - Political News"
## [2] "El Economista - Online Edition"
## [3] "El Economista - Print Edition"
## [4] "El Financiero - Financial Newspaper"
## [5] "El Universal - Financial News"
## [6] "El Universal - International News"
## [7] "El Universal - National News"
## [8] "El Universal-Mexico City and States"
## [9] "Excelsior - Print Edition"
## [10] "Excelsior Online -Newspaper"
## [11] "Milenio Diario D.F."
## [12] "National and International News"
## [13] "Reforma Newspaper - Daily News"
niveles(datos$format) # Solo HTML
## [1] "HTML"
niveles(datos$title)[1:10] # 1053 titulos
## [1] "¡aguas!"
## [2] "¡ORÍLLESE A LA GRILLA!"
## [3] "¡Y se le cumplió a la gente!"
## [4] "¿por qué nos inundamos?"
## [5] "¿sabe o no sabe?"
## [6] "'Beatriz' se aleja <span style=\"color: red\">de</span> la costa mexicana"
## [7] "'Chocan' proyectos <span style=\"color: red\">de</span> Conagua y del DF"
## [8] "'Desnivelan' la <span style=\"color: red\">Ciudad</span>"
## [9] "'Encienden' los problemas"
## [10] "'ese muro ya no se cae': afirma mario palacios"
niveles(datos$body)[1] # Es codigo html! Lo podemos # visualizar con el str_view()
## [1] "\n<!UNIDFN3>\n<h3>Sobreexplotación de mantos acuíferos genera hundimientos</h3>\nPOR YETLANECI ALCARAZ MEXICO, D.F., noviembre 6 (EL UNIVERSAL).- La <span style=\"color: red\">ciudad</span> <span style=\"color: red\">de</span> <span style=\"color: red\">México</span> y su zona metropolitana enfrentan serios hundimientos, que llegan a ser hasta de 30 centímetros anuales en algunas áreas y que dan como resultado severos daños a la infraestructura hidráulica capitalina.<p> Esto es el resultado de la sobreexplotación de los acuíferos del valle de México, pues dos tercios del agua potable que se consume en el Distrito Federal se extrae de ellos.<p> Autoridades de la Comisión Nacional del Agua (CNA) y del Sistema de Aguas de la <span style=\"color: red\">Ciudad</span> <span style=\"color: red\">de</span> <span style=\"color: red\">México</span>, así como investigadores de la Universidad Nacional Autónoma de México (UNAM), han advertido el problema que ello representa para el valle de México.<p> \"No podemos ni técnica, ni política, ni socialmente seguir extrayendo más agua (del subsuelo), el aeropuerto y toda la zona oriente registran hundimientos anuales de 30 centímetros y que se traduce en daños severos a la infraestructura\", dijo el subdirector general de Infraestructura Hidráulica Urbana de la CNA, Jesús Campos.<p> Por su parte, el Programa de Gestión Integral de los Recursos Hidráulicos 2004-2009 para el Distrito Federal advierte sobre los riesgos de <span style=\"color: red\">inundación</span> debido a que no se brinda mantenimiento adecuado al drenaje de la ciudad.<p> Más aún, como otra consecuencia de la sobreexplotación de los mantos acuíferos, el Sistema de Aguas de la <span style=\"color: red\">Ciudad</span> <span style=\"color: red\">de</span> <span style=\"color: red\">México</span> ha reconocido que pozos que abastecen el oriente de la ciudad han tenido que ser cerrados por la mala calidad del agua que de ahí sale.<p> El director ejecutivo de Planeación y Construcción del Sistema de Aguas, Juan Carlos Guasch, reveló que unos 15 pozos se han dejado de explotar en la ciudad.<p> \"Existen problemas importantes en torno a la calidad del agua que se extrae del acuífero (particularmente de la zona sureste de la ciudad) y estos problemas aumentarán si no se controlan las descargas de contaminantes al suelo y se evita la sobreexplotación del acuífero\", dijo.<p> El Distrito Federal tiene tres fuentes de abastecimiento de agua potable: los mantos acuíferos de su subsuelo y los sistemas Lerma y Cutzamala.S080 \n<p>\n\n\n\n"
# Ver el HTML
str_view(datos$body[1], pattern = " ")
# Seguimos explorando
niveles(datos$pages) # Numero de paginas de cada articulo (varía de 1 a 25 pags)
## [1] "1" "2" "3" "4" "5" "6" "7" "9" "10" "13" "25"
datos$size[1:10] # Numero de palabras que tiene cada articulo
## [1] 2385 5552 4553 3461 2817 2125 2817 2743 3933 846
str_length(datos$body)[1:10] # Longitud de las primeras 10 palabras
## [1] 2439 5568 4579 3606 2903 2233 2903 2840 3968 949
Una pregunta lógica que podemos hacernos es saber si hay títulos repetidos en la base de datos. El que haya títulos repetidos nos dice que hay notas que, o están duplicadas, o hablan de los mismos temas (aunque esto no es necesariamente cierto).
Con el siguiente código exploraremos si hay notas repetidas.
# Forma Facil
table(datos$title[duplicated(datos$title)]) %>% head()
##
## Agenda Confidencial
## 1
## Al día, 25 quejas por tarifas <span style="color: red">de</span> agua
## 1
## Atienden unidades <span style="color: red">de</span> emergencia 102 encharcamientos por lluvia
## 1
## Capital federal
## 1
## CAPITANES
## 1
## CARBAJAL INVERSION CHIAPAS
## 16
En la tabla anterior, tenemos notas repetidas que posiblemente nos esten dando la misma información, sin embargo, están desordenadas. Una forma facil de generar una tabla donde los títulos están ordenados de mayor a menor repetición es mediante el siguiente código:
# Forma tidy
datos$title[duplicated(datos$title)] %>%
as_tibble() %>%
group_by(value) %>%
summarise(n = n()) %>%
arrange(desc(n))
## Warning: Calling `as_tibble()` on a vector is discouraged, because the behavior is likely to change in the future. Use `enframe(name = NULL)` instead.
## This warning is displayed once per session.
## # A tibble: 56 x 2
## value n
## <chr> <int>
## 1 Pronostico del tiempo 192
## 2 Pronóstico del tiempo 189
## 3 CARBAJAL INVERSION CHIAPAS 16
## 4 Pronóstico del clima 4
## 5 CARDENAS LLUVIAS DF 3
## 6 Columnas: CORPORATIVO 3
## 7 "Cronista <span style=\"color: red\">de</span> guardia" 3
## 8 LLUVIA DISTRITO FEDERAL 3
## 9 Un Vistazo 3
## 10 Desde la región más transparente 2
## # … with 46 more rows
Como podemos observar, Pronostico del tiempo
se repite demasiadas veces y no aporta a nuestro objetivo principal (el conocer los eventos e inundación en la Ciudad de México). Igualmente, CARBAJAL INVERSIÓN CHIAPAS
ni Columnas: CORPORATIVO
se ven de mucha utilidad, y sin embargo, se repiten bastante. Nos haremos cargo posteriormente de estos casos, por ahora lo dejaremos así.
# 1. Detectamos que celdas tienen Economista o El Universal o el EXCELSIOR como periodico Principal
niveles(datos$periodico)
## [1] "DPA - Political News"
## [2] "El Economista - Online Edition"
## [3] "El Economista - Print Edition"
## [4] "El Financiero - Financial Newspaper"
## [5] "El Universal - Financial News"
## [6] "El Universal - International News"
## [7] "El Universal - National News"
## [8] "El Universal-Mexico City and States"
## [9] "Excelsior - Print Edition"
## [10] "Excelsior Online -Newspaper"
## [11] "Milenio Diario D.F."
## [12] "National and International News"
## [13] "Reforma Newspaper - Daily News"
# 2. Detectamos los patrones
# Approach: Eliminando todo lo que este despues del signo
#pat_guion <- "-" %R% captura(WRD %R% SPC) %R% END
pat_guion <- "-([\\w\\s]+)$"
str_view(datos$periodico[1:15], pat_guion, match = T)
datos$periodico <- str_remove_all(datos$periodico, pattern = pat_guion)
niveles(datos$periodico)[1:10] # Se repite el UNIVERSAL! Por que?
## [1] "DPA " "El Economista "
## [3] "El Financiero " "El Universal"
## [5] "El Universal " "Excelsior "
## [7] "Excelsior Online " "Milenio Diario D.F."
## [9] "National and International News" "Reforma Newspaper "
# Rehacemos el patron
#pat_guion <- optional(SPC) %R% "-" %R% captura(WRD %R% SPC) %R% END
pat_guion <- '[\\s]?-([\\w\\s]+)$'
datos$periodico <- str_remove_all(datos$periodico, pattern = pat_guion)
niveles(datos$periodico) # Se repite el UNIVERSAL! Por que?
## [1] "DPA " "El Economista "
## [3] "El Financiero " "El Universal"
## [5] "El Universal " "Excelsior "
## [7] "Excelsior Online " "Milenio Diario D.F."
## [9] "National and International News" "Reforma Newspaper "
# R: El espacio del final... como lo eliminamos?
#pat_espacio_final <- SPC %R% END
pat_espacio_final <- "\\s$"
datos$periodico <- str_remove_all(datos$periodico, pattern = pat_espacio_final)
# Y si en vez de DPA queremos volver a tener DPA-NEWS?
datos$periodico <- str_replace_all(datos$periodico, pattern = "DPA", replacement = "DPA - Political News")
datos$periodico <- str_replace_all(datos$periodico, pattern = "Excelsior Online", replacement = "Excelsior")
niveles(datos$periodico)
## [1] "DPA - Political News" "El Economista"
## [3] "El Financiero" "El Universal"
## [5] "Excelsior" "Milenio Diario D.F."
## [7] "National and International News" "Reforma Newspaper"
# Visualizamos datos
ggplot(data = datos, aes(x = periodico)) + geom_bar() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
#ggplot(data = datos, aes(x = periodico, fill = periodico)) + geom_bar() + theme_minimal() + scale_color_brewer("Blues")
Como pudimos ver en el Problema 1, hay muchas noticias repetidas y que no aportan información útil al problema. Utilizaremos la librería stringr
para poder eliminar estas noticias de nuestra base de datos.
##################################################################
# ELIMINAR NOTICIAS Y QUEDARME SOLO CON LAS QUE DICEN INUNDACION #
##################################################################
# Tidy repaso! Vemos las noticias repetidas!
FREC <- datos$title %>%
as_data_frame() %>%
group_by(value) %>%
summarise(n = n()) %>%
arrange(desc(n))
# Visualizamos
FREC
## # A tibble: 2,053 x 2
## value n
## <chr> <int>
## 1 Pronostico del tiempo 193
## 2 Pronóstico del tiempo 190
## 3 CARBAJAL INVERSION CHIAPAS 17
## 4 Pronóstico del clima 5
## 5 CARDENAS LLUVIAS DF 4
## 6 Columnas: CORPORATIVO 4
## 7 Cronista de guardia 4
## 8 LLUVIA DISTRITO FEDERAL 4
## 9 Un Vistazo 4
## 10 Desde la región más transparente 3
## # … with 2,043 more rows
#-----------------#
# E L I M I N A R
#-----------------#
# Generamos una variable con datos en Minusculas
# NOTA! De esta manera es mas facil trabajar con expresiones regulares!
# Pero no es obligatorio
datos$minus <- tolower(datos$title) # Convertimos a minusculas
palabras <- c( "pronóstico del tiempo" ,
"pronostico del tiempo",
"chiapas",
"veracruz",
"crême",
"hermanos fuentes",
"cardenas visita")
#pat_noticias <- rebus::or1(palabras)
pat_noticias <- paste0("(?:", paste(palabras, collapse = "|"), ")")
str_view_all(datos$minus[1:15], pattern = pat_noticias, match = T)
# Generamos dummy!
datos$noticia_inservible <- str_detect(datos$minus, pattern = pat_noticias)
datos <- datos %>%
filter(noticia_inservible != TRUE)
# Quedarme solo con las que dicen inundacion # T i d y
noticias_inundacion <- datos %>%
mutate(tiene_inundacion = str_detect(minus, pattern = "inundaci")) %>%
filter(tiene_inundacion == TRUE) %>%
select(minus, tiene_inundacion)
# Inundacion o lluvias
noticias_inundacion_lluvias <- datos %>%
mutate(tiene_inundacion = str_detect(minus, pattern = or1(c("inundaci","lluvia")))) %>%
filter(tiene_inundacion == TRUE) %>%
select(minus, tiene_inundacion, body)
# LIMPIAMOS TAGS
noticias_inundacion_lluvias$body <- str_remove_all(noticias_inundacion_lluvias$body, pattern = patron_complejo)
Para hacer limpieza de palabras y exploración de la base de datos, haremos un wordCloud para averigüar que temas se repiten más frecuentemente.
# Creamos nube de palabras
source("https://raw.githubusercontent.com/JuveCampos/DataEng/master/R/create_fast_wordCloud.R")
create_wordcloud(noticias_inundacion_lluvias$body, stop_words = c("méxico", "ciudad", "así"), num_words = 200)
# Frecuencia palabras
freq_palabras[1:30,]
## word freq
## 1 lluvias 1134
## 2 agua 1120
## 3 inundaciones 838
## 4 lluvia 786
## 5 federal 616
## 6 sistema 582
## 7 aguas 562
## 8 zona 555
## 9 gobierno 513
## 10 mil 484
## 11 protección 483
## 12 civil 466
## 13 drenaje 465
## 14 distrito 462
## 15 nacional 401
## 16 valle 396
## 17 encharcamientos 359
## 18 dijo 357
## 19 zonas 351
## 20 río 350
## 21 horas 346
## 22 inundación 321
## 23 obras 319
## 24 riesgo 318
## 25 informó 313
## 26 oriente 299
## 27 parte 298
## 28 evitar 298
## 29 autoridades 297
## 30 afectadas 282