Best way to add visualizations to a Plotly go.Figure object

Hi all,

In quite some apps, Plotly is used to generate visualizations in the WebView. Usually the go.Figure object is created in the controller. After that all kind of visualizations are added to the object from different models/objects. I am curious what your preferred method is to do this and why? I have seen several methods to do this:

  1. Pass the fig object as an argument and return the fig object. Possibly making the argument optional and creating the fig object in the method.
@WebView('Visualization', duration_guess=2)
def visualize(self, params, **kwargs) -> WebAndDataResult:
    model1 = model(params)
    fig = go.Figure()
    fig = model1.get_visualization(fig)
    return WebResult(html=StringIO(fig.to_html())
  1. pass the fig object, but do not return anything
@WebView('Visualization', duration_guess=2)
def visualize(self, params, **kwargs) -> WebAndDataResult:
    model1 = model(params)
    fig = go.Figure()
    model1.get_visualization(fig)
    return WebResult(html=StringIO(fig.to_html())
  1. Let the method return a list of traces and add them in the controller
@WebView('Visualization', duration_guess=2)
def visualize(self, params, **kwargs) -> WebAndDataResult:
    model1 = model(params)
    fig = go.Figure()
    for trace in model1.get_visualizations():
        fig.add_trace(trace)
    return WebResult(html=StringIO(fig.to_html())

I recently changed from option 2 to 3. Because it is slightly easier to understand by more inexperienced (external) devs.
I am curious about your opinions about this.

1 Like

My opinion is that for your example the differences are minimal. Nevertheless, to emphasize stateless code I would prefer method 1 to method 2.

Method 3 is always good, especially when ‘traces’ originate from different objects:

@WebView('Visualization', duration_guess=2)
def visualize(self, params, **kwargs) -> WebAndDataResult:
    fig = go.Figure()
    fig.add_trace(model1.get_plot())
    fig.add_trace(model2.get_plot())
    fig.add_trace(model3.get_plot())
    return WebResult(html=StringIO(fig.to_html())

Now that I have seen suggestion 3, I’m quite sure I’ll be using that design pattern more often.


Another tip with regards to using plotly, to improve that loading speed of the visualization you can use:
return WebResult(html=StringIO(figure.to_html(include_plotlyjs="cdn")))

instead of

return WebResult(html=StringIO(figure.to_html()))

because less data will be added to the WebResult html file, which in turn will speed up the loading time of the WebView.

(Without the include_plotlyjs="cdn" flag, the html page will contain all plotly JavaScript code (roughly 4MB of data), but when the flag is included it will download this JavaScript code over a CDN, while the WebView is loading in the web-browser, which is much faster)

1 Like