Search code examples
jsonchartsvisualizationvega-lite

How can you have multiple levels in an axis in vega-lite?


Is it possible in Vega-Lite to have multiple (hierarchical) facets of a dimension on one axis or would that require Vega?

Desired structure

Best result I found so far was concatenating the information, which is effective but way less appealing:

All labels are repeated every row

If I create row-facets, two problems arise: 1) I cannot nest facets, therefore this does not work for 3 or more aspects. 2) all labels are repeated even if there's no data, eg Copenhagen, Apples, Thin Air in the example.


Solution

  • As suggested, using hconcat provided a (somewhate convoluted) solution combined with lag in aggregate.

    Example: like this

    {
      "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
      "data": {
        "values": [
          {
            "BI_City": "Amsterdam",
            "BI_Sector": "Cars",
            ...
            etc
            ...
            "Source": "Thin air",
            "Start": "2015-01-01",
            "End": "2019-01-01"
          }
        ]
      },
      "hconcat": [
        {
          "transform": [
            {"aggregate": [], "groupby": ["BI_City", "BI_Sector", "Source"]},
            {
              "window": [
                {"op": "row_number", "as": "rownum"},
                {"op": "lag", "field": "BI_City", "as": "prevCity"},
                {"op": "lag", "field": "BI_Sector", "as": "prevSector"}
              ],
              "sort": [
                {"field": "BI_City"},
                {"field": "BI_Sector"},
                {"field": "Source"}
              ]
            },
            {
              "calculate": "join([datum.rownum, datum.BI_City, datum.BI_Sector, datum.Source, datum.prevCity, datum.prevSector], '|')",
              "as": "rowkey"
            }
          ],
          "height": 500,
          "mark": {"type": "bar", "tooltip": false, "color": "transparent"},
          "encoding": {
            "y": {
              "field": "rowkey",
              "sort": {"field": "rownum"},
              "title": null,
              "axis": {
                "domain": false,
                "grid": false,
                "ticks": false,
                "labelExpr": "split(datum.label, '|')[1] == split(datum.label, '|')[4] ? '' : split(datum.label, '|')[1]"
              }
            }
          }
        },
        {
          "transform": [
            {"aggregate": [], "groupby": ["BI_City", "BI_Sector", "Source"]},
            {
              "window": [
                {"op": "row_number", "as": "rownum"},
                {"op": "lag", "field": "BI_City", "as": "prevCity"},
                {"op": "lag", "field": "BI_Sector", "as": "prevSector"}
              ],
              "sort": [
                {"field": "BI_City"},
                {"field": "BI_Sector"},
                {"field": "Source"}
              ]
            },
            {
              "calculate": "join([datum.rownum, datum.BI_City, datum.BI_Sector, datum.Source, datum.prevCity, datum.prevSector], '|')",
              "as": "rowkey"
            }
          ],
          "height": 500,
          "mark": {"type": "bar", "tooltip": false, "color": "transparent"},
          "encoding": {
            "y": {
              "field": "rowkey",
              "sort": {"field": "rownum"},
              "title": null,
              "axis": {
                "domain": false,
                "ticks": false,
                "labelExpr": "split(datum.label, '|')[1] == split(datum.label, '|')[4] && split(datum.label, '|')[2] == split(datum.label, '|')[5] ? '' : split(datum.label, '|')[2]"
              }
            }
          }
        },
        {
          "transform": [
            {"aggregate": [], "groupby": ["BI_City", "BI_Sector", "Source"]},
            {
              "window": [
                {"op": "row_number", "as": "rownum"},
                {"op": "lag", "field": "BI_City", "as": "prevCity"},
                {"op": "lag", "field": "BI_Sector", "as": "prevSector"}
              ],
              "sort": [
                {"field": "BI_City"},
                {"field": "BI_Sector"},
                {"field": "Source"}
              ]
            },
            {
              "calculate": "join([datum.rownum, datum.BI_City, datum.BI_Sector, datum.Source, datum.prevCity, datum.prevSector], '|')",
              "as": "rowkey"
            }
          ],
          "height": 500,
          "mark": {"type": "bar", "tooltip": false, "color": "transparent"},
          "encoding": {
            "y": {
              "field": "rowkey",
              "sort": {"field": "rownum"},
              "title": null,
              "axis": {
                "domain": false,
                "ticks": false,
                "labelExpr": "split(datum.label, '|')[3]"
              }
            }
          }
        },
        {
          "width": 800,
          "height": 500,
          "transform": [
            {
              "calculate": "join([datum.BI_City,datum.BI_Sector,datum.Source],'|')",
              "as": "rowkey"
            }
          ],
          "mark": {
            "type": "bar",
            "height": {"band": 0.5},
            "stroke": "black",
            "strokeWidth": 0.3
          },
          "encoding": {
            "y": {
              "field": "rowkey",
              "sort": "ascending",
              "axis": {"grid": true, "tickBand": "extent", "labels": false},
              "title": null
            },
            "x": {
              "field": "Start",
              "type": "temporal",
              "axis": {"grid": false, "title": "Year"}
            },
            "x2": {"field": "End"}
          }
        }
      ],
      "config": {
        "concat": {"spacing": 0},
        "view": {"fill": "transparent", "stroke": null}
      }
    }