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

En esta nueva versión de mi gráfica realizada con D3.js, voy a añadirle un nuevo gráfico, superpuesto al anterior.

El resultado final

El resultado final es el siguiente:

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

El nuevo gráfico superpuesto

Hasta ahora, en todas las versiones que he ido realizando, he usado como base una gráfica lineal con la evolución temporal del índice IBEX35:

A mí, personalmente, me resulta muy interesante ver cómo evoluciona esta gráfica: subidas, bajadas, nuevas subidas, nuevas bajadas, a veces parece que va consolidando valores… para caer de nuevo… y volver a subir.

Hay gente que decide ganarse la vida apostando a la evolución de estas gráficas. E intenta encontrar pautas en la evolución pasada de los valores, para predecir los valores futuros (tanto a corto como a medio plazo). A mí, personalmente, no me atrae ese tipo de estrategias. Pero había una cuestión que me rondaba la cabeza:

  • Supongamos que confío en la evolución positiva del IBEX35. Detrás del IBEX35 no deja de haber empresas. Supongamos que son empresas serias. Y puedo suponer que van a hacer bien su labor (que ya es mucho suponer). Y, por lo tanto, van a aportar valor a sus accionistas (que es una manera muy elegante de decir «que va a subir la valoración»).
  • Además, no estoy buscando subidas aceleradas. Ninguna empresa madura puede aumentar su valoración real de forma acelerada. Del mismo modo, pienso que la valoración bursátil razonable debe subir gradualmente, en concordancia con los valoración real de las empresas.
  • Y este aumento de valoración, no puede ser conseguido a corto plazo, sino a medio o largo plazo. Está claro que el mercado sesga la valoración de estas empresas (a veces, demanda acciones y dispara el valor; otras veces, entra el pánico y el valor cae). Incluso factores externos a la empresa en sí, afectan a su valor (por ejemplo, la «marca España» puede ser cuestionada en ciertos momentos… pero hay algunas empresas que tienen tanto o más negocio fuera de España que dentro de ella). Pero entre momentos de euforia y de pánico debe ir hacia el punto de estabilidad (que debería coincidir con la valoración real de las empresas).

En este contexto, imaginemos que invierto en el IBEX35 y dejo esa inversión madurar, por ejemplo, un año ¿Es razonable pensar que la revalorización de esa inversión puede ser, al menos, similar a si hubiera invertido ese capital en un producto de menor riesgo (letras del tesoro, depósitos financieros,…)?

Para investigar esta última pregunta, quería superponer a mi gráfico original un segundo gráfico:

  • Defino un porcentaje de revalorización.
  • Para cada punto de mi gráfica, busco el momento futuro en el que se consigue esa revalorización (si es que se consigue alguna vez).
  • El número de días transcurridos, para cada punto, me da esta segunda gráfica.
  • Como el rango de días necesario puede ser muy amplio, he definido en el interfase un segundo slider que permite indicar un número máximo de días (que me sirve como tope superior para mi nueva gráfica.

Jugando con la gráfica, la casuística que nos podemos encontrar es grande:

  • Si decidimos un porcentaje de revaloración del 4% y un plazo de 365 días (hace un par de años, no era extraño encontrar depósitos financieros a un año que te daban este rendimiento, sin ningún tipo de riesgo). Con estos valores, la mayor parte de las veces sí que se cumplen las expectativas. Pero también hay períodos concretos que no se cumplen.
  • Si esperamos una revalorización que compense al riesgo (supongamos un 10% de revalorización), el número de períodos que cumplen las expectativas todavía sigue siendo grande.
  • Pero si hubieras invertido entre los años 2007 y 2008 … desgraciadamente todavía no habrás recuperado tu inversión.

Bueno, dejémonos de divagaciones filosóficas y vamos al grano… digo… al código.

El código html

El código html necesario es similar al de la versión anterior, excepto que incluyo dos nuevos sliders con id returnRage y daysLimit para poder introducir la revalorización deseada y el tiempo máximo en que espero conseguirla.

El código javascript

El código javascript es similar a la versión anterior, excepto que ampliado para incluir el nuevo gráfico superpuesto. Bueno, además he aprovechado esta nueva versión para introducir un método fundamental de D3.js: el método data.

Vamos a ir leyéndolo, poco a poco.

Definición variables globales

Las variables globales son las mismas que la versión anterior más nuevas variables para los dos nuevos parámetros (returnRate, daysLimit, returnRateLabel y daysLimitLabel).

Definición de los sliders

Al slider scrollSlider que ya existía en la versión previa, añadimos los dos nuevos sliders returnRate y daysLimit.

Definición de los elementos de la gráfica

Los elementos necesarios, son los mismos más:

  • Un nuevo scale vertical para nuestro nuevo gráfico. Lo he llamado yRangeDays. Para que el código sea más claro, el scale vertical que ya existía lo he renombrado yRangeValues.
  • Un nuevo axis vertical yAxisDays que dibujaré en el lado derecho. El axis existente lo he renombrado yAxisValues.
  • Un nuevo line dayFunc que usaré para dibujar mi segunda gráfica.

Definición del popup

La mecánica para la definición del popup que aparece conforme se mueve el ratón sobre la gráfica, sigue igual. Excepto que he ampliado su contenido. A los dos datos que ya mostraba (point.date y point.high he añadido la información asociada a una nueva propiedad point.returnPoint (creo que es fácil de entender: más adelante, calculo para cada punto de mi gráfica el punto cuando se consigue la revalorización deseada y lo guardo en la nueva propiedad point.returnPoint; además, guardo los días de diferencia en la nueva propiedad point.days).

Adapto mi gráfica al espacio disponible en el navegador

Como en versiones anteriores, la función redrawsSvg se encarga de calcular las dimensiones disponibles y de adaptar los distintos elementos a estas dimensiones:

La única diferencia es que debo adaptar también el nuevo eje vertical (de clase y.axis.right) y la nueva escala para los días yRangeDays.

Dibujo el gráfico principal

La función redrawData sigue encargándose de dibujar el gráfico principal.

Pero ahora hay dos diferencias fundamentales:

  • Una vez que ha calculado los datos filtrados filteredData los pasa al método svg.data.
  • La gráfica, se genera de forma ligeramente distinta a la versión anterior.

Comparemos la versión anterior con la nueva:

La versión anterior, era relativamente fácil de comprender. Pero la nueva versión requiere una explicación más detallada.

El método d3.data

d3.js es una librería orientada a representar gráficamente series de valores. En este contexto, es natural esperar que exista una correlación directa entre los valores representados y los elementos del documento. Por ejemplo:

  • Si la serie tiene 10 valores, hacen falta diez columnas.
  • La altura de cada columna debe ser proporcional al valor asociado a esa columna.

(Por lo general, estos elementos asociados a nuestras series de valores serán elementos svg pero no hay inconveniente en que usemos otros elementos estándares como div, p, ul, etc).

Para facilitar este tipo de correlaciones, d3.js dispone del método data:

  • Permite asociar un array de valores a un elemento del documento (que actúa como «padre» o «contenedor»).
  • Una vez asociados, dispone de funciones adicionales que permiten coordinar los valores con los elementos que representarán a estos valores.

(Esto está explicado muy someramente. Haría falta una entrada completa para explicarlo detenidamente).

En nuestro caso:

  • Tenemos nuestros valores filtrados en la variable filteredData.
  • Asocio dichos valores al elemento contenedor svg.
  • Cuando genero la gráfica, sé que a la función callback que le paso function(d) { return valueFunc(d); } D3.js le pasa al argumento d los valores asociados previamente. Hasta esta versión de mi gráfica, ignoraba dicho parámetro. Pero en esta versión, uso este parámetro para pasarle los valores a mi función valueFunc.

Un último detalle importante: el método data espera que se le pase como parámetro un array de datos, que posteriormente podremos mapear en una lista de elementos del DOM (por cada elemento del array, asociará un elemento del documento). Pero, en este caso, tenemos un solo elemento (el elemento path.daily-values) que espera como dato un array (que usaremos rellenar su atributo d para generar la gráfica). Por eso, al método data nosotros le pasamos un array con un solo elemento (que es nuestro array de valores).

Siento que la explicación no sea más extensa. Como he dicho antes, el método data es uno de los más potentes de D3.js y explicarlo detenidamente necesitaría de una entrada propia.

Calculo los datos para la nueva gráfica

La función calcReturnDays es la encargada de buscar para cada punto, el punto futuro cuando se consigue la revalorización esperada ¿Os acordáis que cuando se generaba el popup, usaba dos nuevas propiedades de cada punto, days y returnPoint? Pues esta función es la responsable de calcularlos:

Podría haber optimizado mucho más esta función. Pero como no aprecio retrasos apreciables en la representación de la gráfica, he preferido dejarlo así porque creo que es más lineal para comprenderlo.

Dibujo la segunda gráfica

La función redrawNumDays es la responsable de dibujar la segunda gráfica:

Su código es muy similar a redrawData con las siguientes diferencias:

  • Sólo invoca a calcReturnDays para recalcular los datos asociados si se le pasa su argumento recalc a true (por ejemplo, cuando se varíen los sliders se recalcularán los datos, pero no se recalcularán si varían las fechas límites).
  • Usa la misma escala horizontal xRange que la gráfica principal pero usa su propia escala vertical yRangeDays (que es por lo tanto la única que tiene que configurar).

Y, por último, la diferencia fundamental: acordaros que en la función redrawData ya asociamos los datos al elemento contenedor svg:

Esta asociación de datos que hemos realizado es permantente. Esto quiere decir que los datos a representar ya están asociados al elemento svg. Por lo tanto, para dibujar nuestra segunda gráfica, sólo tenemos que usarlos.

Es la misma operativa que cuando dibujamos la gráfica principal:

Excepto que para dibujar la segunda gráfica usamos la función dayFunc.

Inicialización de la gráfica

El código para inicializar la gráfica es similar a la versión anterior, excepto que tiene que llamar a redrawNumDays para dibujar la gráfica secundaria:

¿Conclusiones?

Esta nueva versión, he aprovechado para incluir una segunda gráfica. Esto quiere decir que tenemos escalas y ejes adicionales para esta nueva gráfica. Las dos gráficas usan el mismo conjunto de datos, excepto que cada una usa propiedades distintas (la gráfica principal usa date y high mientras que la gráfica secundaria usa date y days). Por eso, he usado el método data para asociar los datos al contenedor svg para que así los dos elementos path que representan cada gráfica busquen los datos que necesitan de este contenedor padre.

Como he dicho antes, el métod data es muy potente. En esta práctica, me he limitado a usarlo ligeramente. A ver si en próximas versiones se me ocurren nuevas excusas para extender su uso con otros ejemplos.

Deja una respuesta

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

*
*
Sitio Web