gregsexton / ob-ipython

org-babel integration with Jupyter for evaluation of (Python by default) code blocks
737 stars 111 forks source link

Rendering Altair/Vega graphs #178

Closed ghost closed 6 years ago

ghost commented 6 years ago

I'm trying to use Altair (which uses the Vega JSON spec for graphs) per https://altair-viz.github.io/gallery/simple_heatmap.html. Running the code in Emacs generates the javascript code below as a source block, rather than rendering the image. Altair has the ability to download PNG files directly, which may be useful here.

Incidentally, also makes very cool interactive graphics in JS for the browser. It'd be really nice to render the static PNG graphs in ob-ipython. (Of course it'd be super cool to get the interactive ones too, but I have no idea how to make work.)

Has anybody tried getting Vega-style graphs working in ob-ipython? I've looked around a bit but didn't see any examples. Thanks!

#+BEGIN_SRC ipython
import altair as alt
import numpy as np
import pandas as pd
#+END_SRC

#+BEGIN_SRC ipython
alt.__version__
#+END_SRC

#+RESULTS:
:results:
# Out[5]:
# text/plain
: '2.0.1'
:end:

#+BEGIN_SRC ipython
alt.renderers.enable('notebook')
#+END_SRC

#+RESULTS:
:results:
# Out[6]:
# text/plain
: RendererRegistry.enable('notebook')
:end:

#+BEGIN_SRC ipython

# Compute x^2 + y^2 across a 2D grid
x, y = np.meshgrid(range(-5, 5), range(-5, 5))
z = x ** 2 + y ** 2

# Convert this grid to columnar data expected by Altair
data = pd.DataFrame({'x': x.ravel(),
                     'y': y.ravel(),
                     'z': z.ravel()})

alt.Chart(data).mark_rect().encode(
    x='x:O',
    y='y:O',
    color='z:Q'
)
#+END_SRC

#+RESULTS:
:results:
# Out[8]:
# text/plain
: 

# text/html
#+BEGIN_EXPORT html
<div class="vega-embed" id="ad2a6b0d-dee7-4313-b8b3-723238c1c84a"></div>

<style>
.vega-embed .vega-actions > a {
    transition: opacity 200ms ease-in;
    opacity: 0.3;
    margin-right: 0.6em;
    color: #444;
    text-decoration: none;
}

.vega-embed .vega-actions > a:hover {
    color: #000;
    text-decoration: underline;
}

.vega-embed:hover .vega-actions > a {
    opacity: 1;
    transition: 0s;
}

.vega-embed .error p {
    color: firebrick;
    font-size: 1.2em;
}
</style>

#+END_EXPORT

# application/javascript
#+BEGIN_SRC javascript
var spec = {"config": {"view": {"width": 400, "height": 300}}, "data": {"values": [{"x": -5, "y": -5, "z": 50}, {"x": -4, "y": -5, "z": 41}, {"x": -3, "y": -5, "z": 34}, {"x": -2, "y": -5, "z": 29}, {"x": -1, "y": -5, "z": 26}, {"x": 0, "y": -5, "z": 25}, {"x": 1, "y": -5, "z": 26}, {"x": 2, "y": -5, "z": 29}, {"x": 3, "y": -5, "z": 34}, {"x": 4, "y": -5, "z": 41}, {"x": -5, "y": -4, "z": 41}, {"x": -4, "y": -4, "z": 32}, {"x": -3, "y": -4, "z": 25}, {"x": -2, "y": -4, "z": 20}, {"x": -1, "y": -4, "z": 17}, {"x": 0, "y": -4, "z": 16}, {"x": 1, "y": -4, "z": 17}, {"x": 2, "y": -4, "z": 20}, {"x": 3, "y": -4, "z": 25}, {"x": 4, "y": -4, "z": 32}, {"x": -5, "y": -3, "z": 34}, {"x": -4, "y": -3, "z": 25}, {"x": -3, "y": -3, "z": 18}, {"x": -2, "y": -3, "z": 13}, {"x": -1, "y": -3, "z": 10}, {"x": 0, "y": -3, "z": 9}, {"x": 1, "y": -3, "z": 10}, {"x": 2, "y": -3, "z": 13}, {"x": 3, "y": -3, "z": 18}, {"x": 4, "y": -3, "z": 25}, {"x": -5, "y": -2, "z": 29}, {"x": -4, "y": -2, "z": 20}, {"x": -3, "y": -2, "z": 13}, {"x": -2, "y": -2, "z": 8}, {"x": -1, "y": -2, "z": 5}, {"x": 0, "y": -2, "z": 4}, {"x": 1, "y": -2, "z": 5}, {"x": 2, "y": -2, "z": 8}, {"x": 3, "y": -2, "z": 13}, {"x": 4, "y": -2, "z": 20}, {"x": -5, "y": -1, "z": 26}, {"x": -4, "y": -1, "z": 17}, {"x": -3, "y": -1, "z": 10}, {"x": -2, "y": -1, "z": 5}, {"x": -1, "y": -1, "z": 2}, {"x": 0, "y": -1, "z": 1}, {"x": 1, "y": -1, "z": 2}, {"x": 2, "y": -1, "z": 5}, {"x": 3, "y": -1, "z": 10}, {"x": 4, "y": -1, "z": 17}, {"x": -5, "y": 0, "z": 25}, {"x": -4, "y": 0, "z": 16}, {"x": -3, "y": 0, "z": 9}, {"x": -2, "y": 0, "z": 4}, {"x": -1, "y": 0, "z": 1}, {"x": 0, "y": 0, "z": 0}, {"x": 1, "y": 0, "z": 1}, {"x": 2, "y": 0, "z": 4}, {"x": 3, "y": 0, "z": 9}, {"x": 4, "y": 0, "z": 16}, {"x": -5, "y": 1, "z": 26}, {"x": -4, "y": 1, "z": 17}, {"x": -3, "y": 1, "z": 10}, {"x": -2, "y": 1, "z": 5}, {"x": -1, "y": 1, "z": 2}, {"x": 0, "y": 1, "z": 1}, {"x": 1, "y": 1, "z": 2}, {"x": 2, "y": 1, "z": 5}, {"x": 3, "y": 1, "z": 10}, {"x": 4, "y": 1, "z": 17}, {"x": -5, "y": 2, "z": 29}, {"x": -4, "y": 2, "z": 20}, {"x": -3, "y": 2, "z": 13}, {"x": -2, "y": 2, "z": 8}, {"x": -1, "y": 2, "z": 5}, {"x": 0, "y": 2, "z": 4}, {"x": 1, "y": 2, "z": 5}, {"x": 2, "y": 2, "z": 8}, {"x": 3, "y": 2, "z": 13}, {"x": 4, "y": 2, "z": 20}, {"x": -5, "y": 3, "z": 34}, {"x": -4, "y": 3, "z": 25}, {"x": -3, "y": 3, "z": 18}, {"x": -2, "y": 3, "z": 13}, {"x": -1, "y": 3, "z": 10}, {"x": 0, "y": 3, "z": 9}, {"x": 1, "y": 3, "z": 10}, {"x": 2, "y": 3, "z": 13}, {"x": 3, "y": 3, "z": 18}, {"x": 4, "y": 3, "z": 25}, {"x": -5, "y": 4, "z": 41}, {"x": -4, "y": 4, "z": 32}, {"x": -3, "y": 4, "z": 25}, {"x": -2, "y": 4, "z": 20}, {"x": -1, "y": 4, "z": 17}, {"x": 0, "y": 4, "z": 16}, {"x": 1, "y": 4, "z": 17}, {"x": 2, "y": 4, "z": 20}, {"x": 3, "y": 4, "z": 25}, {"x": 4, "y": 4, "z": 32}]}, "mark": "rect", "encoding": {"color": {"type": "quantitative", "field": "z"}, "x": {"type": "ordinal", "field": "x"}, "y": {"type": "ordinal", "field": "y"}}, "$schema": "https://vega.github.io/schema/vega-lite/v2.4.1.json"};
var opt = {};
var selector = "#ad2a6b0d-dee7-4313-b8b3-723238c1c84a";
var type = "vega-lite";

var output_area = this;

require(['nbextensions/jupyter-vega3/index'], function(vega) {
  vega.render(selector, spec, type, opt, output_area);
}, function (err) {
  if (err.requireType !== 'scripterror') {
    throw(err);
  }
});

#+END_SRC
:end:
ghost commented 6 years ago

This is a bit awkward but works for showing static images:

Install selenium and chromedriver. In ob-ipython call chart.save('chart.png'). In org, insert a link to chart.png. Execute (org-toggle-inline-images). The image appears in the org document.

Some docs here: https://altair-viz.github.io/user_guide/saving_charts.html#png-and-svg-format