gopherdata / gophernotes

The Go kernel for Jupyter notebooks and nteract.
MIT License
3.84k stars 265 forks source link

Directly support display MIME types for Vega and Vega-lite JSON rendering? #203

Open vsivsi opened 4 years ago

vsivsi commented 4 years ago

Hi, I've finally worked out how to use gophernotes to display Vega / VegaLite SVG graphics, (crudely) similar to what the Altair Python package can do. This works "out of the box" in Nteract and JupyterLab. In classic Jupyter notebooks I believe it requires installing a Jupyter extension to support Vega(lite).

Anyway, thanks to the excellent example documentation for the Gophernotes display support package, I was able to figure out how to make this work:

// Support for Vega...
type Vega struct { 
    Json map[string]interface{}
}

func (v Vega) SimpleRender() display.MIMEMap {
    return display.MIMEMap{
        `application/vnd.vega.v5+json`: v.Json,
        `text/plain`: "<Vega 5 object>",
    }
}

// ...and VegaLite
type VegaLite struct { 
    Json map[string]interface{}
}

func (vl VegaLite) SimpleRender() display.MIMEMap {
    return display.MIMEMap{
        `application/vnd.vegalite.v4+json`: vl.Json,
        `text/plain`: "<VegaLite 4 object>",
    }
}

To use, simply create and populate one of the above types:

import "encoding/json"

g := []byte(`
    {
        "$schema": "https://vega.github.io/schema/vega-lite/v2.0.json",
        "description": "A simple bar chart with embedded data.",
        "data": {
          "values": [
            {"a": "A", "b": 28},
            {"a": "B", "b": 55},
            {"a": "C", "b": 43},
            {"a": "D", "b": 91},
            {"a": "E", "b": 81},
            {"a": "F", "b": 53},
            {"a": "G", "b": 19},
            {"a": "H", "b": 87},
            {"a": "I", "b": 52}
          ]
        },
        "mark": "bar",
        "encoding": {
          "x": {"field": "a", "type": "ordinal"},
          "y": {"field": "b", "type": "quantitative"}
        }
 }`)

vl := VegaLite{}
err := json.Unmarshal(g, &vl.Json)
vl

visualization

It would be cool if this was actually built into Gophernotes, or at least documented, since Vega(Lite) are pretty world class declarative plotting libraries and this opens them up for use with gophernotes!

vsivsi commented 4 years ago

Heres what it looks like in an actual notebook:

https://gist.github.com/vsivsi/f63be32d037e3effcd7c0e04aacda2cf

cosmos72 commented 4 years ago

Definitely very useful! Also, the structs Vega and VegaLite you wrote above are perfect for inclusion in Gophernotes, and they do not even depend on some Vega library.

The only part I did not understand is: how do you configure jupyter to make it recognize MIME types application/vnd.vega.v5+json and application/vnd.vegalite.v4+json? I.e. does jupyter know about them "out of the box", or you need to execute some procedure?

vsivsi commented 4 years ago

Jupyterlab and Nteract both support it out-of-the-box. Apparently their front-end JS bundles already include recent versions of Vega and VegaLite. To make it work with classic Jupyter notebooks you need to install a Jupyter extension. I think this is the one, but I haven't tried it.

https://github.com/vega/ipyvega

vsivsi commented 4 years ago

After posting I realized that the wrapping struct in my code is redundant. This works just as well:

import "encoding/json"

type VegaLite map[string]interface{}

func (v VegaLite) SimpleRender() display.MIMEMap {
    return display.MIMEMap{
       "application/vnd.vegalite.v4+json": v,
       "text/plain": "<VegaLite 4 object>",
    }
}

graph := []byte(`
{
   "$schema": "https://vega.github.io/schema/vega-lite/v4.0.json",
   "description": "A simple bar chart with embedded data.",
   "data": {
      "values": [
         {"a": "A", "b": 28},
         {"a": "B", "b": 55},
         {"a": "C", "b": 43},
         {"a": "D", "b": 91},
         {"a": "E", "b": 81},
         {"a": "F", "b": 53},
         {"a": "G", "b": 19},
         {"a": "H", "b": 87},
         {"a": "I", "b": 52}
      ]
   },
   "mark": "bar",
   "encoding": {
      "x": {"field": "a", "type": "ordinal"},
      "y": {"field": "b", "type": "quantitative"}
   }
}`)

v := VegaLite{}
json.Unmarshal(graph, &v)

v