Embed Jupyter Notebook into Hexo Post

Jupyter Notebook is a flexible tool that helps you create readable analyses which can keep code, images, comments, formulae and plots together. I also would like to make a post to discuss an data analyses result using markdown in my Hexo blog system. In this blog, I will share how to embed the content of Jupyter Notebook into Hexo using markdown format.

Useful Ideas

There are several existing solutions on the Internet:

  • .ipynb -> .md -> Copy the content of .md into a Hexo post;
  • .ipynb -> .html -> Reference the HTML file in a Hexo post;
  • .ipynb -> .html -> Embed the HTML file in a iFrame;
  • .ipynb -> .pdf -> Embed the pdf file in a Hexo post using PDF tag.

However, I’m just not satisficed with any of them because of the inconsistent format and style.

My Solution

During my testing, for solution 1, the only biggest problem is Hexo cannot render the DataFrame table very well as it is a raw HTML code in markdown file. Probably we can make it better.

Convert .ipynb to .md

Run the following command to convert .ipynb file from Jupyter Notebook format to a markdown file.

1
jupyter nbconvert --to markdown  notebook.ipynb

Two new items will be created after running the command:

  • notebook.md: a new markdown file;
  • notebook_files: a folder containing all the images in the notebook.

Embed in a Hexo Post

Create a new post in Hexo and copy the content of notebook.md to the post body.

1
2
3
4
5
---
layout: '[post]'
title: New Blog Post
---
<Copy the content to here>

Next, move the notebook_files folder to the resource folder of the post. Edit the image tag in Hexo post and point it to correct path.

Fix the DataFrame Table Display Issue

You may notice that DataFrame table is transformed into HTLM format in the markdown file. It seems to be a known issue in Hexo that raw HTLM table code always having problem to be rendered properly. In Hexo, If certain content is causing processing issues, wrap it with the raw tag to avoid rendering errors. Therefore, To fix the problem, just add a pair of raw tags to wrap the DataFrame section.

1
2
3
4
5
6
7
8
9
10
{% raw %}
<div>
<style scoped>
...
</style>
<table border="1" class="dataframe">
...
</table>
</div>
{% endraw %}

Pretty Tables

Next, add some CSS code to format the DataFrame tables appropriately. I’m using Next Theme of Hexo which provides a global option for customized CSS code.
Add the customized CSS code in themes\next\source\css\_costom\custome.styl. This change will take effect for all DataFrame tables in all posts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// beauty DataFrame 
table.dataframe {
width: 100%;
height: 240px;
display: block;
overflow: auto;
font-family: Arial, sans-serif;
font-size: 13px;
line-height: 20px;
text-align: center;
}
table.dataframe th {
font-weight: bold;
padding: 4px;
border-bottom: 0px;
}
table.dataframe td {
padding: 4px;
border-bottom: 0px;
}
table.dataframe tr:hover {
background: #b8d1f3;
}

Done. Here is a sample DataFrame table:

Total Percent
PuaMode 8919174 99.974119
Census_ProcessorClass 8884852 99.589407
DefaultBrowsersIdentifier 8488045 95.141637
Census_IsFlightingInternal 7408759 83.044030
Census_InternalBatteryType 6338429 71.046809
Census_ThresholdOptIn 5667325 63.524472
Census_IsWIMBootEnabled 5659703 63.439038
SmartScreen 3177011 35.610795
OrganizationIdentifier 2751518 30.841487
SMode 537759 6.027686
CityIdentifier 325409 3.647477
Wdft_IsGamer 303451 3.401352
Wdft_RegionIdentifier 303451 3.401352
Census_InternalBatteryNumberOfCharges 268755 3.012448
Census_FirmwareManufacturerIdentifier 183257 2.054109
Census_IsFlightsDisabled 160523 1.799286
Census_FirmwareVersionIdentifier 160133 1.794915
Census_OEMModelIdentifier 102233 1.145919
Census_OEMNameIdentifier 95478 1.070203
Firewall 91350 1.023933
Census_TotalPhysicalRAM 80533 0.902686
Census_IsAlwaysOnAlwaysConnectedCapable 71343 0.799676
Census_OSInstallLanguageIdentifier 60084 0.673475
IeVerIdentifier 58894 0.660137
Census_PrimaryDiskTotalCapacity 53016 0.594251
Census_SystemVolumeTotalCapacity 53002 0.594094
Census_InternalPrimaryDiagonalDisplaySizeInInches 47134 0.528320
Census_InternalPrimaryDisplayResolutionHorizontal 46986 0.526661
Census_InternalPrimaryDisplayResolutionVertical 46986 0.526661
Census_ProcessorModelIdentifier 41343 0.463410
Census_ProcessorManufacturerIdentifier 41313 0.463073
Census_ProcessorCoreCount 41306 0.462995
AVProductsEnabled 36221 0.405998
AVProductsInstalled 36221 0.405998
AVProductStatesIdentifier 36221 0.405998
IsProtected 36044 0.404014
RtpStateBitfield 32318 0.362249
Census_IsVirtualDevice 15953 0.178816
Census_PrimaryDiskTypeName 12844 0.143967
UacLuaenable 10838 0.121482
Census_ChassisTypeName 623 0.006983
GeoNameIdentifier 213 0.002387
Census_PowerPlatformRoleName 55 0.000616
OsBuildLab 21 0.000235
LocaleEnglishNameIdentifier 0 0.000000
AvSigVersion 0 0.000000
OsPlatformSubRelease 0 0.000000
Processor 0 0.000000
OsVer 0 0.000000
AppVersion 0 0.000000

Looks much better.

Reference: