itle: “Un ejemplo de captura de datos” |
ate: “July 29, 2019” |
Para crear nuestro primer análisis de redes real, usaremos datos de las redes de colaboración en el Congreso de los Diputados de España. Esto nos servirá para ver en la práctica el tipo de decisiones que tenemos que tomar a la hora de construir la red.
library(jsonlite)
library(httr)
Podemos empezar por extraer los datos de la lista de diputados
URL <- 'http://api.quehacenlosdiputados.net/diputados'
diputados <- GET(URL)
diputados <- content(diputados, as="text")
diputados <- fromJSON(diputados, simplifyVector=FALSE)
A continuación tomamos la lista de diputados y extraeremos su identificador único.
list_diputados <- lapply(diputados, function(x) x$id)
Recordemos que lo que nos interesa es capturar las leyes en las que cada diputado a trabajado y cuáles son los coautores. La información sobre las iniciativas legislativas en las que ha participado cada diputado está disponible en el endpoint iniciativas
que es hijo de la id
de cada diputado
. Por tanto, lo que haremos será crear una función que, para cada id
recoja la información sobre las iniciativas asociadas a ese candidato.
get_iniciativas <- function(x) {
url <- sprintf("http://api.quehacenlosdiputados.net/diputado/%s/iniciativas", x)
dip_data <- GET(url)
## Estamos haciendo una recogida muy breve de datos
## En recogidas mas grandes querremos
## 1. verificar que el contenido no es vacio (status == 200)
## 2. Guardar en disco a medida que capturamos datos
## 3. Tomar acciones si recibimos error o si falla la conexion
out <- fromJSON(content(dip_data, as="text"), simplifyVector=FALSE)
return(out)
}
Ahora, crearemos un espacio para poder almacenar estos datos. Lo haremos en una lista, así que podemos aprovechar los nombres de la lista para poder mantener un poco de orden en la información recogida.
collab <- vector("list", length(list_diputados))
names(collab) <- unlist(list_diputados)
head(collab)
Ahora, iremos sobre cada una de las id
que hemos recogido antes y recogeremos los datos relativos a las iniciativas de cada diputado. Para no sobrecargar el servidor, dejaremos un espacio de un segundo entre llamadas. Además, imprimiremos información a medida que la descarga vaya avanzando para asegurarnos de que no hay ningún problema:
for (i in names(collab)) {
remaining <- length(collab) - which(names(collab) == i)
print(sprintf("Capturando los datos de id=%s. Faltan %s llamadas", i, remaining))
collab[[i]] <- get_iniciativas(i)
## Sys.sleep(1)
}
En la lista collab
tenemos mucha información, pero podemos hacer un poco de limpieza para generar una base de datos. Para eso, crearemos una pequeña función que, para cada diputado, tome los datos que queremos mantener y los ponga en un data.frame
.
recuperar_atributos <- function(x) {
out <- data.frame("id"=as.numeric(x$id),
"nombre"=x$normalized$url,
"grupo"=x$grupo,
"circunscripcion"=x$circunscripcion,
"sexo"=x$sexo,
"antiguedad"=length(x$legislaturas),
"edad"=difftime(Sys.time(),
strptime(x$fecha_nac, "%d/%M/%Y"),
unit="days"))
return(out)
}
Ahora podemos recorrer la lista de diputados y extraer los atributos que nos interesan. En lugar de un loop usaremos una forma más idiomática en R
de atravesar una lista.
atributos <- lapply(diputados, recuperar_atributos)
atributos <- do.call(rbind, atributos)
atributos$id <- as.character(atributos$id)
head(atributos)
Ahora que tenemos información acerca de los diputados (los vértices de nuestra red), queremos recuperar las aristas que los unen. En esta red, dos vértices están unidos por una arista si los vértices han colaborado en una pieza de legislación.
recuperar_coautores <- function(x) {
out <- unique(unlist(lapply(x, function(y) unlist(y$autores))))
return(out)
}
Podemos aplicar la misma estrategia para crear la lista de coautores para cada diputado
autores <- lapply(collab, recuperar_coautores)
head(autores)
eliminando además a los diputados que no han participado en ninguna autoría
autores <- autores[!sapply(autores, function(x) length(x) == 0)] ## Eliminar 0 autorias
head(autores)
aunque podríamos tratarlos como islas de nuestra red.
Lo que tenemos ahora es una lista en la que para cada diputado tenemos un vector que contiene los diputados con los que ha colaborado. Lo que haremos a continuación es colapsar eso para tener una matriz.
coautores <- lapply(1:length(autores),
function(x) cbind(as.numeric(names(autores)[x]),
autores[[x]]))
edgelist <- do.call(rbind, coautores)
head(edgelist)
Un último paso de limpieza. En la matriz tal y como la tenemos ahora, tenemos bucles, es decir, tenemos listado a cada diputado colaborando consigo mismo. No es necesariamente un problema, pero no es lo que nos interesa en este análisis, así que es mejor que quitemos este tipo de observaciones
loops <- edgelist[, 1] == edgelist[, 2]
edgelist <- edgelist[!loops, ]
head(edgelist)
Finalmente, guardaremos los datos en un archivo .RDS
asegurándonos de que la lista de aristas que acabamos de crear contenga los nombres (las id numéricas) de cada diputaod.
edgelist <- apply(edgelist, 2, as.character)
saveRDS(edgelist, "dta/edgelist-diputados.RDS")
saveRDS(atributos, "dta/atributos-diputados.RDS")
IC0tLSAKdGl0bGU6ICJVbiBlamVtcGxvIGRlIGNhcHR1cmEgZGUgZGF0b3MiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgY2FjaGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gRkFMU0UpIAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLnBhdGggPSAnLi9hc3NldHMvJykKYGBgCgpQYXJhIGNyZWFyIG51ZXN0cm8gcHJpbWVyIGFuw6FsaXNpcyBkZSByZWRlcyByZWFsLCB1c2FyZW1vcyBkYXRvcyBkZSBsYXMgcmVkZXMgZGUKY29sYWJvcmFjacOzbiBlbiBlbCBDb25ncmVzbyBkZSBsb3MgRGlwdXRhZG9zIGRlIEVzcGHDsWEuIEVzdG8gbm9zIHNlcnZpcsOhIHBhcmEKdmVyIGVuIGxhIHByw6FjdGljYSBlbCB0aXBvIGRlIGRlY2lzaW9uZXMgcXVlIHRlbmVtb3MgcXVlIHRvbWFyIGEgbGEgaG9yYSBkZQpjb25zdHJ1aXIgbGEgcmVkLiAKCmBgYHtyfQpsaWJyYXJ5KGpzb25saXRlKQpsaWJyYXJ5KGh0dHIpCmBgYAoKUG9kZW1vcyBlbXBlemFyIHBvciBleHRyYWVyIGxvcyBkYXRvcyBkZSBsYSBsaXN0YSBkZSBkaXB1dGFkb3MKYGBge3J9ClVSTCA8LSAnaHR0cDovL2FwaS5xdWVoYWNlbmxvc2RpcHV0YWRvcy5uZXQvZGlwdXRhZG9zJwpkaXB1dGFkb3MgPC0gR0VUKFVSTCkKZGlwdXRhZG9zIDwtIGNvbnRlbnQoZGlwdXRhZG9zLCBhcz0idGV4dCIpCmRpcHV0YWRvcyA8LSBmcm9tSlNPTihkaXB1dGFkb3MsIHNpbXBsaWZ5VmVjdG9yPUZBTFNFKQpgYGAKCkEgY29udGludWFjacOzbiB0b21hbW9zIGxhIGxpc3RhIGRlIGRpcHV0YWRvcyB5IGV4dHJhZXJlbW9zIHN1IGlkZW50aWZpY2Fkb3IKw7puaWNvLiAKCmBgYHtyfQpsaXN0X2RpcHV0YWRvcyA8LSBsYXBwbHkoZGlwdXRhZG9zLCBmdW5jdGlvbih4KSB4JGlkKQpgYGAKClJlY29yZGVtb3MgcXVlIGxvIHF1ZSBub3MgaW50ZXJlc2EgZXMgY2FwdHVyYXIgbGFzIGxleWVzIGVuIGxhcyBxdWUgY2FkYQpkaXB1dGFkbyBhIHRyYWJhamFkbyB5IGN1w6FsZXMgc29uIGxvcyBjb2F1dG9yZXMuIExhIGluZm9ybWFjacOzbiBzb2JyZSBsYXMKaW5pY2lhdGl2YXMgbGVnaXNsYXRpdmFzIGVuIGxhcyBxdWUgaGEgcGFydGljaXBhZG8gY2FkYSBkaXB1dGFkbyBlc3TDoSBkaXNwb25pYmxlCmVuIGVsIF9lbmRwb2ludF8gYGluaWNpYXRpdmFzYCBxdWUgZXMgaGlqbyBkZSBsYSBgaWRgIGRlIGNhZGEgYGRpcHV0YWRvYC4gUG9yCnRhbnRvLCBsbyBxdWUgaGFyZW1vcyBzZXLDoSBjcmVhciB1bmEgZnVuY2nDs24gcXVlLCBwYXJhIGNhZGEgYGlkYCByZWNvamEgbGEKaW5mb3JtYWNpw7NuIHNvYnJlIGxhcyBpbmljaWF0aXZhcyBhc29jaWFkYXMgYSBlc2UgY2FuZGlkYXRvLiAKCmBgYHtyfQpnZXRfaW5pY2lhdGl2YXMgPC0gZnVuY3Rpb24oeCkgewogICAgdXJsIDwtIHNwcmludGYoImh0dHA6Ly9hcGkucXVlaGFjZW5sb3NkaXB1dGFkb3MubmV0L2RpcHV0YWRvLyVzL2luaWNpYXRpdmFzIiwgeCkKICAgIGRpcF9kYXRhIDwtIEdFVCh1cmwpCiAgICAjIyBFc3RhbW9zIGhhY2llbmRvIHVuYSByZWNvZ2lkYSBtdXkgYnJldmUgZGUgZGF0b3MKICAgICMjIEVuIHJlY29naWRhcyBtYXMgZ3JhbmRlcyBxdWVycmVtb3MKICAgICMjIDEuIHZlcmlmaWNhciBxdWUgZWwgY29udGVuaWRvIG5vIGVzIHZhY2lvIChzdGF0dXMgPT0gMjAwKQogICAgIyMgMi4gR3VhcmRhciBlbiBkaXNjbyBhIG1lZGlkYSBxdWUgY2FwdHVyYW1vcyBkYXRvcwogICAgIyMgMy4gVG9tYXIgYWNjaW9uZXMgc2kgcmVjaWJpbW9zIGVycm9yIG8gc2kgZmFsbGEgbGEgY29uZXhpb24KICAgIG91dCA8LSBmcm9tSlNPTihjb250ZW50KGRpcF9kYXRhLCBhcz0idGV4dCIpLCBzaW1wbGlmeVZlY3Rvcj1GQUxTRSkKICAgIHJldHVybihvdXQpCn0KYGBgCgpBaG9yYSwgY3JlYXJlbW9zIHVuIGVzcGFjaW8gcGFyYSBwb2RlciBhbG1hY2VuYXIgZXN0b3MgZGF0b3MuIExvIGhhcmVtb3MgZW4gdW5hCmxpc3RhLCBhc8OtIHF1ZSBwb2RlbW9zIGFwcm92ZWNoYXIgbG9zIG5vbWJyZXMgZGUgbGEgbGlzdGEgcGFyYSBwb2RlciBtYW50ZW5lciB1bgpwb2NvIGRlIG9yZGVuIGVuIGxhIGluZm9ybWFjacOzbiByZWNvZ2lkYS4gCgpgYGB7cn0KY29sbGFiIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aChsaXN0X2RpcHV0YWRvcykpCm5hbWVzKGNvbGxhYikgPC0gdW5saXN0KGxpc3RfZGlwdXRhZG9zKQpoZWFkKGNvbGxhYikKYGBgCkFob3JhLCBpcmVtb3Mgc29icmUgY2FkYSB1bmEgZGUgbGFzIGBpZGAgcXVlIGhlbW9zIHJlY29naWRvIGFudGVzIHkgcmVjb2dlcmVtb3MKbG9zIGRhdG9zIHJlbGF0aXZvcyBhIGxhcyBpbmljaWF0aXZhcyBkZSBjYWRhIGRpcHV0YWRvLiBQYXJhIG5vIHNvYnJlY2FyZ2FyIGVsCnNlcnZpZG9yLCBkZWphcmVtb3MgdW4gZXNwYWNpbyBkZSB1biBzZWd1bmRvIGVudHJlIGxsYW1hZGFzLiBBZGVtw6FzLAppbXByaW1pcmVtb3MgaW5mb3JtYWNpw7NuIGEgbWVkaWRhIHF1ZSBsYSBkZXNjYXJnYSB2YXlhIGF2YW56YW5kbyBwYXJhCmFzZWd1cmFybm9zIGRlIHF1ZSBubyBoYXkgbmluZ8O6biBwcm9ibGVtYToKCmBgYHtyfQpmb3IgKGkgaW4gbmFtZXMoY29sbGFiKSkgewogICAgcmVtYWluaW5nIDwtIGxlbmd0aChjb2xsYWIpIC0gd2hpY2gobmFtZXMoY29sbGFiKSA9PSBpKQogICAgcHJpbnQoc3ByaW50ZigiQ2FwdHVyYW5kbyBsb3MgZGF0b3MgZGUgaWQ9JXMuIEZhbHRhbiAlcyBsbGFtYWRhcyIsIGksIHJlbWFpbmluZykpCiAgICBjb2xsYWJbW2ldXSA8LSBnZXRfaW5pY2lhdGl2YXMoaSkKICAgICMjIFN5cy5zbGVlcCgxKQp9CmBgYAoKRW4gbGEgbGlzdGEgYGNvbGxhYmAgdGVuZW1vcyBtdWNoYSBpbmZvcm1hY2nDs24sIHBlcm8gcG9kZW1vcyBoYWNlciB1biBwb2NvIGRlCmxpbXBpZXphIHBhcmEgZ2VuZXJhciB1bmEgYmFzZSBkZSBkYXRvcy4gUGFyYSBlc28sIGNyZWFyZW1vcyB1bmEgcGVxdWXDsWEgZnVuY2nDs24KcXVlLCBwYXJhIGNhZGEgZGlwdXRhZG8sIHRvbWUgbG9zIGRhdG9zIHF1ZSBxdWVyZW1vcyBtYW50ZW5lciB5IGxvcyBwb25nYSBlbiB1bgpgZGF0YS5mcmFtZWAuIAoKYGBge3J9CnJlY3VwZXJhcl9hdHJpYnV0b3MgPC0gZnVuY3Rpb24oeCkgewogICAgb3V0IDwtIGRhdGEuZnJhbWUoImlkIj1hcy5udW1lcmljKHgkaWQpLAogICAgICAgICAgICAgICAgICAgICAgIm5vbWJyZSI9eCRub3JtYWxpemVkJHVybCwKICAgICAgICAgICAgICAgICAgICAgICJncnVwbyI9eCRncnVwbywKICAgICAgICAgICAgICAgICAgICAgICJjaXJjdW5zY3JpcGNpb24iPXgkY2lyY3Vuc2NyaXBjaW9uLAogICAgICAgICAgICAgICAgICAgICAgInNleG8iPXgkc2V4bywKICAgICAgICAgICAgICAgICAgICAgICJhbnRpZ3VlZGFkIj1sZW5ndGgoeCRsZWdpc2xhdHVyYXMpLAogICAgICAgICAgICAgICAgICAgICAgImVkYWQiPWRpZmZ0aW1lKFN5cy50aW1lKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RycHRpbWUoeCRmZWNoYV9uYWMsICIlZC8lTS8lWSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXQ9ImRheXMiKSkKICAgIHJldHVybihvdXQpCn0KYGBgCgpBaG9yYSBwb2RlbW9zIHJlY29ycmVyIGxhIGxpc3RhIGRlIGRpcHV0YWRvcyB5IGV4dHJhZXIgbG9zIGF0cmlidXRvcyBxdWUgbm9zCmludGVyZXNhbi4gRW4gbHVnYXIgZGUgdW4gX2xvb3BfIHVzYXJlbW9zIHVuYSBmb3JtYSBtw6FzIGlkaW9tw6F0aWNhIGVuIGBSYCBkZQphdHJhdmVzYXIgdW5hIGxpc3RhLiAKCmBgYHtyfQphdHJpYnV0b3MgPC0gbGFwcGx5KGRpcHV0YWRvcywgcmVjdXBlcmFyX2F0cmlidXRvcykKYXRyaWJ1dG9zIDwtIGRvLmNhbGwocmJpbmQsIGF0cmlidXRvcykKYXRyaWJ1dG9zJGlkIDwtIGFzLmNoYXJhY3RlcihhdHJpYnV0b3MkaWQpCmhlYWQoYXRyaWJ1dG9zKQpgYGAKCkFob3JhIHF1ZSB0ZW5lbW9zIGluZm9ybWFjacOzbiBhY2VyY2EgZGUgbG9zIGRpcHV0YWRvcyAobG9zIHbDqXJ0aWNlcyBkZSBudWVzdHJhCnJlZCksIHF1ZXJlbW9zIHJlY3VwZXJhciBsYXMgYXJpc3RhcyBxdWUgbG9zIHVuZW4uIEVuIGVzdGEgcmVkLCBkb3MgdsOpcnRpY2VzCmVzdMOhbiB1bmlkb3MgcG9yIHVuYSBhcmlzdGEgc2kgbG9zIHbDqXJ0aWNlcyBoYW4gY29sYWJvcmFkbyBlbiB1bmEgcGllemEgZGUKbGVnaXNsYWNpw7NuLiAKCmBgYHtyfQpyZWN1cGVyYXJfY29hdXRvcmVzIDwtIGZ1bmN0aW9uKHgpIHsKICAgIG91dCA8LSB1bmlxdWUodW5saXN0KGxhcHBseSh4LCBmdW5jdGlvbih5KSB1bmxpc3QoeSRhdXRvcmVzKSkpKQogICAgcmV0dXJuKG91dCkKfQpgYGAKClBvZGVtb3MgYXBsaWNhciBsYSBtaXNtYSBlc3RyYXRlZ2lhIHBhcmEgY3JlYXIgbGEgbGlzdGEgZGUgY29hdXRvcmVzIHBhcmEgY2FkYQpkaXB1dGFkbwpgYGB7cn0KYXV0b3JlcyA8LSBsYXBwbHkoY29sbGFiLCByZWN1cGVyYXJfY29hdXRvcmVzKQpoZWFkKGF1dG9yZXMpCmBgYAplbGltaW5hbmRvIGFkZW3DoXMgYSBsb3MgZGlwdXRhZG9zIHF1ZSBubyBoYW4gcGFydGljaXBhZG8gZW4gbmluZ3VuYSBhdXRvcsOtYQpgYGB7cn0KYXV0b3JlcyA8LSBhdXRvcmVzWyFzYXBwbHkoYXV0b3JlcywgZnVuY3Rpb24oeCkgbGVuZ3RoKHgpID09IDApXSAjIyBFbGltaW5hciAwIGF1dG9yaWFzCmhlYWQoYXV0b3JlcykKYGBgCmF1bnF1ZSBwb2Ryw61hbW9zIHRyYXRhcmxvcyBjb21vIF9pc2xhc18gZGUgbnVlc3RyYSByZWQuIAoKTG8gcXVlIHRlbmVtb3MgYWhvcmEgZXMgdW5hIGxpc3RhIGVuIGxhIHF1ZSBwYXJhIGNhZGEgZGlwdXRhZG8gdGVuZW1vcyB1biB2ZWN0b3IKcXVlIGNvbnRpZW5lIGxvcyBkaXB1dGFkb3MgY29uIGxvcyBxdWUgaGEgY29sYWJvcmFkby4gTG8gcXVlIGhhcmVtb3MgYQpjb250aW51YWNpw7NuIGVzIGNvbGFwc2FyIGVzbyBwYXJhIHRlbmVyIHVuYSBtYXRyaXouCgpgYGB7cn0KY29hdXRvcmVzIDwtIGxhcHBseSgxOmxlbmd0aChhdXRvcmVzKSwKICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBjYmluZChhcy5udW1lcmljKG5hbWVzKGF1dG9yZXMpW3hdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdXRvcmVzW1t4XV0pKQplZGdlbGlzdCA8LSBkby5jYWxsKHJiaW5kLCBjb2F1dG9yZXMpCmhlYWQoZWRnZWxpc3QpCmBgYAoKVW4gw7psdGltbyBwYXNvIGRlIGxpbXBpZXphLiBFbiBsYSBtYXRyaXogdGFsIHkgY29tbyBsYSB0ZW5lbW9zIGFob3JhLCB0ZW5lbW9zCl9idWNsZXNfLCBlcyBkZWNpciwgdGVuZW1vcyBsaXN0YWRvIGEgY2FkYSBkaXB1dGFkbyBjb2xhYm9yYW5kbyBjb25zaWdvIG1pc21vLgpObyBlcyBuZWNlc2FyaWFtZW50ZSB1biBwcm9ibGVtYSwgcGVybyBubyBlcyBsbyBxdWUgbm9zIGludGVyZXNhIGVuIGVzdGUKYW7DoWxpc2lzLCBhc8OtIHF1ZSBlcyBtZWpvciBxdWUgcXVpdGVtb3MgZXN0ZSB0aXBvIGRlIG9ic2VydmFjaW9uZXMKCmBgYHtyfQpsb29wcyA8LSBlZGdlbGlzdFssIDFdID09IGVkZ2VsaXN0WywgMl0KZWRnZWxpc3QgPC0gZWRnZWxpc3RbIWxvb3BzLCBdCmhlYWQoZWRnZWxpc3QpCmBgYAoKRmluYWxtZW50ZSwgZ3VhcmRhcmVtb3MgbG9zIGRhdG9zIGVuIHVuIGFyY2hpdm8gYC5SRFNgIGFzZWd1csOhbmRvbm9zIGRlIHF1ZSBsYQpsaXN0YSBkZSBhcmlzdGFzIHF1ZSBhY2FiYW1vcyBkZSBjcmVhciBjb250ZW5nYSBsb3MgX25vbWJyZXNfIChsYXMgaWQgbnVtw6lyaWNhcykKZGUgY2FkYSBkaXB1dGFvZC4gCgpgYGB7cn0KZWRnZWxpc3QgPC0gYXBwbHkoZWRnZWxpc3QsIDIsIGFzLmNoYXJhY3RlcikKc2F2ZVJEUyhlZGdlbGlzdCwgImR0YS9lZGdlbGlzdC1kaXB1dGFkb3MuUkRTIikKc2F2ZVJEUyhhdHJpYnV0b3MsICJkdGEvYXRyaWJ1dG9zLWRpcHV0YWRvcy5SRFMiKQpgYGAK