Manejo de Datos del API

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.

Sobre la base de datos

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.

Setup

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

Abrimos y exploramos la base

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

Problema 1: ¿Hay títulos repetidos?

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í.

Problema 2. Eliminación de los HTML tags.

Los tags nos sirven para darle formato a páginas web, sin embargo, no nos interesa tenerlos a la hora del análisis. Para detectar estos patrones, utilizaremos expresiones regulares.

# ELIMINAMOS LOS TAGS #

# patron_sencillo <- or1(c('<span style="color: red">','</span>'))
# <regex> 
patron_sencillo <- '(?:<span style="color: red">|</span>)'

# patron_complejo <- '<' %R% optional('/') %R% captura(WRD %R% optional(SPC) %R% optional(char_class("\":=!"))) %R% ">"
# regex> 
patron_complejo <- '<[/]?([\\w[\\s]?[":=!]?]+)>'

# Extraemos las palabras
str_extract_all(datos$title, patron_sencillo) %>% unlist() %>% niveles() # Nos damos cuenta de que solo hay 2 niveles... pero y si hubiera mas?
## [1] "</span>"                     "<span style=\"color: red\">"
str_extract_all(datos$title, patron_complejo) %>% unlist() %>% niveles() # Caso General
## [1] "</span>"                     "<span style=\"color: red\">"
str_view_all(datos$title[1:10], pattern = patron_sencillo, match = T)
datos$title <- str_remove(datos$title, pattern = patron_complejo)
View(datos)
# No elimino todo!!! -- CHECAR LA MODIFICACION
datos$title <- str_remove_all(datos$title, pattern = patron_complejo)

# Y en otras variables
str_extract_all(datos$abstract[1], pattern = patron_complejo)
## [[1]]
## [1] "<span style=\"color: red\">" "</span>"                    
## [3] "<span style=\"color: red\">" "</span>"                    
## [5] "<span style=\"color: red\">" "</span>"
str_extract_all(datos$body[1], pattern = patron_complejo)
## [[1]]
##  [1] "<!UNIGEN548>"                "<h3>"                       
##  [3] "</h3>"                       "<span style=\"color: red\">"
##  [5] "</span>"                     "<span style=\"color: red\">"
##  [7] "</span>"                     "<span style=\"color: red\">"
##  [9] "</span>"                     "<span style=\"color: red\">"
## [11] "</span>"                     "<p>"

Como podemos ver arriba, el patron_sencillo no hubiera capturado los tags <!UNIGEN548> o los <h3>, porque no los consideraba específicamente. Por eso es mejor el patron_complejo, ya que es una solución más general.

Problema 3. Queremos ver que periódicos aportaron mas a nuestra base.

# 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")

Problema 4. Eliminar noticias que no son de interés.

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)

Problema 5. Queremos saber que palabras se repiten más.

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