<< Go back to Posts

Embedding Bokeh Plots with Jekyll

This website is generated thanks to Jekyll. Embedding a Bokeh plot as an html file was easy. However, this way prevents from adding multiple plot. In this article I describe one possible solution.



Introduction

Bokeh is a nice visualization tool, enabling to create interactive .html pages with data.

When making a website, you want the figures to be integrated in your page, not in a separeted one.

The post is divided in three parts:

  • How to get an .html from Bokeh
  • How to embed it
  • How to embed several Bokeh documents.

Table of Content

Getting an HTML File from Bokeh

This might be easy for you.

You should have included in your code:

from bokeh.plotting  import output_file, show

[... my python code ...]

# p is your plot / figure

output_file(SAVE_PATH)
show(p)

output_file() sets the path where the figure must be stored, and show() creates the .html and open the created file in your browser. Here, nothing special.

Including one Document

To embed one Bokeh document, you need to store the .html generated by Bokeh in the _includes/ repository.

So, if my document is named bokeh_toto.html, it must be stored at _includes/bokeh_toto.html.

To include it in a page of your website, you just need to write:

{% include bokeh_toto.html %}

in the markdown file.

Ok, it should work. You may have the <!DOCTYPE html> header printed that you can remove it (from the file bokeh_toto.html).

Now, if you want to add a second bokeh document, it may not work.

There seems to be a conflict between div naming. I don’t know precisely the reason. I tried to change the IDs, sometimes it worked, sometimes it did not. It was confusing.

Including Several Documents

To include multiple document, we should not include the full .html document, otherwise we may have conflict. Instead, we must include only the <div> and the <script> which enable the stuff to run.

Setting the Main Scripts

For Bokeh to run, there are two things:

  • default Bokeh scripts (library), which must be located in the <head> part
  • figure scripts, which contains the data and behavior of the figure, located in the document.

We do not need them when we embedd a full document as these script are already present in the <head> part. Here, when we chose to include a specific Bokeh’s <div> and <script>, the script part is only for describing component interaction, the library is not in!!

Check the generated Bokeh .html document and look at the header. There might be some related scripts. Please refer to your document (do not copy/paste the thing bellow), as the scripts needed depend on your Bokeh version.

You need to add this script in the .html pages where you want to embed figures:

<head>

[... other scripts ...]

<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.1.min.js"></script>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-3.2.1.min.js"></script>

<script type="text/javascript">
        Bokeh.set_log_level("info");
</script>
</head>

Replace 3.2.1 by your version number (NB: compatibility might be broken. Check if all your documents use the same version)

Option 1: Load the remote scripts

To avoid setting the header manually each time you use Bokeh with Jekyll, I suggest modifying your layout with this:

In _layout/base.html:

<head>

[... other scripts ...]


    {% if page.bokeh == true %}


    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-1.4.0.min.js"></script>
    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js"></script>
    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-tables-1.4.0.min.js"></script>
    <script type="text/javascript">
            Bokeh.set_log_level("info");
    </script>
    <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.2.1.min.js"></script>


    {% endif %}


</head>

And now, in your markdown page, in the yaml header set:

---
  title: My title
  bokeh: true
---

The liquid syntax:

{% if page.bokeh == true %}

enables loading only the script when necessary. If you have a good connection, it does not matter. However, when you have some limitation (or if you pay for each Mo downloaded), reducing the memory footprint is recommended.

Option 2: Load the Local Lib

If you are offline (in the train or in a plane for instance), or if you have a firewall installed, then Bokeh’s library scripts may be blocked and the figures would not load. To prevent from this issue, save the bokeh scripts locally in:

+ assets/
  + js/
    - bokeh-3.2.1.min.js
    - bokeh-gl-3.2.1.min.js
    - bokeh-mathjax-3.2.1.min.js
    - bokeh-tables-3.2.1.min.js
    - bokeh-widgets-3.2.1.min.js

And in your _layout/base.html, put:

<script src="/assets/js/bokeh-3.2.1.min.js"></script>
<script src="/assets/js/bokeh-gl-3.2.1.min.js"></script>
<script src="/assets/js/bokeh-mathjax-3.2.1.min.js"></script>
<script src="/assets/js/bokeh-tables-3.2.1.min.js"></script>
<script src="/assets/js/bokeh-widgets-3.2.1.min.js"></script>

<script type="text/javascript">
        Bokeh.set_log_level("info");
</script>

If your website can load, then everything would load.

Creating the Specific <div> and <script>

When your website is ready to host Bokeh figures, we can generate the div and script parts.

Now that your page is ready to host Bokeh figures, we need to generate the corresponding <div> and <script>.

In your bokeh figure generator, you need to have these lines:

from bokeh.embed import components

[... my code ...]

# p is your plot / figure
script, div = components(p)

with open("tmp_div.txt", "w") as fp:
    fp.write(div)

with open("tmp_script.txt", "w") as fp:
    fp.write("<html>\n" + script + "</html>")

Here components() will generate the minimal material needed. You only need to save it in dedicated files.

For the <div>, you must save it raw without changes. For the <script> , if you include it without any changes, all its content would be printed in your web page, and the <div> would not show the Bokeh figure. To prevent from this issue, adding <html> before and </html> after do the job (I am not an html expert. If you have a cleaner idea, please let me know).

Last, you need to store tmp_div.txt and tmp_script.txt in the _include/ folder.

Finally, invoke them with the include command:

{% include tmp_div.txt %}
{% include tmp_script.txt %}

Conclusion

Checklist:

  1. Design your stuff with bokeh
  2. Use components() to get script and div
  3. Save them in the _include/ folder
  4. Include the tags with {% include myfile.js %} in your markdown page
  5. Include bokeh lib script in the html layout header.

And normally, everything should work.



>> You can subscribe to my mailing list here for a monthly update. <<