[SOLVED] KNIME 3 charting with Plotly.js

Dear All,

not sure whether this has been done before (I couldn't find any reference though) and if would be of interest to a broader audience, but I managed to successfully integrate Plotly.js into KNIME using the Generic JavaScript Node from the KNIME Labs extension.

The solution uses RequireJS to load Plotly.js at runtime and instantiate it. You just need to check the dependency on JQuery, no need to also check D3. I have written a step-by-step tutorial on my blog here.

For convenience below is the JavaScript code to plot a choroplet map taking the input from a CSV Reader node.

Hope it helps. Nice weekend,
Marco.

// Load Plotly - Requires jQuery (tick Dependencies above)
require.config({
    paths: {
        'Plotly': 'https://cdn.plot.ly/plotly-latest.min'
    }
});

// Instantiates Plotly
require(['Plotly'], function (Plotly) {

// Load values to plot from input data table
// column 0 is x
// column 1 is y
var location = knimeDataTable.getColumn(0);
var alcohol = knimeDataTable.getColumn(1);

// Add the DIV for the plot to the DOM
// id of the DIV is "chart"
$(document.body).append('<div id="chart"></div>');

// ******************************
//  Your Plotly drawing code goes here
// ******************************

var data = [{
        type: 'choropleth',
        locationmode: 'country names',
        locations: location,
        z: alcohol,
        text: location,
        autocolorscale: true
    }];

    var layout = {
      title: 'Pure alcohol consumption among adults (age 15+) in 2010',
      geo: {
          projection: {
              type: 'robinson'
          }
      }
    };

    Plotly.plot('chart', data, layout, {showLink: false});

// ******************************
//  End of Plotly drawing code
// ******************************

});

 

1 Like

Sorry the code formatter in this forum is adding HTML where it shouldn't.

Line 4 above should be:

'Plotly': 'https://cdn.plot.ly/plotly-latest.min'

Sorry for the inconvenience.

Cheers,
Marco.

Oh, very nice! Thanks Marco!

Cheers,
E

Hi Marco,

thank you for sharing this with us :-)

I just enjoyed reading your post.

Cheers, Iris 

You are very welcome! :-)

I didn't try yet, but I guess in a similar way other JavaScript based charting libraries could be integrated as well. 

Cheers,
Marco.

1 Like

Thanks, Marco, for sharing that post.

Do you happen to also have an example of how to setup the SVG creation from a Plotly plot, in order to get an image at the output port of the node?

Cheers,
Jan

 

Hi Jan,

unfortunately I didn't succeed in that yet. I managed to output an SVG image for D3.js, but it is not working the same way for Plotly.js. The DOM that Plotly.js generates is much more complex due to the interactive layer, so getting to the inner SVG very likely requires a different strategy. Which one I haven't figured out yet.

I will keep trying and let you know if I make it.

Cheers,
Marco.

Hi Marco,

I've been trying to get this to work, but after a few hours I just gave up. 

Nothing ever shows up in the view. I even tried one of the example plots from the pltoly website, but it failed to generate a graph as well.

The Javascript View has no debugging options, so I have no clue what is going wrong. I attached the workflow to this post. I'm using the latest version of Knime (3.2.0)

If you have any idea what could be wrong, I'd appreciate your help.

Regards,

Tim

Hi Marco, thanks for sharing this.

How did you actually develop this code? Right now it is very tedious to develop with the "Generic JavaScript view" node. You have to open the configure dialog, make edits, close the dialog, reset the node, run the node, right-click to access the plot and then try to figure out what when wrong. Is there a faster way to iterate on your code?

I tried to load a local JS file that contains the actual plotting code but all I get are noninformative requireJS stack traces :(

Hi knot,

long time has passed since then, but if I remember correctly I developed the code using a web browser and Firebug starting from an online example, then moved it over to KNIME simply adjusting how the data are brought in and assigned to javascript variables.

It took anyway quite some trials and errors to make it work, that's why I thought to share it on the forum so others could use it as a starting point.

I agree with you that in their present way of working the JS nodes are not suitable for development and testing. I would also favor some functional improvement on these aspects (e.g. add the possibility to inspect the DOM, a test function in the configuration and a better way to capture/display the JS log output/stack trace).

Cheers,
Marco.

 

Hi Marco,

this is really great, thanks! I created some chart templates with a metanode before the generic javascript node that provide forms to easily configure the chart (e.g. to select data, rename titles etc.) by passing parts of the javascript code via variables into the generic javascript node. Nice for users that don’t want to write code. :wink:

Just one question: plot.ly supports some interesting features in R which are not supported in JavaScript (e.g. data based symbols in scatter plots). Have you tried to use plotl.ly in an R View node?

Thanks and kind regards
Florian

Hi Marco

I have been working to make an animated graph, based in your work…maybe you, or some one elsle can help me.

Schedule.knwf (9.9 KB)


I’m working in this workflow. Inside there is a Generic JavaScript node, that i have been successfully coded (the results are what is expected, but i think it can be better coded.

For example:

To get the information from the previews node, we used:

var h1= knimeDataTable.getColumn(1);
var h2= knimeDataTable.getColumn(2);
var h3= knimeDataTable.getColumn(3);
var h4= knimeDataTable.getColumn(4);
var h5= knimeDataTable.getColumn(5);
.
.
.
var h55= knimeDataTable.getColumn(55);
var h56= knimeDataTable.getColumn(56);
var h57= knimeDataTable.getColumn(57);
var h58= knimeDataTable.getColumn(58);
var h59= knimeDataTable.getColumn(59);
var h60= knimeDataTable.getColumn(60);

Then, we substitute all of previews code for this next one:

for (var i = 1; i < 61; i++) {
this[“h”+i] = knimeDataTable.getColumn(i);
}

And is working right !.

We want to do the same with the next part of the code:
steps: [
{label: n[1], method: ‘animate’, args: [[n[1]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[2], method: ‘animate’, args: [[n[2]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[3], method: ‘animate’, args: [[n[3]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[4], method: ‘animate’, args: [[n[4]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[5], method: ‘animate’, args: [[n[5]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
.
.
.
{label: n[55], method: ‘animate’, args: [[n[55]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[56], method: ‘animate’, args: [[n[56]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[57], method: ‘animate’, args: [[n[57]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[58], method: ‘animate’, args: [[n[58]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[59], method: ‘animate’, args: [[n[59]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
{label: n[60], method: ‘animate’, args: [[n[60]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
]

and this part of the code:

frames:[
{name: n[1], data: [{ y: h1, marker: {color: c1, size: s0}}]},
{name: n[2], data: [{ y: h2, marker: {color: c2, size: s0}}]},
{name: n[3], data: [{ y: h3, marker: {color: c3, size: s0}}]},
{name: n[4], data: [{ y: h4, marker: {color: c4, size: s0}}]},
{name: n[5], data: [{ y: h5, marker: {color: c5, size: s0}}]},
.
.
.
{name: n[55], data: [{ y: h55, marker: {color: c55, size: s0}}]},
{name: n[56], data: [{ y: h56, marker: {color: c56, size: s0}}]},
{name: n[57], data: [{ y: h57, marker: {color: c57, size: s0}}]},
{name: n[58], data: [{ y: h58, marker: {color: c58, size: s0}}]},
{name: n[59], data: [{ y: h59, marker: {color: c59, size: s0}}]},
{name: n[60], data: [{ y: h60, marker: {color: c60, size: s0}}]},
]

we thought that something like the following code would work, but not:

for (var i = 1; i < 61; i++) {
{name: n[i], data: [{ y: h[i], marker: {color: c[1], size: s0}}]},
}

or:

for (var i = 1; i < 61; i++) {
{label: n[i], method: ‘animate’, args: [[n[i]], { mode: ‘immediate’, frame: {redraw: false, duration: 500}, transition: {duration: 500} }]},
}

The workflow is this one. Please check it and help me,