10. Visualización de datos

10.6. Ejemplo práctico de tratamiento de datos de una fuente española

Los datos provenientes de fuentes españolas suelen encontrarse en archivos .csv que no están delimitados por comas, sino por puntos y comas. Vamos a ver un ejemplo que trata muchas de las instrucciones que hemos ido viendo a lo largo del curso y que trabaja con datos de una fuente española.

Para realizar este ejemplo, tomaremos los datos de un repositorio público, en este caso del Ayuntamiento de Madrid. Hemos elegido los datos de accidentes de tráfico donde hay implicadas bicicletas. Estos datos están disponibles en este enlace.

Para hacer este ejemplo, tomamos los datos de 2021, que tiene disponibles todos los meses.

Lo primero que deberemos hacer es un pequeño cambio en el archivo. Por defecto, en España los archivos .csv vienen delimitados por puntos y coma, pero el estándar es que sea una coma. Esto es así porque en España el separador de decimales es una coma, mientras que en los países anglosajones es un punto. Para poder diferenciar la coma decimal de la coma separadora de campos de datos, en España se usa el ; como separador de datos.

Así que lo primero que haremos será abrir el archivo descargado con un editor de hojas de cálculo y volverlo a guardar como texto separado por tabuladores.

Figura 133. Guardando el archivo como texto separado por tabuladores
Fuente: elaboración propia.

Podemos aprovechar que abrimos el archivo para hacernos una idea de cómo están distribuidos los datos y de sus principales características.

Ahora ya podemos subir el archivo a la Mediateca de CodeLab.

Para poderlo usar en nuestro programa, deberemos abrirlo con la instrucción loadTable. Haremos como con las imágenes, crear una variable y, dentro de la función preload(), cargar los datos.

let bicis;
function preload() {
  bicis = loadTable("URL", "tsv", "header");
}

A loadTable le pasamos tres parámetros: URL, que es la dirección de nuestro archivo en el servidor (nos lo indica la Mediateca de CodeLab); «tsv» le dice a loadTable que el archivo está delimitado por tabuladores, y «header» le dice que la primera fila de datos es de cabecera.

Ya tenemos el archivo y ahora vamos a trabajar con él. Vamos a representar el número de accidentes por meses, así que lo que haremos es recorrer todos los datos contando el número de accidentes de cada mes.

Para guardar el número de accidentes de cada mes, crearemos un array de doce posiciones:

let meses = [0,0,0,0,0,0,0,0,0,0,0,0];

Para obtener estos datos, recorreremos toda la tabla de datos mirando la fecha, y nos quedaremos con el mes. Aquí nos servirá saber cómo están guardados los datos. Cuando hemos abierto el archivo, hemos visto que todas las fechas tienen el formato dd/mm/aaaa añadiendo un cero a la izquierda cuando es necesario. Por lo tanto, de la fecha nos quedaremos con los valores de mm, situados en las posiciones cuarta y quinta de la fecha.

let bicis;
let meses = [0,0,0,0,0,0,0,0,0,0,0,0];

function preload() {
   bicis = loadTable("https://codelab.uoc.edu/filemanager/source_repo/ccasadom/AccidentesBicicletas_2021-TAB.txt", "tsv", "header");
}

Veamos cómo queda ahora nuestro programa.

En la función setup() creamos el canvas y llamamos a la función preparación que guardará en la tabla meses cuántos accidentes hay cada mes. Cuatro apuntes:

  • Hemos usado dos métodos que trabajan con los datos getRowCount(), que nos dice cuántos datos hay en bicis, y get(i,j), que recupera el dato situado en la fila i, columna j. Recordad que siempre debemos empezar por 0. Además, como hemos indicado que el archivo tiene cabecera, p5.js directamente ignora la fila de cabecera.
  • Usamos el método slice(x,y), que extrae de la cadena de texto los valores que van desde la posición x hasta la posición y-1.
  • Usamos la función parseInt(t), que convierte el texto t en un número (siempre que t esté compuesto de dígitos).
  • Como la tabla empieza por cero y los meses por 1, resto 1 al número de mes para guardar los datos en la tabla.

Si añadimos la instrucción console.log(meses); después de llamar a la función preparacion(), veremos en la consola la tabla con los datos obtenidos:

Figura 134. Los datos obtenidos de la tabla
Fuente: elaboración propia.

El siguiente paso es visualizar los datos. Para ello, vamos a usar un simple gráfico de barras.

El primer paso consistirá en saber cuál es el valor máximo de los obtenidos. ¿Por qué? Porque así podemos ajustar bien la altura de las barras. Si no sabemos cuál es la altura máxima que debemos representar, el gráfico podría quedar muy pequeño o demasiado grande.

Vamos a crear una función que nos calcule el valor máximo de todos los elementos de un array:

function maxArray(tabla){
  let max = tabla[0];
  for (let i = 1; i < tabla.lenght; i++) { if (tabla[i]>max) {
      max = tabla[i];
    }
  }
  return max; 
}

Ahora ya tenemos los datos que queremos representar y un valor máximo para determinar el valor de las barras. Solo queda representar los datos. Y dibujar es cosa de la función draw().

function draw() {
  background(100);
  fill(255);
  for (var i = 0; i < 12; i++) {
    rect(30+(i*46), 380-map(meses[i],0,max,0,100), 30, map(meses[i],0,max,0,100));
   }
}

Lo que hacemos es, para cada día de la semana, dibujar un rectángulo. El for nos sirve para pintar los siete días. Y el dibujo se hace con una función rect().

Dibujar las barras es un poco complicado, así que iremos viendo todos los parámetros de la función rect() uno a uno. Primero, sin embargo, recordemos cómo es la función rect():

rect(x, y, w, h)
x     Número: coordenada x del rectángulo.
y     Número: coordenada y del rectángulo.
w     Número: ancho del rectángulo.
h     Número: altura del rectángulo.

Recordemos que empezamos a pintar los rectángulos a partir de su vértice superior izquierdo. Bien, veamos ahora los diferentes parámetros. La x:

30+(i*46)

Lo que estamos haciendo es que la primera barra la pondremos a 30 píxeles de la izquierda del canvas. La segunda, 46 píxeles más allá, y así sucesivamente. Como las barras las haremos de 30 píxeles, tendremos una separación de 16 píxeles entre barra y barra. De esta manera, los doce meses quedan bien presentados.

Ahora la y.

380-map(meses[i],0,max,0,100)

Esto es más complicado. Las barras empezarán (visualmente) en los 380 píxeles. Y tendrán una altura máxima de 100 píxeles. Primero, lo que tenemos que hacer es convertir el número de accidentes, que estará entre 0 y max (¿recordáis de dónde viene este valor?), en un valor entre 0 y 100. Esto lo hacemos con la función map(). Con la función map() obtenemos la altura que tendrá la barra. Para decidir en qué posición y hay que empezar, restamos a la posición final (380) la altura de la barra. Y ya sabemos dónde tenemos que empezar a pintar.

Ahora nos falta determinar la anchura:

30

y la altura:

map(meses[i],0,max,0,100)

Que ya habíamos usado antes. El programa queda así:

let bicis;
let meses = [0,0,0,0,0,0,0,0,0,0,0,0];
let max;

function preload() {
  bicis = loadTable("https://codelab.uoc.edu/filemanager/source_repo/ccasadom/AccidentesBicicletas_2021-TAB.txt", "tsv", "header");
}

function setup() {
  createCanvas(600, 400);
  noStroke();
  preparacion();
  max = maxArray(meses);
}

function draw() {
  background(100);
  fill(255);
  for (var i = 0; i < 12; i++) {
    rect(30+(i*46), 380-map(meses[i],0,max,0,100), 30, map(meses[i],0,max,0,100));
   }
}

function preparacion(){
  for (let i = 0; i < bicis.getRowCount(); i++) {
    let fecha = bicis.get(i,1);
    let mes = fecha.slice(3,5);
    meses[parseInt(mes)-1]++; 
  } 
}

function maxArray(tabla){
  let max = tabla[0];
  for (let i = 1; i < tabla.lenght; i++) { if (tabla[i]>max) {
      max = tabla[i];
    }
  }
  return max; 
}

Hasta aquí hemos visto cómo recuperar los datos y trabajar después con estos.

Sin embargo, es verdad que falta información. Sería recomendable al menos indicar a qué mes representa cada barra y el número total de accidentes.

Para ello, haremos que cuando el cursor pase por encima de una barra, se pinte de color rojo y se escriba el día de la semana y el número de accidentes encima, como se puede ver en esta captura:

Figura 135. Visualización interactiva de los datos
Fuente: elaboración propia.

Los cambios que tenemos que hacer afectan a las funciones setup() y draw(). Además, en el inicio hemos añadido una nueva variable:

let nomMes =["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Sepiembre","Octubre","Noviembre","Diciembre"];

Esta variable es una tabla en la que guardamos los nombres de los meses, para usarlos después en la creación del texto.

function setup() {
  createCanvas(600, 400);
  noStroke();
  preparacion();
  max = maxArray(meses);
  textFont("Arial");
  textSize(24);
}

En la función setup() hemos añadido las dos instrucciones que determinan el tipo de letra y el tamaño.

En la función draw() hemos añadido un if para que cuando el ratón esté encima de una barra se escriba el nombre del mes y el número de accidentes que ha habido.

Con este código, podemos bajarnos otro año de datos (siempre y cuando mantenga la misma estructura) y representarlo.

function draw() {
  background(100);
  for (var i = 0; i < 12; i++) { fill(255); rect(30+(i*46), 380-map(meses[i],0,max,0,100), 30, map(meses[i],0,max,0,100)); if ((mouseX > 30+(i*46)) && (mouseX < 30+(i*46)+30)) {
        fill(255,0,0);
        rect(30+(i*46), 380-map(meses[i],0,max,0,100), 30, map(meses[i],0,max,0,100));
        fill("#CECEF6");
        text(nomMes[i] + ": "+meses[i]+" accidentes.", 40, 70);
      }
  }
}