Filtrar contenidos por

Uso de WebCola y D3.js para crear diseños jerárquicos

Un buen diseño es esencial para descubrir la estructura inherente a los datos conectados. Los diseños jerárquicos se utilizan normalmente para mostrar las relaciones padre-hijo entre los nodos del gráfico. D3.js off-the-shelf D3.js implementación de diseño jerárquico no se puede utilizar en el caso de los datos del mundo real en el que un nodo hijo puede tener múltiples nodos padre. Usamos WebCola y D3.js para dibujar el diseño jerárquico del gráfico usando la especificación declarativa de restricciones en unas pocas líneas de código.

El mundo real está ricamente interconectado y creo que nuestras interfaces de usuario deben imitar esas conexiones. Las conexiones en red transmiten información. Un simple vistazo a la red, si se representa correctamente, puede descubrir estructuras inherentes que no son visibles superficialmente. Dada la naturaleza altamente conectada de los datos de nuestros clientes, en Alyne decidimos invertir algún tiempo en una visualización escalable que representara los datos y las conexiones entre ellos de forma intuitiva.

Uno de los casos de uso que más lo aprovechará en Alyne es nuestra Biblioteca de objetos. Permitimos a nuestros clientes replicar sus activos digitales y físicos en la biblioteca de objetos de nuestra aplicación. Por lo general, estos activos son de naturaleza jerárquica, e identificamos que el diagrama de red puede ser una buena herramienta para permitir a los clientes tener una vista de pájaro sobre su paisaje de objetos.

Visualización de redes

Una de las formas más sencillas y potentes de visualizar las redes es en una red dirigida por la fuerza. Los grafos dirigidos por la fuerza reorganizan los nodos y las aristas de forma que haya una cantidad mínima de solapamientos de nodos e intersecciones de aristas. Los grafos dirigidos por la fuerza son una buena representación de las redes, ya que desentrañan patrones comunes como agrupaciones de nodos, nodos de alto grado, componentes conectados, etc., pero no pueden representar la información jerárquica de forma directa.

Existen varias bibliotecas excelentes, como D3, para calcular la información de posición de los trazados de red.

En este post, nos centraremos especialmente en la representación jerárquica de los datos. D3 proporciona fuera de la caja de diseño jerárquico para calcular las posiciones de los nodos de forma jerárquica. Sin embargo, el diseño jerárquico trabaja con datos que están en formato de árbol (estructura implícita padre-hijo), con la restricción de que un nodo no debe tener más de un padre. Por lo tanto, no podemos utilizar d3-hierarchy para crear diseños en forma de árbol para denotar información jerárquica cuando la estructura de datos se parece a un gráfico.

Podemos resolver este problema mediante el aprovechamiento de la fuerza D3 diseño dirigido con el fin de lograr la no superposición de nodos y poner restricciones adicionales en las posiciones geométricas de nodos para lograr el diseño jerárquico. Se puede lograr puramente en D3, sin embargo, es un poco tedioso para escribir las restricciones en JavaScript.

Hay una forma más fácil: WebCola.

WebCola nos ofrece una forma de especificar la información de disposición en términos de restricciones geométricas. Además, genera dinámicamente restricciones para el trazado de flujos, nodos no solapados, etc., con el fin de despejar el diagrama de red. Me gusta WebCola porque es sencillo y flexible de utilizar.

Comencemos con un simple grafo d3 force layout con los siguientes datos. Como puedes ver, no hay una estructura padre-hijo inherente en estos datos. Por lo tanto, para organizar los nodos de forma jerárquica, introducimos una propiedad de nivel por nodo; denota la posición de un nodo en una jerarquía.

Vamos a mostrar un diseño dirigido de fuerza de vainilla para los siguientes datos utilizando la biblioteca de diseño de fuerza D3.

const nodes = [
  { id: 1, level: 1 }, { id: 2, level: 2 },
  { id: 3, level: 2 }, { id: 4, level: 2 },
  { id: 5, level: 3 }, { id: 6, level: 3 },
  { id: 7, level: 3 }, { id: 8, level: 3 }, 
  { id: 9, level: 3 }, { id: 10, level: 3 },
  { id: 10, level: 3 }, { id: 11, level: 4 },
  { id: 12, level: 4 }, { id: 13, level: 4 },
  { id: 14, level: 4 }, { id: 15, level: 4 },
  { id: 16, level: 4 }
];
const links = [
  { start: 1, end: 2 },
  { start: 1, end: 3 },
  { start: 1, end: 4 },
  { start: 2, end: 5 },
  { start: 2, end: 6 },
  { start: 3, end: 7 },
  { start: 3, end: 8 },
  { start: 3, end: 9 },
  { start: 4, end: 5 },
  { start: 4, end: 9 },
  { start: 4, end: 10 },
  { source: 5, target: 11 },
  { source: 5, target: 12 },
  { source: 7, target: 13 },
  { source: 8, target: 14 },
  { source: 10, target: 15 },
  { source: 10, target: 16 },
  { source: 9, target: 16 },
];

En aras de la observabilidad, en la visualización anterior mostramos los nodos con el mismo level con el mismo color, y ajustamos el radio del nodo en función del atributo level es decir, cuanto mayor sea el level menor será el radio.

Como se puede ver en el diagrama, el diseño dirigido por la fuerza sólo genera la restricción para los nodos no superpuestos con la ayuda de varias fuerzas. Pero no hay manera fácil en D3, fuera de la caja, para organizar los datos jerárquicamente basado en la propiedad exógena del nodo.

Podemos aprovechar WebCola para añadir las restricciones geométricas para alinear los nodos en diferentes niveles en función de su propiedad de nivel.

Restricciones de WebCola

Existen diferentes categorías de restricciones en WebCola tales como alineamiento, agrupamiento, restricciones de igualdad, etc. Vamos a utilizar las restricciones de alineación y las restricciones de desigualdad para alinear los nodos con el mismo nivel a lo largo del eje y. Estas pueden ser especificadas en JSON como sigue:

{
  "type": "alignment",
  "axis": "y",
  "offsets": [
    { "node": 1, "offset": 0 },
    { "node": 2, "offset": 0 },
    { "node": 3, "offset": 0 }
  ]
}

Esta restricción especificada alinearía los nodos con índices 1,2,y 3 a lo largo del eje y, es decir, horizontalmente. offset denota el desplazamiento del centro del nodo que se suele utilizar en caso de nodos de tamaño variable.

Intentemos ahora organizar nuestra red en forma de árbol. Tenemos que tener dos tipos de restricciones como sigue:

  1. Restricción de alineación - Queremos que los nodos con la misma propiedad de nivel tengan el mismo y coordenadas
  2. Restricción de igualdad - Los nodos del mismo nivel deben tener un margen a la izquierda y a la derecha para mejorar la legibilidad.

Así, en nuestro ejemplo, agrupamos los nodos por atributo de nivel y, para cada nodo del mismo grupo, especificamos una restricción de alineación a lo largo del eje y. Además, en cada grupo asignamos una restricción de igualdad posicional a lo largo del eje x, con una separación de 50 píxeles. Además, en cada grupo, asignamos una restricción de igualdad posicional a lo largo del eje x con una separación de 50 píxeles, lo que nos ayuda a desordenar los nodos del mismo nivel. El valor de la separación puede obtenerse dinámicamente en función del tamaño del nodo, pero se trata de una decisión específica del renderizado.

const constraints = [];
const { nodes, links } = loadData();
const groups = _.groupBy(nodes, "level");

for (const level of Object.keys(groups)) {
  const nodeGroup = groups[level];
  const constraint = {
    type: "alignment",
    axis: "y",
    offsets: [],
  };
  let prevNodeId = -1;
  for (const node of nodeGroup) {
    constraint.offsets.push({
      node: _.findIndex(nodes, (d) => d.id === node.id),
      offset: 0,
    });

    if (prevNodeId !== -1) {
      constraints.push({
        axis: "x",
        left: _.findIndex(nodes, (d) => d.id === prevNodeId),
        right: _.findIndex(nodes, (d) => d.id === node.id),
        gap: 50,
      });
    }

    prevNodeId = node.id;
  }

  constraints.push(constraint);
}

d3Cola
  .nodes(nodes)
  .links(links)
  .constraints(constraints)
  .flowLayout("y", 80)
  .linkDistance(50)
  .symmetricDiffLinkLengths(40)
  .avoidOverlaps(true)
  .on("tick", ticked)
  .start(10, 40, 50);

Las restricciones basadas en el atributo de nivel del nodo son restricciones definidas por el usuario. Además de estas restricciones definidas por el usuario, WebCola genera dinámicamente restricciones para determinar la longitud de los enlaces (linkDistance, flowLayout), la restricción de no solapamiento (avoidOverlaps), etc.

Realizamos la simulación aplicando restricciones generadas dinámicamente durante 10 iteraciones, restricciones definidas por el usuario durante 40 iteraciones y ambos conjuntos de restricciones juntos durante 50 iteraciones. Estos números pueden variarse heurísticamente en función del número de nodos y restricciones de la simulación.

Con estas restricciones, obtenemos una disposición similar a un árbol utilizando la simulación dirigida por fuerza a pesar de no tener la estructura de datos similar a un árbol. Se puede ver que los nodos con el mismo valor de nivel tienen el mismo color y están situados a la misma altura. Además, nuestra visualización es estable a varios nodos que tienen múltiples padres.

Esto es sólo un vistazo de cómo WebCola se puede utilizar para aplicar diferentes diseños al diagrama de red en unas pocas líneas de código. Te recomendaría que echaras un vistazo a SetCola que es un lenguaje de alto nivel para crear restricciones WebCola y con unas pocas líneas de código, puedes crear diseños de gráficos complejos.

[content_block id=53166]

Estamos aquí para ayudar

Póngase en contacto con nosotros y responderemos a cualquier pregunta sobre cómo Mitratech apoya su éxito.

Póngase en contacto con nosotros