Introducción a D3 para crear una infografía

D3 significa Data Drive Document, es decir, documentos orientados a datos. Es una librería libre escrita en JavaScript que no solo nos permite trabajar con los datos, sino que también nos permite trabajar con documentos. Trabajar con documentos significa realmente trabajar con HTML. D3 permite crear contenido en HTML de modo dinámico, pudiéndose crear páginas web a partir de los datos. En esta entrada vamos a mostrar una introducción a D3 para crear una infografía. Más concretamente, veremos una infografía de texto. Para crear una figura con D3, os recomiendo leeros esta entrada completamente y después seguir con esta otra.

Introducción a D3 para crear una infografía: archivos necesarios

Vamos a necesitar dos archivos, uno en HTML y el otro en JavaScript. Los vamos a llamar index.html y script.js, respectivamente. Guardaremos los dos archivos en una misma carpeta. Formamos primero un esqueleto de una web HTML y luego llamamos a la librería de D3 escrita en JavaScript.

Datos sobre partidos políticos de España

Uno de los puntos fuertes de D3 tiene que ver con el uso y manejo de los datos. Vamos a emplear un conjunto de datos sobre información de partidos políticos. Estos datos se encuentra en formato JSON, y éste está construido por un array que contiene una serie diccionarios agrupados por llaves. Cada una de estas llaves es información de un partido político: nombre del partido (partido), número de votantes (votantes), la media de sus votantes (izquierda a derecha en escala de 1 a 10, mediaAutoubicacion) y no sabe/no contesta (nsnc). Vamos a mostrar los primeros dos partidos para ilustrar su estructura.

[
   {
      "partido":"PP",
      "mediaAutoubicacion":7.09,
      "votantes":2509,
      "nsnc":302
   },
   {
      "partido":"PSOE",
      "mediaAutoubicacion":3.81,
      "votantes":2330,
      "nsnc":260
   }
]

Introducción a D3 para crear una infografía de texto

La librería D3 es una librería de uso gratuito y escrita en JavaScript, como mencionamos anteriormente. Lo primero que tenemos que hacer es cargarla en HTML.

Cargar la librería de D3 en una página HTML

Podemos cargar la librería de dos formas. O bien descargarnos la librería para trabajarla en local desde su página principal (d3.zip), o bien hacemos una llamada a la librería almacenada en internet en la cabecera del HTML con el código siguiente.

<script src="https://d3js.org/d3.v5.min.js"></script>

Si empleamos la estructura básica de un HTML, quedaría ese código integrado entre las etiquetas <head></head>. Vamos a llamar al archivo como index.html.

<html>
	<head>
		<script src="https://d3js.org/d3.v5.min.js"></script>
	</head>
	<body>
	</body>
</html>

A continuación vamos a crear un archivo JavaScript que llamaremos script.js y lo salvamos en el mismo directorio donde guardamos index.html. Inicialmente va a estar vacío, pero en él vamos a ir escribiendo y llamando a las diferentes funciones de D3. Hacemos una llamada a este archivo JavaScript en index.html escribiendo <script src="script.js"></script>.

Es importante que escribamos primero la llamada a la librería D3 y luego a nuestro archivo script.js. Esto se debe a que la lectura en HTML es secuencial, por lo que si cargamos primero nuestro archivo script.js con las funciones de D3, como la librería de D3 no está cargada nos devolverá un error. Debe estar escrito como se ve a continuación:

<html>
	<head>
		<script src="https://d3js.org/d3.v5.min.js"></script>
		<script src="script.js"></script>
	</head>
	<body>
	</body>
</html>

Cargar los datos en JSON en D3

En esta ocasión pasamos a editar el archivo script.js para cargar los datos en JSON del ejemplo. La sintaxis para cargar sería d3.json("url"). Por lo tanto quedaría así la línea:

d3.json("http://output.jsbin.com/lixujex/1.js")

A continuación concatenamos una función anónima a nuestro archivo cargado con un punto

d3.json("http://output.jsbin.com/lixujex/1.js").then (function () {})

Esa función es la que nos va a permitir hacer cosas con los datos que hemos cargado, dentro de esa función anónima.

Editar la función anónima

Vamos a llamar a los datos cargados JSON dentro de la función anónima como partidos. Podemos poner lógicamente el nombre que queramos, siempre y cuando podamos identificarlo fácilmente.

d3.json("http://output.jsbin.com/lixujex/1.js").then (function (partidos) {})

Para verificar que los datos se han cargado correctamente cuando se ejecute el script, vamos a añadir un console.log() con un mensaje en su interior que nos lo confirme.

d3.json("http://output.jsbin.com/lixujex/1.js").then (function (partidos) {
	console.log("Los datos se han cargado satisfactoriamente")
})

Variables globales, locales y alcance (scope)

El alcance o scope indica cómo de visible son las diferentes variables a lo largo del código. Las variables pueden ser de dos tipos. Las variables locales son solo visibles donde son definidas. Si está definida dentro de una función, solo será visible dentro de esa función. Las globales están fuera de las funciones y se pueden emplear por todas ellas.

Si tenemos una función (hija) dentro de otra función (padre), las variables definidas en la función padre pueden emplearse en la función hija, pero no al revés.

En la función anónima anterior, datos era una variable local. Si queremos convertirla en global, tendríamos que añadir dentro de la función window.partidos = partidos.

d3.json("http://output.jsbin.com/lixujex/1.js").then (function (partidos) {
	console.log("Los datos se han cargado satisfactoriamente")
	window.partidos = partidos
})

De este modo, al ir a la consola del navegador y escribir datos nos devuelve que es un array de 44 elementos.

>> partidos
Array(44) [ {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, … ]

Creamos una lista en D3

Para crear una lista en HTML tenemos que incluir sus etiquetas en el <body></body>. Pero también podemos hacerlo a través de D3 en JavaScript.

Entra en juego el concepto de jerarquía y contenedor. El contenedor es donde vamos a tener los datos, entendidos tanto como los datos de nuestro conjunto de datos como los elementos HTML.

En este caso, nuestro contenedor es el <body>. Y lo seleccionamos con la siguiente función d3.select("body"). Dentro de este contenedor tenemos que añadir una lista HTML, y lo hacemos concatenando con append("ul"). En conjunto, tenemos que generar una variable que contenga las dos funciones anteriores, que llamaremos elementoLista. La línea de código sería la siguiente:

var elementoLista = d3.select("body").append("ul")

Dos conceptos de D3: escalas y join

Hay dos conceptos importante en D3: las escalas y el join. Mientras que las escalas las veremos un poquito después, el join lo veremos ahora mismo. El join lo que hace es unir dos tablas de bases de datos. Lo que hace en D3 es combinar nuestro conjunto de datos (en nuestro ejemplo el de los partidos políticos) con código HTML. Se van a crear tantos elementos HTML como partidos políticos en nuestros datos. Existen tres métodos: añadir, eliminar o actualizar elementos de una página web HTML.

Vamos a hacer el join ahora para crear una lista HTML con tantos partidos políticos presentes en nuestros datos. El contenedor hijo de ul es li. Lo que hacemos ahora es concatenar varias funciones de D3 a elementoLista. Por un lado, como no sabemos cuántos puntos de la lista tendremos que dibujar, hacemos una selección vacía con .selectAll(). A continuación hacemos el join con nuestros datos llamados partidosy la función .data(). Seguidamente introducimos los datos con .enter(). para acabar introducido las etiquetas li con .append(). En resumen, el código a escribir sería el siguiente:

elementoLista
	.selectAll("li") // Selección vacía
	.data(partidos)  // hacemos el join
	.enter()         // introducimos los datos
	.append("li")    // introducimos las etiquetas li

De momento, el archivo script.js con todos los códigos vistos e introduciendo comentarios queda del siguiente modo:

d3.json("http://output.jsbin.com/lixujex/1.js").then (function (partidos) {
	
	//Confirmamos que los datos están cargados
	console.log("Los datos se han cargado satisfactoriamente")
	
	// Convertimos en global a datos
	window.datos = datos
	
	// Generamos el contenedor
	var elementoLista = d3.select("body").append("ul")
	
	// Hacemos el join entre datos y etiquetas HTML
	elementoLista
		.selectAll("li") // Selección vacía
		.data(partidos)  // hacemos el join
		.enter()         // introducimos los datos
		.append("li")    // introducimos las etiquetas li
})

Al visualizar la web HTML empleando nuestro archivo script.js y cargando la librería HTML, tendríamos la siguiente visualización, donde se ven tantos puntos de la lista (de momento vacíos) como número de partidos políticos haya en el conjunto de datos. Como tenemos 44 arrays o partidos políticos en nuestros conjunto de datos, tendremos 44 elementos en esa lista.

Añadir texto para generar una infografía textual en D3

Continuando con esta introducción a D3 para crear una infografía de texto, vamos a comenzar ahora a personalizar los textos. Comienza la combinación de HTML y datos.

Si queremos añadir texto, podemos emplear la función .text(). Si lo añadimos en nuestro script.js incluyendo el texto vivaelsoftwarelibre.com, nos aparecen 44 elementos de la lista con el nombre de esta página web.

elementoLista
	.selectAll("li") // Selección vacía
	.data(partidos)  // hacemos el join
	.enter()         // introducimos los datos
	.append("li")    // introducimos las etiquetas li
	.text("vivaelsoftwarelibre.com")
En esta imagen de la introducción a D3 para crear una infografía se muestra cómo añadir texto desde D3 a un archivo HTML.

Pero lo que nos interesa realmente es que aparezca el texto con los nombres de los partidos políticos. Por ese motivo, dentro de .text() tenemos que añadir una función anónima que nos devuelva esos valores. Como el nombre de la columna con el nombre de los partidos se llama partido, tenemos que hacer alusión expresa al mismo.

elementoLista
	.selectAll("li") // Selección vacía
	.data(partidos)  // hacemos el join
	.enter()         // introducimos los datos
	.append("li")    // introducimos las etiquetas li
	.text(function(d){
		return d.partido
	})

Personalizar el texto con .style()

El texto puede modificarse. Para ello podemos emplear style(), el cuál requiere dos argumentos, el tipo de modificación y su valor. Por ejemplo, si queremos cambiar el tamaño del texto a 9, escribiríamos .style("font-size", 9).

elementoLista
	.selectAll("li") // Selección vacía
	.data(partidos)  // hacemos el join
	.enter()         // introducimos los datos
	.append("li")    // introducimos las etiquetas li
	.text(function(d){
		return d.partido
	})
	.style("font-size", 9)

Ahora bien, si queremos que el tamaño del texto esté en función del número de votantes de cada partido, por lo que nos tenemos que centrar en el segundo valor de la función .style(). Para ello volvemos a emplear una función anónima en ese lugar.

elementoLista
	.selectAll("li") // Selección vacía
	.data(partidos)  // hacemos el join
	.enter()         // introducimos los datos
	.append("li")    // introducimos las etiquetas li
	.text(function(d){
		return d.partido
	})
	.style("font-size",function(d) {
		return d.votantes
	})

Como es lógico, hay partidos con una cantidad de votantes muy por encima que otros. Cuando observamos el Inspector del navegador, vemos los tamaños que adquieren cada uno de los elementos de la lista. El primero de ellos, que se corresponde con el PP, tiene un tamaño de 2509px. Esto hace que exceda los límites de visualización del monitor y de la pantalla.

La escala en D3

Como se ha comprobado, este tipo de visualización es muy poco práctico, ya que tenemos tamaños de más de 2500px y otros de 1px nada más. Aquí es donde viene otro concepto importante que es el de escala. Esta escala nos va a permitir relativizar los valores mínimo y máximo, pudiendo adquirir sus valores la parte proporcional de este intervalo. Mediante la aplicación de la escala eliminamos el problema de visualización que tenemos.

Vamos a crear una escala lineal. Observando el número de votantes de los datos, vemos que el intervalo oscila entre 0 y 3000 votantes. Pues vamos a tomarlos como referencia. Al relativizarlos y escalarlos, vamos a indicar que 0 votantes se corresponda con un tamaño de 5px, y 3000 votantes con un tamaño de 50px.

En D3 tenemos que emplear la función scaleLinear(), la cuál consta de dos partes:

  • .domain(): indicamos el rango de nuestros datos, en este caso de 0 a 3000.
  • .range(): el rango relativo que queremos escalar, en este caso entre 5px y 50px.

En nuestro archivo script.js tenemos que definir una variable con la escala, que vamos a llamar Escala:

var Escala = d3.scaleLinear()
	.domain([0, 3000])
	.range(["5px", "50px"])

Esta variable tenemos que escribirla antes de elementoLista, para poder llamarla a continuación. Sin embargo, podemos automatizar un poco más el proceso. En vez de indicar manualmente el dominio (entre 0 y 3000 votantes), podemos hacer que D3 localice los mínimos y máximos automáticamente. Para es fin se requiere la función .extent(), que localiza el rango mínimo máximo del campo que estemos interesados. Vamos a modiciar la variable anterior llamándola EscalaAuto:

	var EscalaAuto = d3.scaleLinear()
		.domain(d3.extent(partidos, function(d) {
			return d.votantes}))
		.range(["5px", "50px"])

Ahora lo que nos quedaría es incluir la variable con la escala, EscalaAuto, en la función .style().

 .style("font-size", function(d) {
     return EscalaAuto(d.votantes)
 })

Al visualizar la web, se vería ya con la escala relativizada entre 50px (el que más votos tiene) y 5px (el que menos).

Introducción a D3 para crear una infografía. Ejemplo de cómo el tamaño del texto en una lista de HTML se adapta al número de votantes obtenido de un archivo JSON. D3 hace posible conectar datos y HTML.

Código final de script.js en esta introducción a D3 para crear una infografía

Para resumir esta introducción a D3 para crear una infografía de texto, mostramos el código final con todo lo visto de nuestro archivo script.js:

 d3.json("http://output.jsbin.com/lixujex/1.js").then (function (partidos) {
	
	//Confirmamos que los datos están cargados
	console.log("Los datos se han cargado satisfactoriamente")
	
	// Convertimos en global a datos
	window.partidos = partidos
	
	// Generamos el contenedor
	var elementoLista = d3.select("body").append("ul")
	
	// Creamos la escala de tamaño de letra
	var EscalaAuto = d3.scaleLinear()
		.domain(d3.extent(partidos, function(d) {
			return d.votantes}))
		.range(["5px", "50px"])
	
	// Hacemos el join entre datos y etiquetas HTML
	elementoLista
		.selectAll("li") // Selección vacía
		.data(partidos)  // hacemos el join
		.enter()         // introducimos los datos
		.append("li")    // introducimos las etiquetas li
		.text(function(d){
			return d.partido
		})
		.style("font-size", function(d) {
			return EscalaAuto(d.votantes)
		})
})

Si queréis continuar haciendo una figura con D3, no os perdáis esta entrada en la que se explica con mucho detalle cómo crear una bonita gráfica a partir de los mismos datos empleados en esta.

3 comentarios en “Introducción a D3 para crear una infografía”

Deja un comentario