Adding Markdown Tables to a Post

Hugo Markdown Jupyter Python

The intent of using Markdown and Hugo for my site is to directly export from a Jupyter notebook into a post. However, when I preview a dataframe with head and export the notebook, I obtain a HTML table that is not nicely rendered or styled. The intent is to export in pure Markdown, and let the Hugo theme dictate what the aspect of the table should be.

The naive approach

I start with the creation of a pandas dataframe in the first cell of a notebook:

import pandas as pd


df = pd.DataFrame(
    {
        "A": [1, 5, 7, 3, 4, 0],
        "B": [9, 4, 5, 6, 1, 0],
        "C": ["ABC", "BAC", "CAB", "ABC", "BAC", "CAB"],
    }
)
df

When downloading the Jupyter notebook as Markdown, the table was generated as the following HTML file:

<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>A</th>
      <th>B</th>
      <th>C</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1</td>
      <td>9</td>
      <td>ABC</td>
    </tr>
    <tr>
      <th>1</th>
      <td>5</td>
      <td>4</td>
      <td>BAC</td>
    </tr>
    <tr>
      <th>2</th>
      <td>7</td>
      <td>5</td>
      <td>CAB</td>
    </tr>
    <tr>
      <th>3</th>
      <td>3</td>
      <td>6</td>
      <td>ABC</td>
    </tr>
    <tr>
      <th>4</th>
      <td>4</td>
      <td>1</td>
      <td>BAC</td>
    </tr>
    <tr>
      <th>5</th>
      <td>0</td>
      <td>0</td>
      <td>CAB</td>
    </tr>
  </tbody>
</table>
</div>

This is rendered as:

A B C
0 1 9 ABC
1 5 4 BAC
2 7 5 CAB
3 3 6 ABC
4 4 1 BAC
5 0 0 CAB

Using tabulate

On the other hand, I can use the tabulate package to convert the table into a string containing a valid Markdown table. The following line:

print(df.to_markdown(index=False))

produce these output in te notebook:

|   A |   B | C   |
|----:|----:|:----|
|   1 |   9 | ABC |
|   5 |   4 | BAC |
|   7 |   5 | CAB |
|   3 |   6 | ABC |
|   4 |   1 | BAC |
|   0 |   0 | CAB |

However, once the notebook is exported into Markdown, the table is not rendered because of the trailing spaces. This the way it would be rendered:

|   A |   B | C   |
|----:|----:|:----|
|   1 |   9 | ABC |
|   5 |   4 | BAC |
|   7 |   5 | CAB |
|   3 |   6 | ABC |
|   4 |   1 | BAC |
|   0 |   0 | CAB |

As I do not want to manually remove the trailing space, I am going to use a smarter approach.

Using display

I can display Markdown rendered code in the notebook using IPython:

from IPython.display import display
from IPython.display import Markdown


display(Markdown(df.to_markdown(index=False)))

With this approach, the table is rendered in the notebook and in the exported Mardown. Here how it looks like:

A B C
1 9 ABC
5 4 BAC
7 5 CAB
3 6 ABC
4 1 BAC
0 0 CAB

The index=False argument to to_markdown() eliminate the indices from the visualization of the dataframe.

Refactoring to a function

For the sake of brevity, I create a function that display the table as above removing the indices, and with the option to only display a nrows number of rows:

def dm(df: pd.DataFrame, nrows=None):
    display(Markdown(df.head(nrows).to_markdown(index=False)))
    
    
dm(df)
A B C
1 9 ABC
5 4 BAC
7 5 CAB
3 6 ABC
4 1 BAC
0 0 CAB

Displaying only two rows would result in:

dm(df, 2)
A B C
1 9 ABC
5 4 BAC