vega / vl-convert

Utilities for converting Vega-Lite specs from the command line and Python
BSD 3-Clause "New" or "Revised" License
96 stars 12 forks source link

refactoring: remove vega and vegalite wrappers #42

Closed mattijn closed 1 year ago

mattijn commented 1 year ago

Continuation of the discussion that started from this comment and onwards.

Vega 5 specifications can currently be rendered using Altair, with support of interactivity:

import altair.vega.v5 as alt
import json
vg_spec = r"""
{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A basic bar chart example, with value labels shown upon mouse hover.",
  "width": 400,
  "height": 200,
  "padding": 5,
  "data": [
    {
      "name": "table",
      "values": [
        {"category": "A", "amount": 28},
        {"category": "B", "amount": 55},
        {"category": "C", "amount": 43},
        {"category": "D", "amount": 91},
        {"category": "E", "amount": 81},
        {"category": "F", "amount": 53},
        {"category": "G", "amount": 19},
        {"category": "H", "amount": 87}
      ]
    }
  ],
  "scales": [
    {
      "name": "xscale",
      "type": "band",
      "domain": {"data": "table", "field": "category"},
      "range": "width",
      "padding": 0.05,
      "round": true
    },
    {
      "name": "yscale",
      "domain": {"data": "table", "field": "amount"},
      "nice": true,
      "range": "height"
    }
  ],
  "axes": [
    {"orient": "bottom", "scale": "xscale"},
    {"orient": "left", "scale": "yscale"}
  ],
  "marks": [
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "xscale", "field": "category"},
          "width": {"scale": "xscale", "band": 1},
          "y": {"scale": "yscale", "field": "amount"},
          "y2": {"scale": "yscale", "value": 0}
        },
        "update": {"fill": {"value": "steelblue"}},
        "hover": {"fill": {"value": "red"}}
      }
    }
  ]
}
"""
alt.vega(json.loads(vg_spec))

image

Now, one can also use vl-convert (pip/conda install vl-convert-python) to render these as png or svg in a notebook:

import vl_convert as vlc
from IPython.display import Image
from IPython.display import SVG, display

#vg_png = vlc.vega_to_png(vg_spec)  # Image(vg_png)
vg_svg = vlc.vega_to_svg(vg_spec)
display(SVG(vg_svg))

image But as far as I can tell it does not support the interactivity (btw just like a svg-export from the Vega Editor with above example).

Introducing errors in the spec, propagate JavaScript warnings back to the notebook, in both cases: Changing eg: "from": {"data": "table"}, to "from": {"data": "FOO"}, Results with Altair variant:

Javascript Error: Undefined data set name: "FOO"

And with vl-convert variant:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[19], line 6
      3 from IPython.display import SVG, display
      5 #vg_png = vlc.vega_to_png(vg_spec)  # Image(vg_png)
----> 6 vg_svg = vlc.vega_to_svg(vg_spec)
      7 display(SVG(vg_svg))

ValueError: Vega to SVG conversion failed:
Error: Undefined data set name: "FOO"
    at b (https://cdn.skypack.dev/-/vega-util@v1.17.0-uRskU0IBL2vWCP4Va8OC/dist=es2020,mode=imports,min/optimized/vega-util.js:1:324)
    at ue.getData (https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:45069)
    at At (https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:21597)
    at Sa (https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:21499)
    at Bt (https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:24270)
    at https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:39099
    at Array.forEach (<anonymous>)
    at hn (https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:39088)
    at al (https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:39762)
    at Module.sl (https://cdn.skypack.dev/-/vega-parser@v6.1.4-PbzshukasZitnbnp29GQ/dist=es2020,mode=imports,min/optimized/vega-parser.js:1:47716)

Since it is not the core of Altair, we can decide for maintenance purposes to remove the Vega (currently v5) wrappers and leave this to vl-convert if people like to make use of this within a notebook environment.

Altair had until this commit support in the main version on Github support for the following Vega-Lite versions: Altair Version Vega-Lite Version Vega Version
4.3.0.dev0 v3.4.0, v4.17.0, v5.2.0 v5.21.0

Three versions of Vega-Lite seems too many, and we are still discussing if we can reduce it to only a single version of Vega-Lite. But the v3 version is obsolete anyway. These packages/tests can safely be removed from main.

binste commented 1 year ago

Maybe this issue is better placed in the Altair repo?

For completeness: JupyterLab has builtin support for Vega files which can be rendered with interactivity:

image

JupyterLab docs on this

You can also plot Vega specs in a notebook without converting to svg or png first so you still have the interactivity:

import json
from IPython.display import display

vg_spec = r"""
{
  "$schema": "https://vega.github.io/schema/vega/v5.json",
  "description": "A basic bar chart example, with value labels shown upon mouse hover.",
  "width": 400,
  "height": 200,
  "padding": 5,
  "data": [
    {
      "name": "table",
      "values": [
        {"category": "A", "amount": 28},
        {"category": "B", "amount": 55},
        {"category": "C", "amount": 43},
        {"category": "D", "amount": 91},
        {"category": "E", "amount": 81},
        {"category": "F", "amount": 53},
        {"category": "G", "amount": 19},
        {"category": "H", "amount": 87}
      ]
    }
  ],
  "scales": [
    {
      "name": "xscale",
      "type": "band",
      "domain": {"data": "table", "field": "category"},
      "range": "width",
      "padding": 0.05,
      "round": true
    },
    {
      "name": "yscale",
      "domain": {"data": "table", "field": "amount"},
      "nice": true,
      "range": "height"
    }
  ],
  "axes": [
    {"orient": "bottom", "scale": "xscale"},
    {"orient": "left", "scale": "yscale"}
  ],
  "marks": [
    {
      "type": "rect",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "xscale", "field": "category"},
          "width": {"scale": "xscale", "band": 1},
          "y": {"scale": "yscale", "field": "amount"},
          "y2": {"scale": "yscale", "value": 0}
        },
        "update": {"fill": {"value": "steelblue"}},
        "hover": {"fill": {"value": "red"}}
      }
    }
  ]
}
"""

display({
    "application/vnd.vega.v5+json": json.loads(vg_spec)
}, raw=True)
image

The above code with display also works in VS Code and presumably a few other notebook frontends.

mattijn commented 1 year ago

How did this end up at the vl-convert repo? That was unintentional. I will move it to the Altair repo! Done per https://github.com/altair-viz/altair/issues/2817