Skip to main content
  1. Posts/

Notebook 2 hugo

·561 words·3 mins

As a researcher who spends a lot of his time analyzing data in a Jupyter notebook, I would love to be able to use this format for blog posts as well. When I was looking at static site generators, I considered using Pelican. The reason being the excellent pelican-jupyter plugin.

This plugin uses a very elegant way of integrating notebooks inside the markdown content. After the front matter, you can include any notebook in the article using a liquid tag. Liquid is an open-source template language written in Ruby, similar to jinja2.

1
2
3
4
5
# blog_example.md
title:
date:

{% notebook path/to/notebook.ipynb %}

I would love to have a functionality like this in hugo. There are some solutions, but no official support yet.

So… what can we do?

The first thing that came to mind is to use nbconvert to convert the .ipynb file to a .md file. Then include the markdown file as a post. As suggested in the links above, I put the front matter in the first cell of the notebook as a Raw NBConvert cell (so that’s what they are for!)

The convert command is: jupyter nbconvert --to markdown jupyter_blog.ipynb

Python #

Let’s get started!

1
print('Hello from Python!')
Hello from Python!

Plots #

Alright, that seems to work! I’m mostly interested in showing some plots with numpy, pandas, and matplotlib:

1
2
3
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

Let’s generate some data:

1
2
3
4
index = pd.date_range(start='2022-01-01', end='2022-12-31', freq='D')
data = np.random.rand(len(index))
s = pd.Series(data=data, index=index)
s
2022-01-01    0.899271
2022-01-02    0.614524
2022-01-03    0.829996
2022-01-04    0.384019
2022-01-05    0.641648
                ...
2022-12-27    0.585333
2022-12-28    0.153174
2022-12-29    0.344631
2022-12-30    0.702626
2022-12-31    0.636027
Freq: D, Length: 365, dtype: float64
1
s.plot(title='Hello from matplotlib!')
<AxesSubplot:title={'center':'Hello from matplotlib!'}>

plot

That almost worked right away! nbconvert first seems to generate a directory with a png file, that is then linked in the markdown file. I copied the entire directory to the static directory. Except that the url above needs to be tweaked too (preprend a /).

Math #

One of the problems I read about nbconvert with hugo, is that it has issues with latex. So, this cell tests latex support:

\(F_{hkl} = \sum_{j=1}^{N} n_j f_j \exp[2\pi i (hx_j + ky_j + lz_j)]\)

(That’s the structure factor equation).

For the theme I’m using, this involved adding a shortcode for math support. And, add math: true to the header (see first cell).

HTML #

Another great feature of jupyter notebooks is that it can also easily render HTML content. In fact, many of the interactive tools like widgets or plots are rendered in html / javascript. To get this to work, I had to create a shortcode to display raw html.

1
2
from IPython import display
display.HTML('<h1 style="color:red;">HELLO FROM HTML</h1>')

HELLO FROM HTML

nb2hugo #

In the end, running nbconvert on this notebook generates a bunch of files:

  • jupyter_blog.md
  • jupyter_blog_files/jupyter_blog_11_1.png (see below)

These need to be copied to the site directory, and the markdown needed some tweaks to get it to render correctly.

A bit of work, so I used nb2hugo to apply some of the fixes automatically.

1
2
3
>>> python ../nb2hugo/bin/nb2hugo ./jupyter_blog.ipynb --site-dir stefsmeets.nl --section posts`
Created 'stefsmeets.nl/static/posts/jupyter_blog/output_12_1.png'
Created 'stefsmeets.nl/content/posts/jupyter_blog.md'

This applies the fixes described here and puts the files in the right place! 🚀 1


  1. This emoji required patching nb2hugo to write with UTF-8 encoding. ↩︎