How to embed Tablesaw graph in Jupyter Notebook with IJava kernel?

Garret Wilson :

I'd like to embed Tablesaw interactive graphs in Jupyter Notebook using the IJava kernel. I realize that Tablesaw may not be able to do this out of the box, but I'm willing to put in a little effort to make this happen. I'm a Java expert, but I'm new to Jupyter Notebook and to the IJava kernel, so I'm not sure where to start. Is there some API for Jupyter Notebook or for IJava for embedding objects?

First of all I installed Anaconda and then installed the IJava kernel in Jupyter Notebook with no problem. So far it's working without a hitch using OpenJDK 11 on Windows 10! Next I tried to use Tablesaw. I was able to add its Maven dependencies, load a CSV file, and create a plot. Very nice!

However to produce a graph Tablesaw generates a temporary HTML file using Plotly, and invokes the browser to show the interactive plot. In other words, the graph does not appear inside Jupyter Notebook.

Tablesaw has an example with embedded graphs using the BeakerX kernel (not IJava), and as you can see (scroll down to "Play (Money)ball with Linear Regression"), they are embedding a Tablesaw Plot directly within Jupyter Notebook. So I know that conceptually embedding an interactive Tablesaw graph in Jupyter Notebook with a Java kernel is possible.

Is this capability something specific to BeakerX? I would switch to BeakerX, but from the documentation I didn't see anything about BeakerX support Java 9+. In addition IJava seemed like a leaner implementation, built directly on top of JShell.

Where do I start to figure out how to embed a Tablesaw Plot object as an interactive graph in Jupyter Notebook using the IJava kernel, the way they are doing in BeakerX?

SpencerPark :

The IJava kernel has 2 main functions for display_data; display and render. Both hook into the Renderer from the base kernel. We can register a render function for the Figure type from tablesaw.

  1. Add the tablesaw-jsplot dependency (and all required transitive dependencies) via the loadFromPOM cell magic:

    %%loadFromPOM
    <dependency>
        <groupId>tech.tablesaw</groupId>
        <artifactId>tablesaw-jsplot</artifactId>
        <version>0.30.4</version>
    </dependency>
    
  2. Register a render function for IJava's renderer.

    1. We create a registration for tech.tablesaw.plotly.components.Figure.
    2. If an output type is not specified during the render then we want it to default to text/html (with the preferring call).
    3. If an html render is requested, we build the target <div> as well as the javascript that invokes a plotly render into that <div>. Most of this logic is done via tablesaw's asJavascript method but hooks into Jupyter notebook's require AMD module setup.

    import io.github.spencerpark.ijava.IJava;
    
    IJava.getKernelInstance().getRenderer()
        .createRegistration(tech.tablesaw.plotly.components.Figure.class)
        .preferring(io.github.spencerpark.jupyter.kernel.display.mime.MIMEType.TEXT_HTML)
        .register((figure, ctx) -> {
            ctx.renderIfRequested(io.github.spencerpark.jupyter.kernel.display.mime.MIMEType.TEXT_HTML, () -> {
                String id = UUID.randomUUID().toString().replace("-", "");
    
                figure.asJavascript(id);
                Map<String, Object> context = figure.getContext();
    
                StringBuilder html = new StringBuilder();
                html.append("<div id=\"").append(id).append("\"></div>\n");
                html.append("<script>require(['https://cdn.plot.ly/plotly-1.44.4.min.js'], Plotly => {\n");
                html.append("var target_").append(id).append(" = document.getElementById('").append(id).append("');\n");
                html.append(context.get("figure")).append('\n');
                html.append(context.get("plotFunction")).append('\n');
                html.append("})</script>\n");
                return html.toString();
            });
        });
    
  3. Then we can create a table to display (taken from the Tablesaw docs).

    import tech.tablesaw.api.*;
    import tech.tablesaw.plotly.api.*;
    import tech.tablesaw.plotly.components.*;
    
    String[] animals = {"bear", "cat", "giraffe"};
    double[] cuteness = {90.1, 84.3, 99.7};
    
    Table cuteAnimals = Table.create("Cute Animals")
        .addColumns(
            StringColumn.create("Animal types", animals),
            DoubleColumn.create("rating", cuteness)
        );
    cuteAnimals
    
  4. Finally we can create a Figure for the table and display it via one of 3 methods for displaying things in IJava.

    VerticalBarPlot.create("Cute animals", cuteAnimals, "Animal types", "rating");
    

    which is equivalent to a render call where the "text/html" is implicit because we set it as the preferred type (preferring during registration)

    render(VerticalBarPlot.create("Cute animals", cuteAnimals, "Animal types", "rating"), "text/html");
    

    and if it is not at the end of a cell, the display function is another option. For example to display the chart and the cuteAnimals afterwards:

    Figure figure = VerticalBarPlot.create("Cute animals", cuteAnimals, "Animal types", "rating");
    display(figure);
    
    cuteAnimals
    

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=106450&siteId=1