Pensado para ir siguiendo estos videos
En esta parte de la materia vamos a explorar otras fuentes de datos que que no son el tradicional archivo CSV. En rigor, se usa el término no estructurado para referirse a cualquier fuente de datos que no se encuentre en formato de tabla. Como suele pasar con categorias que se define por la negativa contiene cosas de las más variadas:
Por supesto que esta lista es enorme y escapa a los contenidos de la materia verlos todos. Es más, muchos de esos tipos de datos definen campos en sí mismos con técnicas específicas y propias de cada tipo. Por ejemplo las imágenes y videos son objeto de estudio del campo de la visión computacional. Vamos a empezar nuestro recorrido alejandonos un poco del formato super estructurado del CSV, pero no nos vamos a alejar tanto. Si permitimos formas de vinculo un poco más flexibles que la estructura tabular nos encontramos con formatos como HTML, XML y JSON. A veces a este tipo de datos se los llama semi-estructurados.
Este tipo de formatos suelen ser el resultado de peticiones http hechas a través de internet. No es la idea de este curso estudiar el protocolo http y todas sus posibilidades, basta con saber que a partir de una URL (Uniform Resource Locator) podemos acceder a ciertos recursos específicos. Por ejemplo, si hacemos un pedido a la URL https://lcd.exactas.uba.ar/materias/
recibimos como respuesta una serie de caracteres que conforman un archivo HTML. Por supuesto que para mostrar una página web vamos a necesitar un montón de archivos adicionales que iran llegando mediante otros pedidos http dependiendo de cual sea el código. Luego de ese proceso iterativo términamos con algo como esto:
Nosotros no vamos a hacer uso de ese proceso tan complejo lo único que nos importa es que dada una URL podemos recibir una serie de caracteres como respuesta.
Resumido asi no más: URL —> caracteres (texto)
Para poder hacer estas peticiones http vamos a necesitar algunas funciones que no son parte de rbase asi que vamos a ir introduciendo algunos paquetes a medida que lo necesitemos. En primera instancia vamos a usar el paquete httr. Si no lo tienen, pueden instalarlo usando install.packages('httr')
.
require(httr) # Activamos el paquete
Loading required package: httr
No se preocupen mucho por entender este paquete en sí porque lo vamos a usar solo para entender la mecánica básica de los pedidos HTTP pero después vamos a usar paquetes más específicos que nos van a simplificar la vida. Veamos que pasa si hacemos un pedido al sitio de exactas usando la función GET
:
res = GET("https://lcd.exactas.uba.ar/materias/") # hacemos el pedido http
summary(res) # Vemos un resumen de la salida del llamado anterior
Length Class Mode
url 1 -none- character
status_code 1 -none- numeric
headers 9 insensitive list
all_headers 1 -none- list
cookies 7 data.frame list
content 111776 -none- raw
date 1 POSIXct numeric
times 6 -none- numeric
request 7 request list
handle 1 curl_handle externalptr
Vemos que nos devuelve un objeto con muchas propiedades pero lo que nos va a interesar es esencialmente el contenido de la respuesta que esta en res$content
res$content[1:1000]
[1] 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0a 3c 68 74 6d 6c 20 63 6c 61 73 73 3d 22 6e 6f 2d 6f 76 65 72 66 6c 6f 77 2d 79 20 61 76 61 64 61 2d 68 74
[52] 6d 6c 2d 6c 61 79 6f 75 74 2d 77 69 64 65 20 61 76 61 64 61 2d 68 74 6d 6c 2d 68 65 61 64 65 72 2d 70 6f 73 69 74 69 6f 6e 2d 74 6f 70 20 61 76 61 64 61
[103] 2d 69 73 2d 31 30 30 2d 70 65 72 63 65 6e 74 2d 74 65 6d 70 6c 61 74 65 20 61 76 61 64 61 2d 68 65 61 64 65 72 2d 63 6f 6c 6f 72 2d 6e 6f 74 2d 6f 70 61
[154] 71 75 65 22 20 6c 61 6e 67 3d 22 65 73 2d 41 52 22 20 70 72 65 66 69 78 3d 22 6f 67 3a 20 68 74 74 70 3a 2f 2f 6f 67 70 2e 6d 65 2f 6e 73 23 20 66 62 3a
[205] 20 68 74 74 70 3a 2f 2f 6f 67 70 2e 6d 65 2f 6e 73 2f 66 62 23 22 3e 0a 3c 68 65 61 64 3e 0a 09 3c 6d 65 74 61 20 68 74 74 70 2d 65 71 75 69 76 3d 22 58
[256] 2d 55 41 2d 43 6f 6d 70 61 74 69 62 6c 65 22 20 63 6f 6e 74 65 6e 74 3d 22 49 45 3d 65 64 67 65 22 20 2f 3e 0a 09 3c 6d 65 74 61 20 68 74 74 70 2d 65 71
[307] 75 69 76 3d 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65 22 20 63 6f 6e 74 65 6e 74 3d 22 74 65 78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 3d 75 74 66 2d
[358] 38 22 2f 3e 0a 09 3c 6d 65 74 61 20 6e 61 6d 65 3d 22 76 69 65 77 70 6f 72 74 22 20 63 6f 6e 74 65 6e 74 3d 22 77 69 64 74 68 3d 64 65 76 69 63 65 2d 77
[409] 69 64 74 68 2c 20 69 6e 69 74 69 61 6c 2d 73 63 61 6c 65 3d 31 22 20 2f 3e 0a 09 3c 74 69 74 6c 65 3e 50 6c 61 6e 20 64 65 20 65 73 74 75 64 69 6f 73 20
[460] 26 23 38 32 31 31 3b 20 4c 69 63 65 6e 63 69 61 74 75 72 61 20 65 6e 20 44 61 74 6f 73 20 26 23 38 32 31 31 3b 20 45 78 61 63 74 61 73 20 26 23 38 32 31
[511] 31 3b 20 55 42 41 3c 2f 74 69 74 6c 65 3e 0a 09 09 3c 73 74 79 6c 65 3e 0d 0a 09 09 23 77 70 61 64 6d 69 6e 62 61 72 20 23 77 70 2d 61 64 6d 69 6e 2d 62
[562] 61 72 2d 63 70 5f 70 6c 75 67 69 6e 73 5f 74 6f 70 5f 62 75 74 74 6f 6e 20 2e 61 62 2d 69 63 6f 6e 3a 62 65 66 6f 72 65 20 7b 0d 0a 09 09 09 63 6f 6e 74
[613] 65 6e 74 3a 20 22 5c 66 35 33 33 22 3b 0d 0a 09 09 09 74 6f 70 3a 20 33 70 78 3b 0d 0a 09 09 7d 0d 0a 09 09 23 77 70 61 64 6d 69 6e 62 61 72 20 23 77 70
[664] 2d 61 64 6d 69 6e 2d 62 61 72 2d 63 70 5f 70 6c 75 67 69 6e 73 5f 74 6f 70 5f 62 75 74 74 6f 6e 20 2e 61 62 2d 69 63 6f 6e 20 7b 0d 0a 09 09 09 74 72 61
[715] 6e 73 66 6f 72 6d 3a 20 72 6f 74 61 74 65 28 34 35 64 65 67 29 3b 0d 0a 09 09 7d 0d 0a 09 09 3c 2f 73 74 79 6c 65 3e 0d 0a 09 3c 73 74 79 6c 65 3e 0a 23
[766] 77 70 61 64 6d 69 6e 62 61 72 20 23 77 70 2d 61 64 6d 69 6e 2d 62 61 72 2d 76 74 72 74 73 5f 66 72 65 65 5f 74 6f 70 5f 62 75 74 74 6f 6e 20 2e 61 62 2d
[817] 69 63 6f 6e 3a 62 65 66 6f 72 65 20 7b 0a 09 63 6f 6e 74 65 6e 74 3a 20 22 5c 66 31 38 35 22 3b 0a 09 63 6f 6c 6f 72 3a 20 23 31 44 41 45 32 32 3b 0a 09
[868] 74 6f 70 3a 20 33 70 78 3b 0a 7d 0a 3c 2f 73 74 79 6c 65 3e 3c 6c 69 6e 6b 20 72 65 6c 3d 27 64 6e 73 2d 70 72 65 66 65 74 63 68 27 20 68 72 65 66 3d 27
[919] 2f 2f 73 2e 77 2e 6f 72 67 27 20 2f 3e 0a 3c 6c 69 6e 6b 20 72 65 6c 3d 22 61 6c 74 65 72 6e 61 74 65 22 20 74 79 70 65 3d 22 61 70 70 6c 69 63 61 74 69
[970] 6f 6e 2f 72 73 73 2b 78 6d 6c 22 20 74 69 74 6c 65 3d 22 4c 69 63 65 6e 63 69 61 74 75 72 61
Oops! Son los bytes pelados! Para convertir esos bytes pelados en caracteres podemos usar rawToChar
rawToChar(res$content[1:1000])
[1] "<!DOCTYPE html>\n<html class=\"no-overflow-y avada-html-layout-wide avada-html-header-position-top avada-is-100-percent-template avada-header-color-not-opaque\" lang=\"es-AR\" prefix=\"og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#\">\n<head>\n\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t<title>Plan de estudios – Licenciatura en Datos – Exactas – UBA</title>\n\t\t<style>\r\n\t\t#wpadminbar #wp-admin-bar-cp_plugins_top_button .ab-icon:before {\r\n\t\t\tcontent: \"\\f533\";\r\n\t\t\ttop: 3px;\r\n\t\t}\r\n\t\t#wpadminbar #wp-admin-bar-cp_plugins_top_button .ab-icon {\r\n\t\t\ttransform: rotate(45deg);\r\n\t\t}\r\n\t\t</style>\r\n\t<style>\n#wpadminbar #wp-admin-bar-vtrts_free_top_button .ab-icon:before {\n\tcontent: \"\\f185\";\n\tcolor: #1DAE22;\n\ttop: 3px;\n}\n</style><link rel='dns-prefetch' href='//s.w.org' />\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Licenciatura"
Un poco más claro pero no mucho. Para pode extraer información de un archivo HTML vamos a tener que “parsearlo” (anglicismo del verbo to parse), es decir identificar sus componentes y separarlo en partes. Por suerte las tareas de parseo mas comunes ya están preprogramadas y hay paquetes que nos van a simplificar un poco la vida. Acá es donde entra rvest (juego de palabras entre R y harvest, que quiere decir recolectar o cosechar en inglés). Recuerden instalarlo si no lo tienen con install.packages("rvest")
y acceder a la documentación con: ?rvest
require(rvest) # Cargamos el paquete
Loading required package: rvest
y vamos a usarlo para extaer información de la pagina de materias de la carrera que vimos recien. Para eso usamos la función read_html
que nos permite leer directamente una URL
pag_materias = read_html("https://lcd.exactas.uba.ar/materias/")
y ahora vamos a “parsear” los datos que estan en la tabla de correlatividades de esa pagina Para eso, tenemos que seleccionar el elemento HTML que contiene la tabla. Acá es donde el “parseo” se convierte un poco en un arte, podemos usar la consola de nuestro navegador para estudiar el código html y ver que el elemento que contiene lo que nos interesa pertenece a una clase que se llama table. (Aclaración sobre cómo se hace esto en el video)
pag_materias
{html_document}
<html class="no-overflow-y avada-html-layout-wide avada-html-header-position-top avada-is-100-percent-template avada-header-color-not-opaque" lang="es-AR" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
[1] <head>\n<meta http-equiv="X-UA-Compatible" content="IE=edge">\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n<meta name="viewport" ...
[2] <body class="page-template page-template-100-width page-template-100-width-php page page-id-43 fusion-image-hovers fusion-pagination-sizing fusion-button ...
Podemos elegir los elementos que pertenecen a la clase table de la siguiente manera:
elemento_tabla = html_element(pag_materias, '.table')
toString(elemento_tabla) # Podemos chusmear el pedacito de codigo HTML que seleccionamos
[1] "<table class=\"table\"><tbody>\n<tr>\n<td width=\"73\"><strong>Cuatrimestre</strong></td>\n<td width=\"194\"><strong>Asignatura</strong></td>\n<td width=\"131\"><strong>Correlatividad de Asignaturas</strong></td>\n</tr>\n<tr>\n<td width=\"73\">3</td>\n<td width=\"194\">Análisis I</td>\n<td width=\"131\">CBC</td>\n</tr>\n<tr>\n<td width=\"73\">3</td>\n<td width=\"194\">Álgebra I</td>\n<td width=\"131\">CBC</td>\n</tr>\n<tr>\n<td width=\"73\">4</td>\n<td width=\"194\">Algoritmos y Estructuras de Datos I</td>\n<td width=\"131\">Álgebra I</td>\n</tr>\n<tr>\n<td width=\"73\">4</td>\n<td width=\"194\">Electiva de Introducción a las Ciencias Naturales</td>\n<td width=\"131\">CBC</td>\n</tr>\n<tr>\n<td width=\"73\">5</td>\n<td width=\"194\">Análisis II</td>\n<td width=\"131\">Análisis I</td>\n</tr>\n<tr>\n<td width=\"73\">5</td>\n<td width=\"194\">Algoritmos y Estructuras de Datos II</td>\n<td width=\"131\">Algoritmos y Estructuras de Datos I</td>\n</tr>\n<tr>\n<td width=\"73\">6</td>\n<td width=\"194\">Laboratorio de Datos</td>\n<td width=\"131\">Algoritmos y Estructuras de Datos I</td>\n</tr>\n<tr>\n<td width=\"73\">6</td>\n<td width=\"194\">Análisis Avanzado</td>\n<td width=\"131\">Análisis II, Álgebra I</td>\n</tr>\n<tr>\n<td width=\"73\">6</td>\n<td width=\"194\">Álgebra Lineal Computacional</td>\n<td width=\"131\">Álgebra I</td>\n</tr>\n<tr>\n<td width=\"73\">7</td>\n<td width=\"194\">Probabilidad</td>\n<td width=\"131\">Análisis Avanzado</td>\n</tr>\n<tr>\n<td width=\"73\">7</td>\n<td width=\"194\">Algoritmos y Estructura de Datos III</td>\n<td width=\"131\">Algoritmos y Estructuras de Datos II</td>\n</tr>\n<tr>\n<td width=\"73\">8</td>\n<td width=\"194\">Intr. a la Estadística y Ciencia de Datos</td>\n<td width=\"131\">Alg y Estruct de Datos II, Probabilidad, Álgebra Lineal Computacional</td>\n</tr>\n<tr>\n<td width=\"73\">8</td>\n<td width=\"194\">Intr. a la Investigación Operativa y Optimización</td>\n<td width=\"131\">Alg y Estruc de Datos III, Análisis II, Álgebra Lineal Computacional</td>\n</tr>\n<tr>\n<td width=\"73\">8</td>\n<td width=\"194\">Intr. al Modelado Continuo.</td>\n<td width=\"131\">Análisis Avanzado, Álgebra Lineal Computacional, Alg y Estructura de Datos II</td>\n</tr>\n</tbody></table>"
y luego convertir el código html que corresponde a ese elemento en una tabla de R
correlativas = html_table(elemento_tabla)
correlativas
Fijense que en este caso el parseo automatico de tables que hizo la funcion html_table
no capturo correctamente la primera fila como titulos o headers. Para corregir esto podemos hacer:
nombres = correlativas[1,]
colnames(correlativas) = nombres
correlativas = correlativas[-1,] # Esto quiere decir todas las filas EXCEPTO la primera
correlativas
Esto es algo que, en general, va a haber que hacer: limpiar, emprolijar, uniformizar campos, trabajar con datos incompletos. En este caso fue bastante fácil porque el código html escondía un formato tabular pero podría ser mucho más complejo. Imagínense, por ejemplo, obtener todas las características de los productos listados como ‘guitarra’ en un comercio virtual, cuyos resultados pueden estar separados en varias paginas y contener información muy distinta entre guitarra y guitarra, puede haber errores de etiquetado, etc.
Al scrapear estamos haciendo uso de recursos que generan o almacenan terceras personas y que pueden no querer compartirlo. Es por eso que es importante leer bien los términos y condiciones de uso del sitio del cual queremos extraer información. Por ejemplo Amazon prohibe este tipo de prácticas expresamente:
En los sitios suele haber un archivo en la URL base /robots.txt (por ejemplo https://exactas.uba.ar/robots.txt) donde se explicita que familias de URLs se pueden scrapear y cuáles no. Hay un paquete de R para chequear esto automáticamente.
Extraigan la tabla de velocidad de distintos animales de https://es.wikipedia.org/wiki/Velocidad_de_los_animales.
pag_wiki = read_html("https://es.wikipedia.org/wiki/Velocidad_de_los_animales")
elem_tabla = html_element(pag_wiki, ".wikitable")
velocidades = html_table(elem_tabla)
velocidades
Van a notar que la columna interesante es muy sucia porque tiene rangos de velocidades (hay que tomar una decision y reemplazar por promedio, maxima o minima) y ademas tiene llamadas al pie de citas que dificultan la conversion a numeros. Para poder eliminar esta información vamos a ver en que tags html se encuentran almacenadas y los vamos a borrar del codigo de la página antes de extraer la tabla.
Vemos que las etiquetas que nos molestan son <span>
y <sup>
. Vamos a eliminarlas, para eso vamos a usar de la libreria xml2.
require(xml2) # REcuerden instalarla con install.packages('xml2') si no la tienen
Loading required package: xml2
sups = html_elements(pag_wiki, xpath='//table//sup') # elegimos todos los elementos sup que esten dentro de un elemento table
spans = html_elements(pag_wiki, xpath='//table//span') # elegimos todos los elementos span que esten dentro de un elemento table
xml_remove(spans) # Los eliminamos
xml_remove(sups)
Ahora si vuelvan a intentar parsear la tabla
velocidades = html_table(elem_tabla)
velocidades
#
# (completar)
#
Aún así necesitamos procesar un poco más la columna de velocidad. Tenemos que poder converirlas a un número. Para eso pueden resultarles útiles las siguientes lineas:
for (vel_str in ________){ ## Completar
## Completar
}
Esto es opcional no lo vamos a resolver en clase. En la tabla de velocidades de animales cada animal tiene un link a su página de wikipedia. El desafio es recorrer esos links y extaer de cada animal su clase Ej:
Y explorar si existe alguna relación entre las clases de animal y sus respectivas velocidades.
Carguen la tabla de superficies de paises de https://www.worldometers.info/geography/largest-countries-in-the-world/
pag_paises = # Completar
superficies['area_km'] = # Completar
Muchos sitios proveen acceso a su información o sus servicios de manera programática. Es decir que tienen URLs que en vez de estar pensadas para ser visitadas por un navegador están preparadas para integrarse como parte de otros procesos. Esto nos va simplificar la vida porque no vamos a tener que estar parseando código HTML que en realidad está pensado para ser renderizado y mostrado en un navegador.
La sigla API corresponde a Application Programming Interface (interfaz de programación de una apliacación). Es decir que una API son las reglas para interactuar con un dado software. Podemos pensarlo como las especificaciones de una enchufe, con todos los requisitos que tenemos que cumplir para “engancharnos” a un sistema y usarlo. Es una especificación de los métodos, el tipo de datos que ingresan y el tipo de datos que regresan de un dado sistema. Si bien la idea de API es aplicable a cualquier software, se suele usar especialmente en el contexto de software que se expone através de internet. Algunas APIs estan pensadas para ser de uso público, otras son internas; algunos se pueden acceder sin ninguna forma de autorización, otras requieren metodos de autenticación; algunas son gratis otras se ofrecen como servicios pagos.
Empecemos con una API muy sencilla, como puede ser la que expone el ministerio de cultura en esta URL: https://www.cultura.gob.ar/api/v2.0/ Si ingresan en esta URL podemos ver que la API tiene los siguientes endpoints (un endpoint es una suburl dentro de la api para algún fin específico):
Veamos que encontramos en el endpoint museos:
res_museos = GET("https://www.cultura.gob.ar/api/v2.0/museos/")
rawToChar(res_museos$content[1:2000])
[1] "{\"count\":27,\"next\":\"https://www.cultura.gob.ar/api/v2.0/museos/?limit=20&offset=20\",\"previous\":null,\"results\":[{\"id\":27,\"url\":\"https://www.cultura.gob.ar/api/v2.0/organismos/27/\",\"link\":\"https://www.cultura.gob.ar/institucional/organismos/museos/comision-nacional-de-la-manzana-de-las-luces/\",\"nombre\":\"Complejo Histórico Cultural Manzana de las Luces\",\"direccion\":\"Perú 294, Ciudad de Buenos Aires\",\"telefono\":\"+54 (011) 4342-9930 / 6973\",\"descripcion\":\"<p>El Complejo Histórico Cultural Manzana de las Luces depende del Ministerio de Cultura de la Nación.</p>\\r\\n<p>Fue creada en 1971 por el Decreto nº 4657/71 y ampliadas sus funciones por los decretos: 1185/73; 1454/74 y 1479/81. El decreto 108/2013 cambia su nombre de Comisión Nacional a Complejo Histórico Cultural Manzana de las Luces.</p>\\r\\n<p>Son sus objetivos la restauración y conservación de los edificios históricos; la investigación con relación a instituciones, acontecimientos y personajes que desfilaron por la Manzana de las Luces; y la refuncionalización de los edificios a través de la actividad cultural.</p>\",\"email\":\"cnml@manzanadelasluces.gov.ar\",\"provincia\":\"\",\"depende_de\":\"Ministerio de Cultura de la Nación\",\"autoridad\":null},{\"id\":28,\"url\":\"https://www.cultura.gob.ar/api/v2.0/organismos/28/\",\"link\":\"https://www.cultura.gob.ar/institucional/organismos/museos/estancia-de-jesus-maria-museo-jesuitico-nacional/\",\"nombre\":\"Estancia de Jesús María - Museo Jesuítico Nacional\",\"direccion\":\"Pedro de Oñate s/n, Jesús María, Córdoba\",\"telefono\":\"+54 (03525) 420126\",\"descripcion\":\"<p><strong>La estancia</strong></p>\\r\\n<p>La Compañía de Jesús llega a la provincia de Córdoba, en la actual Argentina en 1599. En 1608 crean el Noviciado y dos años después se declara al Colegio de Córdoba como Colegio Máximo. Debido a algunos problemas económicos que se presentan, comi"
Si lo formateamos un poco mas prolijo vemos una parte de la respuesta que llegó:
Vemos que no es un HTML. Es otro formato de datos y es EL formato mas estandar en uso en la interacción con APIs. Es un formato que se llama JSON.
El formato JSON (JavaScript Object Notation) es el caballito de batalla de los datos semi-estructurados asi como el CSV es el caballito de batalla de los datos estructurados. El formato CSV es una especificación sobre como almacenar datos tabulares en un archivo de texto (y define qué caracteres se pueden usar y en qué orden - aca la definicion formal técnica del standard en ingles) y el formato JSON también define cómo codificar en texto cierto tipo de objetos anidados (aca la definicion formal técnica del standard en ingles ). NO ES IMPORTANTE que lean los estándares, quedan solo como referencia por si a algune le interesan.
Al igual que pasa con el HTML para poder usar la información de un JSON vamos a tener que parsearlo. Por suerte como es un formato super estándar ya hay paquetes preparados para hacerlo. En este caso, el paquete que vamos a usar se llama jsonlite
. Recuerden instalarlo si no lo tienen con install.packages("jsonlite")
require(jsonlite) # lo cargamos
Loading required package: jsonlite
museos = fromJSON("https://www.cultura.gob.ar/api/v2.0/museos/") # Llamamos al endpoint ahora ya "parseando" el JSON
summary(museos)
Length Class Mode
count 1 -none- numeric
next 1 -none- character
previous 0 -none- NULL
results 11 data.frame list
museos$results
Vemos que el texto se convirtio en un objeto de R con los mismos campos que podiamos leer en el formato de llaves y comillas
Vamos a ver algunos ejemplos de otras APIs para poner estas ideas en práctica. En cada ejemplo se plantean una serie de ejercicios
Como primer ejemplo de una api sencilla vamos a ver esta https://www.thecocktaildb.com/api.php. Ahi podemos ver sus endpoints. Y vamos a ver algo interesante que no habiamos visto en la API de cultura que es que los endpoints son métodos que pueden recibir parámetros. Por ejemplo vemos que el endpoint www.thecocktaildb.com/api/json/v1/1/filter.php?i=Gin nos permite hacer una búsqueda por ingrediente (el ingrediente Gin es el valor asignado al parámetro i). Veamos:
tragos = fromJSON("http://www.thecocktaildb.com/api/json/v1/1/filter.php?i=Gin")
tragos
$drinks
NA
Y si ahora queremos tragos que lleven naranja simplemente reemplazamos el ingrediente:
tragos = fromJSON("http://www.thecocktaildb.com/api/json/v1/1/filter.php?i=Orange")
tragos
$drinks
NA
Si queremos saber que ingredientes hay disponibles podemos usar el endpoint http://www.thecocktaildb.com/api/json/v1/1/list.php?i=list
ingredientes = fromJSON("http://www.thecocktaildb.com/api/json/v1/1/list.php?i=list")
ingredientes
$drinks
NA
En esta parte de la clase vamos a trabajar con otra api que tiene datos económicos y sociales de distintos paises. Es la api del banco mundial. Es una API bastante compleja, acá pueden encontrar la documentación para usarla: https://datahelpdesk.worldbank.org/knowledgebase/topics/125589-developer-information
Dada la cantidad ENORME de indicadores que tiene el banco mundial, vamos a usar este sitio que nos permite explorarlos visualmente y luego elegir con cuales vamos a querer trabajar https://datos.bancomundial.org/ Ahi pueden buscar por ejemplo PBI (hay varias variantes puede ser por ejemplo -PIB per cápita, PPA ($ a precios internacionales actuales)- ). Si entran van a ver que aparece un grafico para el mundo y hay un boton de detalles. Si lo clickean van a poder encontrar un id para el indicador. En este caso NY.GDP.PCAP.PP.CD
. Ese es el id que vamos necesitar para hacer la consulta:
data = fromJSON("https://api.worldbank.org/v2/es/country/all/indicator/NY.GDP.PCAP.PP.CD?format=json")
data
[[1]]
[[1]]$page
[1] 1
[[1]]$pages
[1] 325
[[1]]$per_page
[1] 50
[[1]]$total
[1] 16226
[[1]]$sourceid
[1] "2"
[[1]]$sourcename
[1] "World Development Indicators"
[[1]]$lastupdated
[1] "2021-07-30"
[[2]]
NA
Vemos que la respuesta incluye dos partes, unos metadatos que nos indican información general sobre el recurso que acabamos de pedir. Por ejemplo que la cantidad total de registros del dataset es 16226 y que se actualizaron por última vez el 30 de Julio de 2021. También que solo recibimos en este pedido 50 datos. En este caso los datos están paginados en 325 páginas de 50 elementos cada una. Hay un parámetro de la api que nos permite traer todo de una:
si agregamos &per_page=20000 cambiamos el maximo por página y traemos todos los datos en una sola consulta
data = fromJSON("https://api.worldbank.org/v2/es/country/all/indicator/NY.GDP.PCAP.PP.CD?format=json&per_page=20000")
pbi = data[[2]]
y podemos graficar el pbi para Argentina y Brasil en función del tiempo
valor_ar = pbi[pbi$countryiso3code == 'ARG', 'value']
anio_ar = pbi[pbi$countryiso3code == 'ARG', 'date']
plot(anio_ar, valor_ar, type='l', col='red', xlab = 'Año', ylab='PBI Per. Cap. PPP')
valor_br = pbi[pbi$countryiso3code == 'BRA', 'value']
anio_br = pbi[pbi$countryiso3code == 'BRA', 'date']
lines(anio_br, valor_br, col='blue')
legend(1960, 15000, legend=c("Argentina", "Brasil"),
col=c("red", "blue"), lty=c(1, 1), cex=0.8)
Tengan presente la documentacion para hacer los ejercicios https://datahelpdesk.worldbank.org/knowledgebase/articles/898581-api-basic-call-structures
Las APIs grandes o muy usadas suelen tener paquetes instalables para los lenguajes más populares de manera tal que no hace falta estar generando a mano las URLs y consultas HTTP. Muchas veces esos paquetes son construidos por la propia comunidad de cada lenguaje y no necesariamente por quién creo la API. Ese es el caso de la api del banco mundial que tiene disponible el paquete WDI para R. Para instalarlo install.packages('WDI')
. Pueden ver la documentación en https://github.com/vincentarelbundock/WDI
Finalmente vamos a tabajar con una API un poco distinta para entender que no solo sirven para consultar datos sino que podemos usarlas como servicios e incluirlas como parte de nuestras rutinas de análisis. Como ejemplo vamos a usar esta API de georreferenciación nacional https://datosgobar.github.io/georef-ar-api/
Esta api tiene distintos endpoints para cada caso de uso, por ejemplo podemos saber a que muncipio, departamento y provincia pertenecen ciertas coordenadas:
fromJSON("https://apis.datos.gob.ar/georef/api/ubicacion?lat=-27.2741&lon=-66.7529")
$parametros
$parametros$lat
[1] -27.2741
$parametros$lon
[1] -66.7529
$ubicacion
$ubicacion$departamento
$ubicacion$departamento$id
[1] "10035"
$ubicacion$departamento$nombre
[1] "Belén"
$ubicacion$lat
[1] -27.2741
$ubicacion$lon
[1] -66.7529
$ubicacion$municipio
$ubicacion$municipio$id
[1] "100077"
$ubicacion$municipio$nombre
[1] "Hualfín"
$ubicacion$provincia
$ubicacion$provincia$id
[1] "10"
$ubicacion$provincia$nombre
[1] "Catamarca"
O cual es la ubicación de una dada dirección. Por ejemplo, ¿Dónde queda Avenida Rivadavia 3000?
res = fromJSON("https://apis.datos.gob.ar/georef/api/direcciones?direccion=av.%20rivadavia%203000")
res$direcciones$nomenclatura
[1] "AV RIVADAVIA 3000, Comuna 3, Ciudad Autónoma de Buenos Aires" "AV RIVADAVIA 3000, Capitán Sarmiento, Buenos Aires"
[3] "AV RIVADAVIA 3000, Ituzaingó, Buenos Aires" "AV RIVADAVIA 3000, Saladillo, Buenos Aires"
[5] "AV RIVADAVIA 3000, Saladillo, Buenos Aires" "AV RIVADAVIA 3000, Saladillo, Buenos Aires"
[7] "AV RIVADAVIA 3000, Saladillo, Buenos Aires" "AV RIVADAVIA 3000, Saladillo, Buenos Aires"
Vemos que hay varias opciones compatibles con esa dirección y recibimos mucha información adicional sobre esas direcciones, por ejemplo sus coordenadas:
res$direcciones$ubicacion
Si agregamos algo de informacion extra, como la provincia o distrito sobre la que estamos haciendo la busqueda (por ejemplo CABA) podemos conseguir una respuesta única
x = fromJSON("https://apis.datos.gob.ar/georef/api/direcciones?direccion=av.%20rivadavia%203000&provincia=caba")
x$direcciones$nomenclatura
[1] "AV RIVADAVIA 3000, Comuna 3, Ciudad Autónoma de Buenos Aires"
Habrán notado que estamos introduciendo las direcciones de una manera un poco extraña en la url, esto es porque las URLs no pueden contener cieros caracteres (como espacios) y entonces hay que convertirlos a otras secuencias de caracteres permitidos. Por suerte hay una función que ya lo hace que podemos usar:
URLencode("Av. de mayo 1658")
[1] "Av.%20de%20mayo%201658"
x = fromJSON(URLencode("https://apis.datos.gob.ar/georef/api/direcciones?direccion=av. del libertador 1400&provincia=caba"))
print(x$direcciones$nomenclatura)
[1] "AV DEL LIBERTADOR 1400, Comuna 2, Ciudad Autónoma de Buenos Aires"
print(x$direcciones$ubicacion)
Escriban un pequeño script que para cada museo de la API del ministerio de cultura obtenga sus coordenadas usando la API de georreferenciación. Partan el problema en pasos:
Referencia de la sesion con la que fue corrido este notebook
sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.3 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/atlas/libblas.so.3.10.3
LAPACK: /usr/lib/x86_64-linux-gnu/atlas/liblapack.so.3.10.3
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8
[6] LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] jsonlite_1.7.2 xml2_1.3.2 rvest_1.0.1 httr_1.4.2
loaded via a namespace (and not attached):
[1] rstudioapi_0.13 knitr_1.33 magrittr_2.0.1 R6_2.5.0 rlang_0.4.11 fansi_0.5.0 stringr_1.4.0 tools_4.1.1
[9] xfun_0.24 utf8_1.2.2 cli_3.0.1 selectr_0.4-2 htmltools_0.5.1.1 ellipsis_0.3.2 yaml_2.2.1 digest_0.6.27
[17] tibble_3.1.3 lifecycle_1.0.0 crayon_1.4.1 vctrs_0.3.8 rsconnect_0.8.24 curl_4.3.2 glue_1.4.2 evaluate_0.14
[25] rmarkdown_2.9 stringi_1.7.3 compiler_4.1.1 pillar_1.6.2 pkgconfig_2.0.3