Editor de gráficas Vega

Ir al Editor

Hace varios meses, tras estar un poco desconectado de los avances de la visualización de datos con tecnologías web, descubrí Vega. Se trata de una librería javascript que trabaja con d3js para construir visualizaciones de datos. Es una gramática que usa como base JSON y es capaz de hacer cualquier tipo de gráfica. Se puede manipular datos filtrándolos, ordenando, transformando, agregando o pivotándolos. Tableau no es nada, en comparación. Claro que para usuarios sin experiencia en javascript ni tecnologías frontend, usar algo como Vega sería imposible o de ser posible, sería un gran dolor. Eso sí, sería más barato. Como con cualquier librería de software libre, Vega es extremadamente poderoso y puede superar a cualquier producto de BI del mercado gracias a su flexibilidad y calidad, pero siempre y cuando uno sepa lo que hace.

Han hecho un par de herramientas con Vega: Lyra y Voyager.  Lyra es bastante usable, pero usa una versión anterior de Vega, mientras que Voyager es una herramienta para explorar posibles visualizaciones, pero no permite hacer tantas cosas con su GUI. También pueden usar el editor oficial.

Para facilitarme las cosas he hecho un editor de gráficos con Vega que puede exportar el resultado de SVG a un PNG con el doble de la resolución puesta. Esto me será útil para mi trabajo, y a lo mejor le sirve a alguien más. Es bastante sencillo, y es parecido al editor que ya existe, pero me permite agregar datos crudos en formato CSV y puede correr offline sin que se publiquen los datos en línea. Además, editando el código HTML y poniendo los datos y la especificación puedo compartir el documento HTML con otras personas de manera privada (lamentablemente no todo es open data 😔 ).

El editor tiene dos cajas de texto. Una arriba en donde se pone los datos crudos (puede ser un CSV, por ejemplo) y una abajo en donde se ponen la especificación en formato JSON. Para armar esto he unido varios pedazos de código cuyas referencias pueden verse en el código fuente. La herramienta está aquí.

Para verlo funcionando pueden probar este código, que genera la imagen del post:

Dataset:

a,b,c,d
A,10,77,X
A,30,56,X
A,50,19,X
B,10,23,X
B,30,29,X
B,50,33,X
C,10,15,X
C,30,34,X
C,50,5,X
A,10,17,Y
A,30,86,Y
A,50,29,Y
B,10,53,Y
B,30,49,Y
B,50,13,Y
C,10,95,Y
C,30,24,Y
C,50,55,Y

Specification:

{
  "$schema": "https://vega.github.io/schema/vega/v4.json",
  "width": 600,
  "height": 400,
  "padding": 5,
  "config": {
    "background": "#333",
    "axis": {
      "labelColor": "#ccc",
      "titleColor": "#ccc"
    },
    "legend": {
      "fillColor": "#eee", "padding": 10, "symbolOffset":10,
      "labelColor": "#444"
    }
  },
  "data": [
    { "name": "table",
      "format": {"type": "csv", "parse": "auto"},
      "transform": [
        { "type": "collect", "sort": {"field": "b", "order":"ascending"}
}
      ]
    }
  ],

  "scales": [
    {
      "name": "dashes",
      "type": "ordinal",
      "range": ["1,0", "5,2"],
      "domain": ["X", "Y"]
    },
    {
      "name": "x",
      "type": "point",
      "range": "width",
      "domain": {"data": "table", "field": "b"}
    },
    {
      "name": "y",
      "type": "linear",
      "range": "height",
      "nice": true,
      "zero": true,
      "domain": {"data": "table", "field": "c"}
    },
    {
      "name": "color",
      "type": "ordinal",
      "range": "category",
      "domain": {"data": "table", "field": "a"}
    }
  ],

  "axes": [
    {
      "orient": "bottom", "scale": "x",
      "labelFontSize": 14,
      "title": "Label X", "titleFontSize": 15,
      "grid":true,
      "gridOpacity": 0.6,
      "encode": {
          "grid": {
              "update": {
                "stroke": {"value": "#666"}
              }
          } 
       }
    },
    {
      "orient": "left", "scale": "y",
      "labelFontSize": 14,
      "title": "Label Y", "titleFontSize": 16,
      "grid": true,
      "encode": {
          "grid": {
              "update": {
                "stroke": {"value": "#666"},
                "opacity": {"value": 0.5}
              }
          } 
       }
    }
  ],

  "marks": [
    {
      "type": "group",
      "from": {
        "facet": {
          "name": "series",
          "data": "table",
          "groupby": ["a", "d"]
        }
      },
      "marks": [
        {
          "type": "line",
          "from": {
            "data": "series"
          },
          "encode": {
            "enter": {
              "x": {"scale": "x", "field": "b"},
              "y": {"scale": "y", "field": "c"},
              "stroke": {"scale": "color", "field": "a"},
              "strokeWidth": {"value": 2}, "strokeDash": {"scale": "dashes", "field":"d"} 
            },
            "hover": {
              "fillOpacity": {"value": 0.5}
            }
          }
        }
      ]
    }
  ],
  "legends": [
    {
      "stroke": "color",
      "encode": {
        "title": {
          "update": {
            "fontSize": {"value": 14}
          }
        }
      },
      "orient": "right",
      "offset": -100,
      "symbolType": "M-3,0H1",
      "symbolStrokeWidth": { "value": 3}
    },
    {
      "strokeDash": "dashes",
      "encode": {
        "title": {
          "update": {
            "fontSize": {"value": 14}
          }
        }
      },
      "orient": "right",
      "offset": -100,
      "symbolType": "M-3,0H1",
      "symbolStrokeWidth": { "value": 3}
    }
  ]
}