Web scraping + Visualización con R: ejemplo con datos de wikipedia
Jilber Urbina
6/17/2020
Web Scraping
En este post mostaré cómo hacer web scraping con R para obtener tablas de datos de la web. Para ello utilizaré la información sobre el Índice de Desarrollo Humano para Nicaragua (IDH) por departamento, estos datos están disponibles en Wikipedia.
¿Qué es Web Scraping? Para no complicarnos la vida, vamos a tomar la definición que proporciona Wikipedia: “es una técnica utilizada mediante programas de software para extraer información de sitios web.”
La siguiente pregunta importante es ¿por qué es importante esto? La respuesta es sencilla, fundamentalmente porque parte de la información que necesitamos para nuestros análisis está disponibe en formato HTML y no no está disponible en un excel o txt, así que hay herramientas apropidadas para hacer esas extraciones de datos de forma rápida y sencilla. Es lo que voy a mostrar en este post.
Para seguir este post y reproducir sus resultados, asegúrese de tener instalado tidyverse, zoo y ggrepel. Vamos utilizar los datos sobe los departamentos de Nicargua y el comportamiento del IDH por departamento que se presentan entos enlaces https://es.wikipedia.org/wiki/Anexo:Departamentos_de_Nicaragua y https://es.wikipedia.org/wiki/Anexo:Departamentos_de_Nicaragua_por_IDH, el objetivo es la extracción de las tablas de datos de ambas páginas. Para ello, primero cargamos los paquetes requeridos
library(rvest)
library(tidyverse)
Para importar las tablas de los departamentos sólo se hace los siguiente:
# departamentos de Nicaragua
url_wiki <- "https://es.wikipedia.org/wiki/Anexo:Departamentos_de_Nicaragua"
departamentos <- url_wiki %>%
read_html() %>%
html_nodes("table") %>%
html_table(fill = TRUE)
El código anterior hace lo siguiente:
read_html()
toma la url contenida enurl_wiki
y lee todo su contenidohtml_nodes
extrae las piezas información especificadas, en este caso tablas (table
) esta es la etiqueta que identifica a las tablas en las hmtl, se sugiere al lector familiarizarse con los selectores CSS.html_table
convierte la tabla html a data.frame- finalmente, este resultado se guarda con el nombre
departamentos
. Tenga en cuenta que endepartamentos
van a estar todos los objetos que tengan el identificadortable
, por tanto, ese resultado será una lista de data.frames, sin embargo, es este caso, por tratarse de una única tabla,departamentos
es una lista de longitud 1 que contiene un data.frame.
Asi, los datos importados lucen de la siguiente manera:
departamentos[[1]] # por lista de longitud 1, debemos seleccionar con [[]] el objeto que deseamos mostrar
Departamento | ISO 3166-2 | Cabecera | Población (2012) | Áreakm² | Densidad |
---|---|---|---|---|---|
Boaco | NI-BO | Boaco | 174 682 | 4177 | 41,8 |
Carazo | NI-CA | Jinotepe | 186 898 | 1081 | 172,8 |
Chinandega | NI-CI | Chinandega | 432 062 | 4822 | 87,7 |
Chontales | NI-CO | Juigalpa | 153 932 | 6481 | 24 |
Costa Caribe Norte | NI-CN | Puerto Cabezas | 314 130 | 33106 | 10 |
Costa Caribe Sur | NI-CS | Bluefields | 306 510 | 27260 | 11 |
Estelí | NI-ES | Estelí | 201 548 | 2230 | 90 |
Granada | NI-GR | Granada | 168 186 | 1040 | 162 |
Jinotega | NI-JI | Jinotega | 331 335 | 9222 | 36 |
León | NI-LE | León | 355 779 | 5138 | 69 |
Madriz | NI-MD | Somoto | 132 459 | 1708 | 78 |
Managua | NI-MN | Managua | 2 132 421 | 3465 | 365 |
Masaya | NI-MS | Masaya | 289 988 | 611 | 475 |
Matagalpa | NI-MT | Matagalpa | 469 172 | 6804 | 69 |
Nueva Segovia | NI-NS | Ocotal | 208 523 | 3491 | 60 |
Río San Juan | NI-SJ | San Carlos | 95 596 | 7541 | 13 |
Rivas | NI-RI | Rivas | 156 283 | 2162 | 72 |
Para importar la tabla del IDH por departamentos, se repite el mismo procedimiento anteiror:
# clasificación de departamentos por Índice de Desarrollo Humano
url_wiki_idh <- "https://es.wikipedia.org/wiki/Anexo:Departamentos_de_Nicaragua_por_IDH"
departamentos_idh_1 <- url_wiki_idh %>%
read_html() %>%
html_nodes("table") %>%
.[2] %>% # en este caso la tabla es el elemento 2 de los nodos extraídos
html_table(fill = TRUE)
# departamentos_idh_1[[1]] %>%
# head(5) # visualizando las primera 5 observaciones
Puesto | Departamento | Capital | IDH | Población | Simpatiza Para Capital | NA |
---|---|---|---|---|---|---|
Desarrollo Humano Muy Alto | Desarrollo Humano Muy Alto | Desarrollo Humano Muy Alto | Desarrollo Humano Muy Alto | Desarrollo Humano Muy Alto | Desarrollo Humano Muy Alto | NA |
1º | Managua | Managua | 0,827 | 1.262.658 | 9.80% | NA |
Desarrollo Humano Alto | Desarrollo Humano Alto | Desarrollo Humano Alto | Desarrollo Humano Alto | Desarrollo Humano Alto | Desarrollo Humano Alto | Desarrollo Humano Alto |
2º | Masaya | Masaya | 0,796 | 289.467 | 7.01% | NA |
3º | León | León | 0,781 | 373.662 | 8.60% | NA |
Como se observa, esta tabla que en la web es visualmente agradable, no lo es tanto para trabajar con ella por su diseño, por tanto, aquí hay que invertir un poco de tiempo para limpiar y convertir esta tabla en un objeto operable en término de análisis.
Así que manos la obra!
library(zoo)
library(forcats)
library(ggrepel)
# Esta requiere de limpieza
departamentos_idh_2 <- departamentos_idh_1[[1]] %>%
data.frame() %>%
select(-NA.) %>% # elimino variable extra sin información
mutate(IDH_categoria = ifelse(grepl("^Desarrollo", IDH), IDH, NA) %>% na.locf()) %>% # creo categoría de IDH
filter(!grepl("^Desarrollo", Puesto)) %>% # eliminando filas en las que se repiten las categorías del IDH
mutate(Puesto = 1:n(), # valores de Puesto a números enteros
IDH = sub(",", "\\.", IDH) %>% as.numeric, # cambio de "," a "." en IDH y convierto a número
Población = gsub("\\.", "", Población)%>% as.numeric, # elimino "." en Población y convierto a número
Simpatiza.Para.Capital = sub("%", "", Simpatiza.Para.Capital) %>% as.numeric, # elmino "%"
IDH_categoria = sub("Desarrollo Humano ", "", IDH_categoria)) %>%
mutate_if(is.character, as.factor) %>% # cambiando atributos: los caracteres a factores
mutate(IDH_categoria = fct_reorder(IDH_categoria, -IDH), # ordenando categoría según IDH
Polacion.porcentaje = 100*Población/sum(Población)) %>% # agregando nueva variable
tibble()
Todas las modificaciones anteriores nos permiten pasar el data.frame original y desordenado, a este:
departamentos_idh_2
Puesto | Departamento | Capital | IDH | Población | Simpatiza.Para.Capital | IDH_categoria | Polacion.porcentaje |
---|---|---|---|---|---|---|---|
1 | Managua | Managua | 0.827 | 1262658 | 9.80 | Muy Alto | 24.896409 |
2 | Masaya | Masaya | 0.796 | 289467 | 7.01 | Alto | 5.707554 |
3 | León | León | 0.781 | 373662 | 8.60 | Alto | 7.367666 |
4 | Granada | Granada | 0.760 | 179437 | 10.62 | Alto | 3.538042 |
5 | Carazo | Jinotepe | 0.734 | 167810 | 5.11 | Medio | 3.308787 |
6 | Estelí | Estelí | 0.712 | 197020 | 7.70 | Medio | 3.884734 |
7 | Rivas | Rivas | 0.686 | 158142 | 6.08 | Medio | 3.118159 |
8 | Chinandega | Chinandega | 0.679 | 405289 | 5.16 | Medio | 7.991270 |
9 | Chontales | Juigalpa | 0.655 | 167895 | 4.18 | Medio | 3.310463 |
10 | Madriz | Somoto | 0.640 | 124973 | 3.38 | Medio | 2.464150 |
11 | Matagalpa | Matagalpa | 0.629 | 450143 | 10.90 | Medio | 8.875677 |
12 | Nueva Segovia | Ocotal | 0.603 | 198521 | 3.68 | Medio | 3.914330 |
13 | Boaco | Boaco | 0.599 | 157973 | 3.40 | Medio | 3.114826 |
14 | Río San Juan | San Carlos | 0.558 | 87401 | 3.39 | Medio | 1.723326 |
15 | Costa Caribe Sur | Bluefields | 0.536 | 340873 | 3.65 | Medio | 6.721150 |
16 | Jinotega | Jinotega | 0.513 | 278504 | 3.87 | Medio | 5.491392 |
17 | Costa Caribe Norte | Puerto Cabezas | 0.497 | 231879 | 3.47 | Bajo | 4.572065 |
Una vez descargados los datos y ordenados, podemos visualizar la información que contienen:
# visualizando
departamentos_idh_2 %>%
mutate_if(is.character, as.factor) %>%
ggplot(aes(y=Población, x = IDH))+
geom_point(aes(color=IDH_categoria))+
labs(title = "IDH de los departamentos de Nicaragua",
caption = "Fuente: Elaboración propia con datos extraidos de Wikipedia")
Pese a que el gráfico, aparentemente, es informativo y está correcto, fíjese bien, falta un detalle fundamental: no contiene identificador para los departamentos, así que esto se puede corregir fácilmente, además se usará el porcentaje de población en lugar del valor absoluto.
plot2 <- departamentos_idh_2 %>%
ggplot(aes(x = IDH, y=Polacion.porcentaje, label=Departamento, color=IDH_categoria))+
geom_point(aes(color=IDH_categoria))+
geom_text_repel() +
labs(title = "IDH de los departamentos de Nicaragua",
caption = "Fuente: Elaboración propia con datos extraidos de Wikipedia",
color = "Nivel de IDH",
y="Porcentaje de población")+
theme(legend.position = "bottom")
print(plot2)
Note que el gráfico es un objeto que puede ser guardado para, posteriormente, editarlo sin tener que reescribir todo el código.
Vamos a incorporar una mejora en este gráfico: vamos a incorporar una estratificación según la región a la que pertenece cada departamento, para ello usaremos la clasificación sugerida en https://www.comercioexterior.ub.edu/fpais/nicaragua/ciudadesmasimportantes.htm en donde se agrupan los departamentos en tres regiones según se indica:
- Los de la Región del Pacífico son: Chinandega, León, Managua, Carazo, Masaya, Granada y Rivas.
- Los de la Región Central son: Nueva Segovia, Madriz, Estelí, Jinotega, Matagalpa, Boaco, Chontales y Río San Juan.
- Los de la Región del Caribe o Costa Atlántica son: Atlántico Norte y Atlántico Sur.
Antes de incorporar esa estratificación al gráfico, se debe crear la variable Región en la base de datos. Para crearla se hace lo siguiente
departamentos_idh_2 <- departamentos_idh_2 %>%
mutate(Region = fct_collapse(
Departamento,
Pacífico = c("Chinandega", "León", "Managua", "Carazo", "Masaya", "Granada", "Rivas"),
Central = c("Nueva Segovia", "Madriz", "Estelí", "Jinotega", "Matagalpa", "Boaco", "Chontales", "Río San Juan"),
Caribe = c("Costa Caribe Norte", "Costa Caribe Sur")))
Lo anterior luce así:
Puesto | Departamento | Capital | IDH | Población | Simpatiza.Para.Capital | IDH_categoria | Polacion.porcentaje | Region |
---|---|---|---|---|---|---|---|---|
1 | Managua | Managua | 0.83 | 1262658 | 9.80 | Muy Alto | 24.896409 | Pacífico |
2 | Masaya | Masaya | 0.80 | 289467 | 7.01 | Alto | 5.707554 | Pacífico |
3 | León | León | 0.78 | 373662 | 8.60 | Alto | 7.367666 | Pacífico |
4 | Granada | Granada | 0.76 | 179437 | 10.62 | Alto | 3.538042 | Pacífico |
5 | Carazo | Jinotepe | 0.73 | 167810 | 5.11 | Medio | 3.308787 | Pacífico |
6 | Estelí | Estelí | 0.71 | 197020 | 7.70 | Medio | 3.884734 | Central |
7 | Rivas | Rivas | 0.69 | 158142 | 6.08 | Medio | 3.118159 | Pacífico |
8 | Chinandega | Chinandega | 0.68 | 405289 | 5.16 | Medio | 7.991270 | Pacífico |
9 | Chontales | Juigalpa | 0.66 | 167895 | 4.18 | Medio | 3.310463 | Central |
10 | Madriz | Somoto | 0.64 | 124973 | 3.38 | Medio | 2.464150 | Central |
11 | Matagalpa | Matagalpa | 0.63 | 450143 | 10.90 | Medio | 8.875677 | Central |
12 | Nueva Segovia | Ocotal | 0.60 | 198521 | 3.68 | Medio | 3.914330 | Central |
13 | Boaco | Boaco | 0.60 | 157973 | 3.40 | Medio | 3.114826 | Central |
14 | Río San Juan | San Carlos | 0.56 | 87401 | 3.39 | Medio | 1.723326 | Central |
15 | Costa Caribe Sur | Bluefields | 0.54 | 340873 | 3.65 | Medio | 6.721150 | Caribe |
16 | Jinotega | Jinotega | 0.51 | 278504 | 3.87 | Medio | 5.491392 | Central |
17 | Costa Caribe Norte | Puerto Cabezas | 0.50 | 231879 | 3.47 | Bajo | 4.572065 | Caribe |
Una vez actualizado el data.frame, se puede agregar esta nueva información al gráfico
plot3 <- plot2 +
facet_grid(~departamentos_idh_2$Region)+
geom_text_repel() +
labs(subtitle = "Estratificado por Región")
plot3
Ahora se puede decir que la zona central de Nicaragua se caracteriza por tener IDH medio, mientras que la zona del pacífico es la única con valor muy alto, sin embargo, esto sólo sucede en Managua, mientras que León, Masaya y Granada destacan por tener índices altos, mientras que Chinandega, Rivas y Carazo tienen IDH que las posicionan en la clasifiación de medio. La Costa Caribe es la de menor desempeño en cuanto al IDH.
¿Podemos agregar más información sin perjucio de la visualizacón en el gráfico en cuestión? ¿Qué tal si incluimos la densidad de población? Para ello, debemos retomar la primer tabla que habíamos importado al inicio
# retomemos la primer tabla que importamos y damos formato numérico a los números
departamentos_2 <- departamentos[[1]] %>%
select(Departamento, Densidad) %>%
mutate(Densidad = sub(",", "\\.", Densidad) %>% as.numeric)
# juntamos ambas tablas en una sola
departamentos_idh_3 <- departamentos_idh_2 %>%
right_join(departamentos_2, by="Departamento")
Ahora actualicemos el gráfico agregando la nueva variable. Esta variable la introduciremos para controlar el tamaño de los puntos, de manera que cuanto mayor sea la densidad de población de cada departamento, mayor será el tamaño del punto en el plano.
plot3 +
geom_point(aes(color=IDH_categoria, size = departamentos_idh_3$Densidad, alpha = .5 ))+
guides(size = FALSE, alpha = FALSE)
Como era de esperarse, Mayasa, Managua, Granada y Carazo son los mayormente poblados.
Para obtener el ultimo gráfico, habido construido previamente la base “departamentos_idh_3” procederíamos con la ejecución del siguiente código:
plot4 <- departamentos_idh_3 %>%
ggplot(aes(x = IDH, y=Polacion.porcentaje, label=Departamento, color=IDH_categoria))+
geom_point(aes(color=IDH_categoria))+
geom_text_repel() +
labs(title = "IDH de los departamentos de Nicaragua",
subtitle = "Estratificado por Región",
caption = "Fuente: Elaboración propia con datos extraidos de Wikipedia",
color = "Nivel de IDH",
y="Porcentaje de población")+
theme(legend.position = "bottom")+
facet_grid(~Region)+
geom_point(aes(color=IDH_categoria, size = Densidad, alpha = .5 ))+
guides(size = FALSE, alpha = FALSE)
plot4
Finalmente, se presenta el mismo gráfico con algunos ajustes de personalización, sòlo para que combie con el tema de colores del blog.
plot4 +
theme_light()+
theme(legend.position = "bottom",
strip.background =element_rect(fill="darkblue"),
strip.text = element_text(colour = 'white', face="bold"))
No hay comentarios:
Publicar un comentario