¿Necesitas construir un gráfico con Vega?

Es este pequeño tutorial vamos a explicar los fundamentos básicos de Vega y cómo podemos construir un gráfico de forma sencilla con un resultado visual y que podremos incorporarlo en cualquier Web.

Lo primero de todo, hay que decir que Vega es un lenguaje declarativo que utiliza JSON como formato de entrada mediante el cual podremos crear gráficos en SVG o Canvas usando como motor a D3.

El objeto JSON que vamos a crear mediante Vega consta de las siguientes partes:

  • $schema: esto es una URL en donde se le especifica que librería y versión de Vega estamos utilizando, por ejemplo https://vega.github.io/schema/[library]/[version].json que sería para el caso de la librería Vega y su versión 3 https://vega.github.io/schema/vega/v3.json.
  • description: descripción de la visualización que vamos a crear:
  • width: ancho en pixeles,
  • height: altura en pixeles,
  • padding: padding que queremos dejar entre el gráfico y el recuadro que envolvería el gráfico. Puede ser un número o un objeto JSON con las claves “left”, “top”, “right” y “bottom”.
  • autosize»: en vega 3, podremos especificarle que cambie el tamaño del gráfico cuando cambia el container html en el que está embebido.
  • signals: array donde pondremos las señales o eventos que generá el gráfico. Aquí lo que definiremos serán acciones que realizan los usuarios y que información nos mandan. En el ejemplo quedará más claro.
  • data: array con el conjunto de datos que vamos a mostrar en modo gráfico.
  • scales: array de escalas. Se crearán escalas con los datos, por ejemplo si tenemos valores de Y entre 0 y 10000, podremos generar una escala que vaya desde 0 hasta 10000 donde podremos reflejar los valores.
  • projections: tan solo se utiliza para gráficos en donde estemos visualizando mapas y podremos especificar como queremos que se vea el gráfico.
  • axes: array de los axes del gráfico. Normalmente los axes serán dos que corresponderían a la X e Y de un gráfico. Los axes serían la parte visual de las
  • escalas previamente definidas.
  • legends: array donde definiremos las leyendas que queremos mostrar en el gráfico.
  • marks»: array donde especificaremos todos los componentes visuales del gráfico tales como las líneas, los símbolos de los puntos, rectándulos …

Gráfico Vega a Construir

Una vez ya hemos descrito las partes principales, vamos a ver qué es lo que vamos a construir.

Vamos a definir un gráfico de barras en donde agrupemos los datos por red social y tipo de publicación. Por ejemplo:

  • Twitter-original: 4490
  • Instagram-original: 4157
  • Twitter-compartido: 10823
  • Instagram-compartido: 7500
  • Twitter-respuesta: 955
  • Instagram-respuesta: 5000

El resultado será

gráfico final

Además queremos añadirle que cuando pongamos el ratón encima de una de las barras, esta sea resaltada con color rojo y nos indique el valor exacto del gráfico.

Layout
"$schema": "https://vega.github.io/schema/vega/v3.json",
"width": 500,
"height": 300,
"padding": 10,

Nos creamos un lienzo de 500*300 pixeles para dibujar nuestro gráfico, con un “padding” a los bordes de 10 pixeles por todos sus bordes.

Datos

Vamos a utilizar un dataset sencillo que resume lo que hemos explicado anteriormente en formato JSON y que le daremos el nombre de “table”:

Data: [{
  "values": [{
      "xaxis": "twitter", "y": 10823, "separador": "Compartido"
    },
    {
      "xaxis": "twitter", "y": 4490, "separador": "Original"
    },
    {
      "xaxis": "twitter", "y": 955, "separador": "Respuesta"
    },
    {
      "xaxis": "instagram", "y": 7500, "separador": "Compartido"
    },
    {
      "xaxis": "instagram", "y": 4157, "separador": "Original"
    },
    {
      "xaxis": "instagram", "y": 5000, "separador": "Respuesta"
    }
  ],
  "name": "table"
}]
Señales

Si miramos la especificación de lo que hemos dicho, deberemos de crear la señal cuando el usuario pone el ratón encima de una barra y también cuando la quita para poder darle un color rojo y mostrar el valor exacto.

"signals": [{
  "value": {},
  "name": "tooltip",
  "on": [{
      "update": "{}",
      "events": "rect:mouseout"
    },
    {
      "update": "datum",
      "events": "rect:mouseover"
    }
  ]
}]

Le hemos dado a la señal el nombre de “tooltip” y vemos que es un lenguaje muy claro. Cuando (“on”) suceda que el ratón está dentro del rectángulo (“events: rect:mouseover”) actualiza el valor de tooltip con el dato de la barra (“update: datum”). Lo mismo cuando el ratón deja de estar sobre la barra.

Escalas

Como hemos explicado anteriormente, deberemos de crearnos como mínimo dos escalas, una para cada abscisa.

Abscisa X
{
  "padding": 0.2,
  "domain": {
    "data": "table",
    "field": "xaxis"
  },
  "name": "cat",
  "range": "width",
  "type": "band"
  }
}

Tomaremos del dataset antes definido el campo “xaxis” como referencia y le llamaremos a la escala “cat” con un padding del “20%” entre valores. El rango será a lo ancho y el tipo de escala será “band” que sería lo mismo que decirle que contiene valores ordinales pero de esta forma Vega nos lo pone en un dominio continuo como si se trataran de valores numéricos.

Abscisa Y
{
  "round": true,
  "domain": {
  "data": "table",
  "field": "y"
},
  "name": "val",
  "range": "height",
  "type": "linear",
  "nice": true
}

Al igual que con la “X” le damos un nombre de “val” y le indicamos que el tipo el linear al tratarse de campos numéricos. Los atributos “nice” y “round” son opcionales pero sobre todo el primero ayuda a la escala que creemos quede redondeada. Por ejemplo si tenemos el valor 10823 nos creará una escala hasta el 11000. Por el contrario el “round” en este caso no nos sirve de nada porque no utilizamos números con decimales, con lo cual podríamos incluso eliminarlo.

Escala de colores

Por último vamos a definir una escala en donde le asignaremos un color a cada tipo de dato à original, respuesta y compartido.

Para ello creamos una escala con nombre “color” de tipo “ordinal” (no hace falta que sea band) donde usaremos la columna “separador” del dataset para asignarle un color.

En el rango de colores, Vega admite esquemas propios o

{
  "name": "color",
  "domain": {
  "data": "table",
  "field": "separador"
},
  "range": [
  "rgb(80,73,213)",
        "rgb(41,157,214)",
        "rgb(184,38,197)"
      ],
      "type": "ordinal"
    }

 

Abscisas

Ya hemos creado las escalas, ahora tan solo necesitamos usarlas para dibujarlas.

"axes": [
    {
      "scale": "cat",
      "orient": "bottom"
    },
    {
      "scale": "val",
      "orient": "left"
    }
  ]

La único particular aquí es que la orientación no se pone “X” e “Y” sino que deberemos de decirle “left” y “bottom” pudiendo cambiarlos siempre de sitio de forma muy sencilla.

Leyenda

Queremos crear una leyenda que nos indique que significa cada barra y con que color se asocia, para ello no tenemos más que usar la escala con nombre “color” que hemos creado dado que esta ya nos indica un color por tipo de contenido.

"legends": [
  {
    "orient": "right",
    "fill": "color"
  }
]

En “orient” le podemos decir donde poner la leyenda, en “fill” le indicamos que lo rellene con la escala “color”. Si querríamos añadirle alguna propiedad, siempre podríamos hacerlo con el atributo “encode” tal y como lo veremos en las “marks”.

Una vez llegados a este punto veamos que forma tiene nuestro gráfico previo a dibujar su contenido por medio de las “marks”.

gráfico con estructura base

Como se puede ver, tiene muy buena pinta a falta de las barras.

Marcas o Marks

Aquí viene la magia de Vega y es que podemos crear una marca de tipo “group” que nos permitirá separar los datos en función a un field y con ello poder crearnos nuevas escalas y nuevas propiedades para esos datos.

Comenzamos definiendo una marca de tipo group:

"marks": [
  {
    "type": "group"
  }

A esta marca le añadimos varias cosa, lo primero de todo es dividir los datos por red social

"from": {
  "facet": {
    "data": "table",
    "name": "facet",
    "groupby": "xaxis"
  }
}

Facet es una propiedad que tan solo se puede usar dentro de una marca agrupada y nos ayuda a dividir los datos en función a un campo.

Creamos una escala con los nuevos datos

"scales": [
  {
    "name": "pos",
    "domain": {
      "data": "facet",
      "field": "separador"
    },
    "range": "width",
    "type": "band"
  }
]

Es exactamente igual que “cat” pero ahora lo hacemos sobre los datos separados y en el rango width. Ahora bien el “width” queremos que sea solo el que le corresponde a cada res social. Para ello deberemos de modificar el “bandwidth” con una señal de la siguiente forma donde cat es la escala que contiene a las redes sociales.

"signals": [
  {
    "name": "width",
    "update": "bandwidth('cat')"
  }
]

El siguiente paso es decirle donde se van a situar las marcas de tipo “rect” o barras:

"encode": {
  "enter": {
    "x": {
      "field": "xaxis",
      "scale": "cat"
    }
  }
}

Con esto le indicamos que se pondrán sobre la escala “cat” (eje X) en función del valor “xaxis” o red social.

Por último solo nos queda dibujar las dos marcas de tipo “rect” y “text” para dibujar las barras y el texto que nos indica el valor exacto cuando pongamos el cursos encima del rectángulo.

Marca de barras (rect)

Usando el dataset “facet” creado recientemente, creamos una marca de tipo “rect”.

"name": "bars",
"from": {
  "data": "facet"
},
"type": "rect"

Sus propiedades son muy sencillas:

  • Enter: el conjunto de propiedades en este apartado tan solo se ejecuta una vez cuando se cargan los datos
    • Cuando alguien ponga el ratón encima, este cambiará a la forma pointer
    • Su posición X será la del valor “separador” en la escala “pos” que acabamos de crear
    • El ancho que tendrá la barra
    • La coordenadas Y e Y2 de la barra, es decir la parte de abajo y de arriba
"enter": {
  "cursor": {
    "value": "pointer"
  },
  "x": {
    "field": "separador",
    "scale": "pos"
  },
  "width": {
    "offset": -1,
    "scale": "pos",
    "band": true
  },
  "y": {
    "scale": "val",
    "value": 0
  },
  "y2": {
    "field": "y",
    "scale": "val"
  },
}
  • Update: se ejecutará siempre que se produzca un cambio en cualquier cambio de instancias de marcas. En ese caso y dado que vamos a cambiar el color de las barras con “rojo” es necesario volver a actualizarlas con el color que les corresponde
    • Ponemos el color que tendrá la barra. Corresponderá con el valor de “separador”
"update":{
  "fill": {
    "field": "separador",
    "scale": "color"
  }
}
  • Hover: esta propiedad nos permitirá cambiar de color de la barra cuando estemos encima. En este caso el rojo
"hover": {
  "fill": {
    "value": "red"
  }
}

Marca de texto

gráfico con resaltado del textoCorresponde al número que veremos cuando ponemos el ratón sobre una de las barras tal y como se puede apreciar en la imagen.

Su definición no es muy compleja pero tiene alguna peculiaridad.

Lo primero de todo es que esta marca usará la marca anterior como fuente:

 

"type": "text",
"from": {
  "data": "bars"
}

Su definición es la siguiente:

  • Enter:
    • Posición “y” será el mismo que el que “bars”
    • La posición de “x” será el mismo que en bars pero para que se vea centrado le aplicaremos un offset
    • Los número serán en negro con un alineado centrado y el punto de offset será “middle” o mitad.
"enter": {
  "y": {
    "field": "y",
    "offset": -5
  },
  "x": {
    "field": "x",
    "offset": {
      "field": "width",
      "mult": 0.5
    }
  },
  "fill": {
    "value": "black"
  },
  "align": {
    "value": "center"
  },
  "baseline": {
    "value": "middle"
  }
}
  • Update:
    • Mostrár el texto “tooltip.y”, es decir el valor de la barra.
    • Si no le ponemos un fillOpacity, eso haría que siempre que ponemos el ratón sobre una barra, mostraría todos los valores. Por ese motivo, tan solo mostraremos aquel número que coincida con la barra en la que estamos. Recordad que tooltip es la señal que se genera cuando ponemos el ratón sobre una de las barras.
"update": {
  "text": {
    "signal": "tooltip.y"
  },
  "fillOpacity": [
    {
      "test": "datum.datum == tooltip",
      "value": 1
    },
    {
      "value": 0
    }
  ]
}

Conclusión

Si has seguido todos los pasos arriba descritos, habrás podido crear un gráfico de barras agrupado.

En el siguiente link spec.json podréis descargar la especificación completa

 

 

Comparte esto en...

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *