Practicando con D3.js y datos del IBEX35 – Parte VII

En la versión anterior de mi gráfica realizada usando las librerías D3.js añadimos una nueva gráfica a la gráfica principal. Esto, nos sirvió como excusa para practicar con un método fundamental de las librerías D3.js: el método d3.data.

El resultado final, sin embargo, no me gustaba demasiado: no lograba representar lo suficientemente claro lo que pretendía: detectar las zonas en las que se habían cumplido las condiciones de revalorización introducidas. A partir de este momento, es cuando interviene la creatividad para conseguir visualizar los datos de una forma más efectiva. Los límites, no los pone la librería D3.js, los límites los pones tú.

Debo reconocer que la parte creativa no es precisamente mi punto fuerte (igual me pegué un golpe de crío en la parte derecha de la cabeza). Pero, de todos modos, hago lo que puedo. Esta vez, voy a sustituir la gráfica secundaria por una nueva versión mediante colores:

  • Si el punto cumplió las condiciones de revalorización en el plazo pedido, aparece tendiendo al color RGB(205,235,139).
  • Si consiguió la revaloración pero fuera de plazo, aparece tendiendo al color RGB(252,63,30).
  • Y si no consigue la revalorización, es marcado de color RGB(246,246,246).

A ver si os gusta más (y sobre todo, os gusta el método para programar esta nueva variante).

El resultado final

El resultado final es el siguiente:

Con los datos por defecto (revalorización del 4% y un plazo de 730 días) casi todas las áreas aparecen marcadas en verde (si se hubiera entrado en esos momentos, se hubiera conseguido la rentabilidad del 4% en ese plazo), hay algunas áreas marcadas en rojo (se consiguió la revalorización, pero se tardó más de ese límite de tiempo) y hay algunas áreas (finales del año 2007 y finales del 2009) que resaltan en blanco (que todavía no se ha conseguido esa revalorización).

Podéis abrir este mismo resultado en una ventana independiente aquí.

El código html

El código html continúa sigue siendo el mismo que en la versión anterior, así que no lo repito.

El código Javascript

El código Javascript completo es el siguiente (por motivos didácticos, veréis que he dejado el código correspondiente a la versión anterior de la gráfica secundaria, comentado):

Vamos a comenzar a estudiarlo, poco a poco. En las explicaciones, nos centraremos en las diferencias con la versión anterior, para comprender cómo la hemos modificado para introducir la nueva representación.

Definición de variables globales

Las variables globales que hemos necesitado, son las misma que la versión anterior:

Definición de los sliders

La definición de los sliders, también es igual:

Definición de los elementos de la gráfica

Los elementos SVG que hemos definido son los mismos, excepto que hemos añadido un nuevo elemento g de la clase return-days:

Más adelante, veremos cómo lo usamos para crear nuestra nueva gráfica. Es importante tener en cuenta el detalle de que lo creo antes que el elemento path de clase daily-values (que es el que contiene mi gráfica principal) para que mi gráfica secundaria se visualice por detrás de mi gráfica principal.

Veréis que están comentados los elementos SVG y los objetos D3.js que necesitaba para la versión anterior de la gráfica secundaria, pero que ya no necesito para mi nueva versión.

Definición del popup

La definición del popup tampoco ha variado (excepto que he quitado el circulito con el que marcaba la gráfica secundaria):

Adapto mi gráfica al espacio disponible en el navegador

Esta parte del código, tampoco ha variado (excepto que ya no necesito configurar el objeto yRangeDays):

Dibujo el gráfico principal

El dibujo del gráfico principal, tampoco ha variado:

Sólo insistir en un detalle que ya expliqué en la versión anterior, pero que es muy importante: el conjunto de datos a mostrar (que está referenciado por la variable filteredData es asignado al objeto svg (que es el contenedor principal) usando el método data de D3.js. Tal como explicamos, cuando dibujamos la gráfica le pedimos que use esos datos que hemos asignado:

Estos datos que hemos asignado, permanecen en el elemento svg y ya veremos que también los usamos para dibujar mi gráfica secundaria.

Calculo los datos para la nueva gráfica

El método de cálculo de los datos para mi gráfica secundaria, sigue siendo el mismo:

Dibujo la segunda gráfica

Y, por fin, aquí es donde encontramos las diferencias:

  • El recálculo de los datos, sigue igual.
  • La configuración del objeto yRangeDays y el dibujo del eje vertical derecho los he eliminado.

A continuación, viene el dibujo del nuevo mapa de colores. Básicamente, lo que haré será dibujar un rectangulo que se expanda a todo lo largo de la gráfica y centrado en el punto. El ancho, lo calcularé en función de la separación en píxeles entre entre puntos consecutivos (si hay muchos puntos, será una simple línea; si hay pocos puntos, el rectángulo se extenderá 2 píxeles a la izquierda y derecha del punto a marcar). El color de este rectángulo dependerá del valor days a codificar.

Primero, unos cálculos preliminares:

  • Calculo la variable width (la anchura del rectángulo que quiero dibujar será 2 * width + 1). Básicamente, calculo el espacio horizontal (en pixeles) entre dos puntos de la gráfica (la variable slotWidth). Si esta diferencia es mayor o igual a 5 píxeles, width valdrá la mitad de esta diferencia (con un tope de 2 píxeles). Si es menor que 5 píxeles, el valor de width será 0 píxel (el rectángulo se convierte en una línea).
  • Después, defino la función calcColor. Esta función, es la encargada de calcular el color asociado a cada valor de mi gráfica secundaria (en función del valor de la propiedad d.days):
    • Si el valor es null (lo que ocurre cuando no ha conseguido la revalorización que queríamos), el color asignado es RGB(246,246,246).
    • Si el valor es 0, el color calculado será RGB(205,235,139).
    • Si el valor es igual o mayor al límite daysLimit, el color calculado será RGB(252,63,30).
    • Para el resto de valores entre 0 y daysLimit, interpolará el valor.

Ahora, viene lo interesante: ya insistí antes que los valores a representar ya estaban asociados al objeto contenedor svg. Y, dentro de este contenedor, ya había creado previamente un elemento g de la clase return-days que voy a usar como contenedor de los rectángulos coloreados. Lo que hago, es solicitar la operación selectAll sobre este objeto g:

Esta simple operación, tiene muchísimo que estudiar:

  • Primero, seleccciono el objeto g.return-days (que, como acabo de decir, es el contenedor de los rectángulos).
  • A esta selección, le pido los objetos rect que contiene.
  • A esta selección de objetos rect, le aplico la operación data y le asocio los valores ya asignados al objeto contenedor svg.
  • Por fin, guardo el resultado de esta última operación en la variable rects para operar con ella.

Cuando hemos pedido la operación .selectAll('rect').data(function(d) { return d; }); hemos creado una estructura interna de D3.js que «empareja» cada objeto rect encontrado con uno de nuestros datos. Este «emparejamiento», puede tener tres alternativas:

  • Que por cada objeto rect haya un dato emparejado.
  • Que haya más objetos rect que datos.
  • Que haya más datos que objetos rect.

(Por ejemplo, cuando la gráfica esté recién creada, habrá 0 objetos rect y habrá un dato por cada día del histórico de datos, o sea, un «desparejamiento» completo)

Se supone que debe haber tantos objetos como datos. Para conseguir este equilibrio, D3.js nos proporciona una serie de métodos que podemos operar sobre el objeto rects.

Con el método enter estamos pidiéndole a D3.js “por cada dato que no esté emparejado, crea un objeto rect”:

IMPORTANTE: al método enter podemos pedirle lo que nos dé la gana. En este caso, le hemos pedido append('rect') para emparejar cada dato «huérfano» con un nuevo rectángulo porque es lo que queremos, pero insisto que enter no sabe si estamos emparejando rectángulos o círculos o… lo que queramos.

También hemos aplicado la operación exit:

Esta operación, lo que hace es borrar los objetos encontrados y que no tengan dato emparejado. O sea: borro los rectángulos que sobren. Vuelvo a insistir en el mismo punto que antes: D3.js no sabe qué tipo de objetos estamos emparejando, sólo sabe que hicimos un emparejamiento entre objetos rect y datos (.selectAll('rect').data(function(d) { return d; });) y le pedimos que borre los objetos que no tengan emparejado datos.

La última operación que nos falta por explicar es:

Esta operación se aplica a todos los objetos rect existentes (y cada uno, como insisto, emparejado a un dato). Para cada objeto rect, defino sus características:

  • Su anchura es 2 * width + 1.
  • Su altura, será la de la gráfica (menos el espacio reservado para los bordes superior e inferior).
  • Su posición x será la posición del punto a la que restamos el valor de width (para que el rectángulo esté centrado en el punto).
  • Su color, lo calculo mediante la función calcColor que definí previamente y que mapeará el valor asociado al punto con un color.

Inicialización de la gráfica

La inicialización no ha variado respecto de la versión anterior:

¿Conclusiones?

Esta nueva representación de la gráfica secundaria nos ha dado una oportunidad para profundizar en las capacidades de D3.js para representar gráficamente cualquier dato (sólo limitados por nuestra imaginación y dominio de las librerías).

D3.js está orientada a representar datos mediante elementos HTML. Estos elementos, pueden ser cualquier elemento HTML. Pero, por lo general, usaremos elementos SVG. Para ayudarnos con estas tareas de representar datos mediantes elementos HTML, D3.js tiene métodos especialmente preparados para ello.

En la versión anterior de la gráfica, ya presentamos el método D3.data que nos permite asignar valores a un objeto para poder posteriormente usar estos valores asociados. Nuestras gráficas, las construímos fácilmente porque usamos el método D3.line que nos permite construir un objeto path donde cada punto está asociado a un dato. Por eso, construir estas gráficas era relativamente directo.

Esta vez, hemos construido una representación gráfica usando un objeto rect por cada dato. Para ello, conforme varían los datos, tenemos que añadir más objetos rect (usando D3.enter) o borrar lo que ya no son necesarios (usando D3.exit).

Estos métodos de D3.js (data, enter, exit) requerirían de entradas específicas para explicarlos detenidamente. Pero, por lo menos, con la excusa de nuestra gráfica, espero haberos dado la oportunidad de conocerlos e introduciros en la operativa básica de D3.js.

Deja una respuesta

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

*
*
Sitio Web