Messin' with jQuery and Jupyter
Just yesterday, my tables got too long!
First reaction
Maybe i can just attach a piece of javascript to the pandas.DataFrame rendering to make them at least sortable.
Second to kind o’third reaction
A well.. let’s look up someone elses code first before spending two nights implementing horrible javascript templating within python strings kind o’stuff.
Fourth to sixteenth reaction
Ohh! There’s a jQuery plugin called DataTables which can add sorting and paging to pure html tables. There must be something for jupyter already.. Let’s try itables… Ohh, i need to adjust my script blocker because everything is loaded from cdn.datatables.net
… Aah, they use this jupyter built-in requirejs
, maybe i can just replace the paths… Ohh, how do i provide them to the notebook?
Ahh, maybe just render a <script>
tag into an output cell… Uhm, $(...).DataTables is not a function
.. Maybe if i add it to the header of the notebook html.. Öh, how exactly? … C’mon! … F** S** .. Aaahh. Just write an nbextension… What the? … Ömmh, have all these javascript errors been there before? .. Oh! It works! It works! Let’s .. oh, it doesn’t work …
… and so on.
Finally, there is this extension which just cruedly injects the DataTables into the jupyter-provided jquery. I find it often complicated when the framework provides jquery but one wants to add another plugin.
The css got adjusted a bit so it matches with my onedork
jupyter theme. Also the images are provided by the extension and do not require a CDN.
Now is it possible to render a blog post with this homebrewn setup?
Here’s the standard DataFrame representation:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.uniform(0, 100, (30, 10))).round()
df.head()
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 76.0 | 68.0 | 77.0 | 30.0 | 67.0 | 68.0 | 44.0 | 36.0 | 35.0 | 15.0 |
1 | 33.0 | 31.0 | 31.0 | 97.0 | 83.0 | 62.0 | 98.0 | 25.0 | 7.0 | 67.0 |
2 | 35.0 | 55.0 | 96.0 | 91.0 | 18.0 | 79.0 | 86.0 | 11.0 | 16.0 | 15.0 |
3 | 92.0 | 84.0 | 50.0 | 6.0 | 33.0 | 96.0 | 67.0 | 21.0 | 85.0 | 14.0 |
4 | 99.0 | 6.0 | 96.0 | 77.0 | 36.0 | 34.0 | 68.0 | 97.0 | 78.0 | 42.0 |
It is already quite cool and useful, but not for 500 rows..
The DataTables enhanced version:
import json
import secrets
from IPython.display import display, HTML
def datatable(
df: pd.DataFrame,
max_rows: int = 1000,
max_cols: int = None,
paging: bool = True,
table_id: str = None,
**kwargs,
):
table_id = table_id or f"table-{secrets.token_hex(10)}"
html = df.to_html(
table_id=table_id,
escape=True,
max_rows=max_rows,
max_cols=max_cols,
)
kwargs.update({
"paging": paging,
})
options_str = json.dumps(kwargs)
html += f"""<script type="text/javascript">
jQuery("#{table_id}").DataTable({options_str});
</script>"""
display(HTML(html))
datatable(df, paging=True)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 76.0 | 68.0 | 77.0 | 30.0 | 67.0 | 68.0 | 44.0 | 36.0 | 35.0 | 15.0 |
1 | 33.0 | 31.0 | 31.0 | 97.0 | 83.0 | 62.0 | 98.0 | 25.0 | 7.0 | 67.0 |
2 | 35.0 | 55.0 | 96.0 | 91.0 | 18.0 | 79.0 | 86.0 | 11.0 | 16.0 | 15.0 |
3 | 92.0 | 84.0 | 50.0 | 6.0 | 33.0 | 96.0 | 67.0 | 21.0 | 85.0 | 14.0 |
4 | 99.0 | 6.0 | 96.0 | 77.0 | 36.0 | 34.0 | 68.0 | 97.0 | 78.0 | 42.0 |
5 | 70.0 | 76.0 | 67.0 | 82.0 | 82.0 | 100.0 | 29.0 | 58.0 | 49.0 | 54.0 |
6 | 14.0 | 18.0 | 25.0 | 26.0 | 93.0 | 47.0 | 78.0 | 2.0 | 25.0 | 67.0 |
7 | 64.0 | 6.0 | 28.0 | 56.0 | 55.0 | 65.0 | 10.0 | 34.0 | 55.0 | 14.0 |
8 | 65.0 | 72.0 | 87.0 | 45.0 | 48.0 | 32.0 | 19.0 | 19.0 | 10.0 | 49.0 |
9 | 66.0 | 53.0 | 54.0 | 87.0 | 24.0 | 79.0 | 0.0 | 35.0 | 57.0 | 66.0 |
10 | 32.0 | 95.0 | 63.0 | 94.0 | 86.0 | 70.0 | 65.0 | 57.0 | 81.0 | 59.0 |
11 | 23.0 | 22.0 | 58.0 | 17.0 | 33.0 | 16.0 | 85.0 | 95.0 | 19.0 | 52.0 |
12 | 99.0 | 62.0 | 33.0 | 88.0 | 78.0 | 19.0 | 28.0 | 42.0 | 61.0 | 23.0 |
13 | 42.0 | 49.0 | 45.0 | 30.0 | 35.0 | 27.0 | 20.0 | 87.0 | 30.0 | 5.0 |
14 | 86.0 | 75.0 | 7.0 | 29.0 | 32.0 | 2.0 | 98.0 | 18.0 | 89.0 | 63.0 |
15 | 78.0 | 98.0 | 80.0 | 9.0 | 14.0 | 81.0 | 35.0 | 96.0 | 50.0 | 63.0 |
16 | 32.0 | 78.0 | 18.0 | 54.0 | 53.0 | 64.0 | 4.0 | 69.0 | 24.0 | 10.0 |
17 | 99.0 | 17.0 | 27.0 | 83.0 | 33.0 | 29.0 | 56.0 | 20.0 | 25.0 | 15.0 |
18 | 4.0 | 14.0 | 89.0 | 95.0 | 35.0 | 34.0 | 35.0 | 38.0 | 88.0 | 85.0 |
19 | 87.0 | 37.0 | 11.0 | 67.0 | 52.0 | 66.0 | 76.0 | 80.0 | 87.0 | 36.0 |
20 | 70.0 | 71.0 | 42.0 | 94.0 | 24.0 | 76.0 | 75.0 | 63.0 | 7.0 | 4.0 |
21 | 10.0 | 8.0 | 45.0 | 18.0 | 39.0 | 96.0 | 63.0 | 21.0 | 23.0 | 44.0 |
22 | 46.0 | 56.0 | 4.0 | 95.0 | 24.0 | 63.0 | 38.0 | 67.0 | 53.0 | 94.0 |
23 | 2.0 | 44.0 | 74.0 | 95.0 | 94.0 | 79.0 | 19.0 | 4.0 | 27.0 | 7.0 |
24 | 30.0 | 70.0 | 77.0 | 13.0 | 26.0 | 5.0 | 27.0 | 9.0 | 11.0 | 3.0 |
25 | 17.0 | 52.0 | 44.0 | 34.0 | 53.0 | 89.0 | 19.0 | 40.0 | 14.0 | 72.0 |
26 | 53.0 | 14.0 | 71.0 | 92.0 | 32.0 | 90.0 | 85.0 | 58.0 | 90.0 | 21.0 |
27 | 96.0 | 98.0 | 73.0 | 61.0 | 36.0 | 70.0 | 40.0 | 0.0 | 6.0 | 79.0 |
28 | 83.0 | 39.0 | 8.0 | 82.0 | 25.0 | 71.0 | 0.0 | 80.0 | 71.0 | 96.0 |
29 | 16.0 | 87.0 | 49.0 | 80.0 | 76.0 | 27.0 | 20.0 | 57.0 | 92.0 | 47.0 |
Does it work when loading the notebook the first time? No
Does it always work? Not always
Why not using $().DataTable
? It does not work
Is the styling of this whole blog worth the trouble? The styling is terrible but it’s worth the trouble
What was the point again? It works offline and does not involve third parties when browsing this post