Search code examples
vertical-alignmentvega-litestacked-chart

How to vertically align Vega-Lite stacked bar chart data labels


How can I align data labels in a stacked bar chart to be centered vertically within each bar segment? In the following example, you can see that the text is positioned at the top of each bar segment. Where a bar segment is thin, the data label overlaps the one below. I realize that having multiple adjacent thin segments would result in overlap even if labels were centered vertically, but that case is unlikely with my data set.

{
  "$schema": "https://vega.github.io/schema/vega-lite/v2.json",
  "data": {
    "values": [
      {"Value": 0.321, "Date": "09/30/2021", "Measure": "Measure 4"},
      {"Value": 0.031, "Date": "09/30/2021", "Measure": "Measure 3"},
      {"Value": 0.123, "Date": "09/30/2021", "Measure": "Measure 2"},
      {"Value": 0.475, "Date": "09/30/2021", "Measure": "Measure 1"}
    ]
  },
  "width": 500,
  "height": 250,
  "resolve": {"scale": {"color": "independent"}},
  "layer": [
    {
      "mark": "bar",
      "encoding": {
        "y": {
          "field": "Value",
          "type": "quantitative",
          "axis": {"format": ".1%"}
        },
        "x": {"field": "Date", "type": "nominal", "axis": {"labelAngle": -45}},
        "color": {"field": "Measure", "type": "nominal"}
      }
    },
    {
      "mark": {"type": "text"},
      "encoding": {
        "y": {"field": "Value", "type": "quantitative", "stack": "zero"},
        "x": {"field": "Date", "type": "nominal"},
        "color": {
          "field": "Measure",
          "type": "nominal",
          "scale": {"range": ["white"]},
          "legend": null
        },
        "text": {
          "aggregate": "sum",
          "field": "Value",
          "type": "quantitative",
          "format": ".1%"
        }
      }
    }
  ]
}

Solution

  • You can do this using a stack and calculate transform. The text layer would look like this:

        {
          ...
          "transform": [
            {"stack": "Value", "groupby": ["Date"], "as": ["lower", "upper"]},
            {"calculate": "(datum.lower + datum.upper) / 2", "as": "midpoint"}
          ],
          "encoding": {
            "y": {"field": "midpoint", "type": "quantitative"},
            ...
          }
        }
    

    You can see the full spec in the vega editor:

    enter image description here