python save plotly plot to local file and insert into html

I am using python and plotly to product interactive html report. This post gives a nice framework.

If I produce the plot(via plotly) online, and insert the url into the html file, it works but refreshing the charts takes a long time. I wonder if I could produce the chart offline and have it embedded in the html report, so that loading speed is not a problem.

I find plot offline would generate a html for the chart, but I don't know how to embed it in another html. Anyone could help?


Solution 1:

There is a better alternative as of right now, that is to do offline plotting into a div, rather than a full html. This solution does not involve any hacks.

If you call:

plotly.offline.plot(data, filename='file.html')

It creates a file named file.html and opens it up in your web browser. However, if you do:

plotly.offline.plot(data, include_plotlyjs=False, output_type='div')

the call will return a string with only the div required to create the data, for example:

<div id="82072c0d-ba8d-4e86-b000-0892be065ca8" style="height: 100%; width: 100%;" class="plotly-graph-div"></div>
<script type="text/javascript">window.PLOTLYENV=window.PLOTLYENV || {};window.PLOTLYENV.BASE_URL="https://plot.ly";Plotly.newPlot("82072c0d-ba8d-4e86-b000-0892be065ca8", 
[{"y": ..bunch of data..., "x": ..lots of data.., {"showlegend": true, "title": "the title", "xaxis": {"zeroline": true, "showline": true}, 
"yaxis": {"zeroline": true, "showline": true, "range": [0, 22.63852380952382]}}, {"linkText": "Export to plot.ly", "showLink": true})</script>

Notice how its just a tiny portion of an html that you are supposed to embed in a bigger page. For that I use a standard template engine like Jinga2.

With this you can create one html page with several charts arranged the way you want, and even return it as a server response to an ajax call, pretty sweet.

Update:

Remember that you'll need to include the plotly js file for all these charts to work.

You could include

<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> 

just before putting the div you got. If you put this js at the bottom of the page, the charts won't work.

Solution 2:

Option 1: Use plotly's offline functionality in your Jupyter Notebook (I suppose you are using a Jupyter Notebook from the link you are providing). You can simply save the whole notebook as a HTML file. When I do this, the only external reference is to JQuery; plotly.js will be inlined in the HTML source.

Option 2: The best way is probably to code directly against plotly's JavaScript library. Documentation for this can be found here: https://plot.ly/javascript/

Update: Calling an internal function has never been a good idea. I recommend to use the approach given by @Fermin Silva. In newer versions, there now is also a dedicated function for this: plotly.io.to_html (see https://plotly.com/python-api-reference/generated/plotly.io.to_html.html)

Hacky Option 3 (original version for reference only): If you really want to continue using Python, you can use some hack to extract the HTML it generates. You need some recent version of plotly (I tested it with plotly.__version__ == '1.9.6'). Now, you can use an internal function to get the generated HTML:

from plotly.offline.offline import _plot_html
data_or_figure = [{"x": [1, 2, 3], "y": [3, 1, 6]}]
plot_html, plotdivid, width, height = _plot_html(
    data_or_figure, False, "", True, '100%', 525)
print(plot_html)

You can simply paste the output somewhere in the body of your HTML document. Just make sure that you include a reference to plotly in the head:

<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>

Alternatively, you can also reference the exact plotly version you used to generate the HTML or inline the JavaScript source (which removes any external dependencies; be aware of the legal aspects however).

You end up with some HTML code like this:

<html>
<head>
  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
  <!-- Output from the Python script above: -->
  <div id="7979e646-13e6-4f44-8d32-d8effc3816df" style="height: 525; width: 100%;" class="plotly-graph-div"></div><script type="text/javascript">window.PLOTLYENV=window.PLOTLYENV || {};window.PLOTLYENV.BASE_URL="https://plot.ly";Plotly.newPlot("7979e646-13e6-4f44-8d32-d8effc3816df", [{"x": [1, 2, 3], "y": [3, 1, 6]}], {}, {"showLink": false, "linkText": ""})</script>
</body>
</html>

Note: The underscore at the beginning of the function's name suggests that _plot_html is not meant to be called from external code. So it is likely that this code will break with future versions of plotly.

Solution 3:

In addition to the other answers, another possible solution is to use to_json() on the plotly figure.

No need for custom JSON serializer or use of internal solutions.

import plotly

# create a simple plot
bar = plotly.graph_objs.Bar(x=['giraffes', 'orangutans', 'monkeys'], 
                            y=[20, 14, 23])
layout = plotly.graph_objs.Layout()
fig = plotly.graph_objs.Figure([bar], layout)

# convert it to JSON
fig_json = fig.to_json()

# a simple HTML template
template = """<html>
<head>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
    <div id='divPlotly'></div>
    <script>
        var plotly_data = {}
        Plotly.react('divPlotly', plotly_data.data, plotly_data.layout);
    </script>
</body>

</html>"""

# write the JSON to the HTML template
with open('new_plot.html', 'w') as f:
    f.write(template.format(fig_json))