Search code examples
pythonvegavega-litealtair

Vega not rendering spec consistently


Scenario

I am producing charts in python using Altair. However, when I save the charts to HTML and open the file, the rendered chart is different. In particular, the legend is horizontal in the HTML file while it was vertical when rendered in my Jupyter Notebook. What's more is that when I use the link created int he HTML to use the Vega editor, it is again rendered with a vertical legend.

What is missing from the HTML to make the vega-lite render with a vertical legend?

My Python Code

import altair as alt
import json

chart = alt.Chart.from_dict(json.loads("""
    {
      "config": {"view": {"width": 400, "height": 300}},
      "data": {
        "values": [
          {"X": "Thing1", "Y": "ThatA", "Value": 1},
          {"X": "Thing1", "Y": "ThatB", "Value": 3},
          {"X": "Thing2", "Y": "ThatA", "Value": 2},
          {"X": "Thing2", "Y": "ThatB", "Value": 4}
        ]
      },
      "mark": "bar",
      "encoding": {
        "color": {"type": "quantitative", "field": "Value"},
        "x": {"type": "nominal", "field": "X"},
        "y": {"type": "nominal", "field": "Y"}
      },
      "height": 300,
      "width": 500,
      "$schema": "https://vega.github.io/schema/vega-lite/v2.4.3.json"
    }"""
))

chart

Renders As

enter image description here

Save

chart.savechart('test.html')

Produces

<!DOCTYPE html>
<html>
<head>
  <style>
    .vega-actions a {
        margin-right: 12px;
        color: #757575;
        font-weight: normal;
        font-size: 13px;
    }
    .error {
        color: red;
    }
  </style>

<script src="https://cdn.jsdelivr.net/npm//[email protected]"></script>
<script src="https://cdn.jsdelivr.net/npm//[email protected]"></script>
<script src="https://cdn.jsdelivr.net/npm//[email protected]"></script>

</head>
<body>
  <div id="vis"></div>
  <script type="text/javascript">
    var spec = {"config": {"view": {"width": 400, "height": 300}}, "data": {"values": [{"X": "Thing1", "Y": "ThatA", "Value": 1}, {"X": "Thing1", "Y": "ThatB", "Value": 3}, {"X": "Thing2", "Y": "ThatA", "Value": 2}, {"X": "Thing2", "Y": "ThatB", "Value": 4}]}, "mark": "bar", "encoding": {"color": {"type": "quantitative", "field": "Value"}, "x": {"type": "nominal", "field": "X"}, "y": {"type": "nominal", "field": "Y"}}, "height": 300, "width": 500, "$schema": "https://vega.github.io/schema/vega-lite/v2.4.3.json"};
    var embed_opt = {"mode": "vega-lite"};

    function showError(el, error){
        el.innerHTML = ('<div class="error">'
                        + '<p>JavaScript Error: ' + error.message + '</p>'
                        + "<p>This usually means there's a typo in your chart specification. "
                        + "See the javascript console for the full traceback.</p>"
                        + '</div>');
        throw error;
    }
    const el = document.getElementById('vis');
    vegaEmbed("#vis", spec, embed_opt)
      .catch(error => showError(el, error));
  </script>
</body>
</html>

If you run that snippet you'll see a chart with a horizontal legend. If you follow the link to edit, you'll see a vertical legend again like this:

enter image description here


Solution

  • Vega-Lite changed the way it renders legends in version 2.5. Previously, continuous legends were vertical, and in version 2.5 and newer, the legends are horizontal. So the root of the issue is that you are using different versions of Vega-Lite in the two cases.

    The version of vega-lite used by the JupyterLab or Jupyter notebook frontend is controlled by the frontend extension, which is not part of the Altair library. If you want an updated renderer, then you can update the frontend extension (this is either the vega-extension if you are using JupyterLab, or the ipyvega package if you are using Jupyter notebook).

    When you call save() on the other hand, it produces HTML which does not depend on the frontend, but loads up-to-date javascript libraries from the web.

    If you would like to explicitly choose a vertical legend orientation within Vega-Lite 2.5 or newer, you can use "legend": {"direction": "vertical"} within the color encoding.