Jupyter Notebook naar Markdown omzetten: het verhaal houden, de verpakking weggooien
Een notebook is eigenlijk twee documenten onder één bestandsnaam. Er is wat jij hebt geschreven — koppen, uitleg, code, de afgedrukte uitkomst die het punt maakte — en er is de JSON-envelop waarin het formaat dat opbergt. Open een .ipynb in een teksteditor en je ziet de envelop: elke regel proza opgesplitst in een eigen string, "execution_count": 7 bij elke cel, overal lege "metadata": {}-objecten, en ergens in het midden een kwart megabyte base64 die één kleine puntenwolk oplevert.
Converteren naar Markdown gooit de envelop weg en houdt het document over. Je markdown-cellen wáren al Markdown, dus die gaan ongewijzigd door. Codecellen worden fenced blocks met de taal van de kernel erop — de vorm die elk model, elke README en elke statischesitegenerator al begrijpt. Sleep hierboven een .ipynb naar binnen en de conversie gebeurt in dit browsertabblad: het bestand wordt nooit geüpload.
Waar het gewicht werkelijk zit
Mensen gaan ervan uit dat een groot notebook groot is door de code. Dat is het bijna nooit. Het gewicht zit op vier plekken, in aflopende volgorde:
- Base64-afbeeldingen in de uitvoer. Elke
plt.show()schrijft de gerenderde PNG als base64-tekst in het bestand. Eén figuur is doorgaans 200 tot 400 KB aan tekens. Een notebook met een stuk of twaalf grafieken bestáát grotendeels uit grafieken. Voor wie de tekst leest, betekent daar niets van iets. - HTML-weergaven van dataframes. Pandas geeft voor hetzelfde resultaat zowel
text/plainalstext/htmlterug. De HTML-versie sleept inline stijlen mee plus een<table>met een tag per cel, dus die kan twintig keer zo groot zijn als de platte versie terwijl er precies hetzelfde staat. - Frames van voortgangsbalken. Een tqdm-lus schrijft bij elke verversing een nieuwe regel, gescheiden door carriage returns. Uiteindelijk staan er honderden kopieën in het bestand van een balk die maar één keer op het scherm stond.
- Metadata per cel. Los van elkaar verwaarloosbaar, bij elkaar niet: cel-id's, uitvoertellers, ingeklapt-vlaggen en lege metadata-objecten over een paar honderd cellen tikken aan.
De conversie hierboven pakt elk van deze punten aan. Mediapayloads worden geteld en vervangen door één markeringsregel, zodat je nog steeds ziet waar een figuur stond. Biedt een resultaat zowel platte tekst als HTML, dan wint de platte tekst. Reeksen carriage returns klappen samen tot de eindtoestand van de regel. Metadata verdwijnt volledig. Het gereedschap rapporteert wat het heeft weggehaald, zodat de besparing zichtbaar is in plaats van beweerd.
Waarom fenced blocks meer uitmaken dan ze lijken
Het nuttigste wat Markdown voor een notebook doet, is de grens tussen proza en code expliciet markeren. In de ruwe JSON bestaat die grens alleen als een veld "cell_type" een paar regels boven de inhoud. Slordig platgeslagen lopen een uitleg en de code die erbij hoort in elkaar over, en moet een lezer — mens of model — puur uit de syntaxis afleiden wat wat is.
Een fence met de taal erop haalt dat giswerk weg. Hij draagt bovendien de kerneltaal mee: de converter leest language_info uit de notebook-metadata en valt terug op de kernelspec, zodat een R- of Julia-notebook ook als R of Julia gemarkeerd wordt en niet stilletjes als Python. Uitvoer krijgt een eigen fence zonder taal onder de cel die hem voortbracht, met daarboven een simpele regel Output:, wat het oorzakelijk verband zichtbaar houdt zonder syntaxis te verzinnen die Markdown niet heeft.
Tracebacks zijn het bewaren waard
Er bestaat een neiging om foutuitvoer samen met de rest weg te gooien. Meestal is dat de verkeerde keuze. Geef je een notebook aan een model met de vraag waarom een cel faalt, dan ís de traceback de hele vraag. Wat tracebacks in ruwe vorm onprettig maakt is niet de inhoud maar de ANSI-kleurcodes waarin IPython ze verpakt — escapereeksen die in een terminal als leesbare kleur verschijnen en overal elders als ESC[0;31m-rommel.
Die reeksen worden gestript en de tekst van de traceback blijft staan. Lange tracebacks worden vanuit het midden ingekort in plaats van vanaf het eind, want in een diepe stack zijn de eerste en de laatste frames de nuttige, en de tweehonderd regels bibliotheekinternals ertussen zijn precies wat je zelf ook had overgeslagen.
Notebooks in een repository-dump
Dezelfde conversie draait binnen de GitHub-, GitLab- en lokale-mapgereedschappen op deze site. Selecteer een .ipynb in een repository en het belandt als leesbare cellen in de uitvoer in plaats van als een muur JSON. Dat maakt meer uit dan het klinkt: in een data-science-repository zit de eigenlijke redenering vaak juist in de notebooks, en tot nu toe waren dat de bestanden die je moest uitvinken om de uitvoer bruikbaar te houden.
In die context is de uitvoerlimiet krapper dan op deze pagina, want een repository-dump is context voor een model en celuitvoer is daarvan het minst waardevolle deel per token. Een notitie in de omgezette tekst legt vast hoeveel er is weggehaald, zodat er niets stilletjes verdwijnt.
Oudere notebooks werken gewoon
Formaatversie 4 zet cellen in een cells-array op het hoogste niveau. Versie 3, die nog altijd verspreid ligt over publieke repository's uit het begin van de jaren tien, nestelt ze een niveau dieper in worksheets en noemt het bronveld input in plaats van source. Beide vormen worden gelezen. Net als het uitvoertype pyout dat versie 3 gebruikte waar versie 4 execute_result zegt.
Twee dingen worden bewust niet geprobeerd. Notebook-widgets — interactieve schuifregelaars en grafieken op basis van ipywidgets — bewaren hun toestand in een apart metadatablok en leveren als tekst niets bruikbaars op; de markering vertelt je dát er een stond. En de uitvoervolgorde van cellen blijft zoals die in het bestand staat, niet gesorteerd op uitvoerteller, want de volgorde waarin je een notebook leest is de volgorde waarin het geschreven is.
Er wordt niets geüpload
Notebooks slepen dingen mee waar mensen niet meer aan denken: een API-sleutel die tijdens het debuggen even in een cel is geplakt, een databaseconnectiestring, een plak productiedata die is afgedrukt om een join te controleren. De conversie draait in JavaScript in dit tabblad. Er is geen uploadstap, geen serverkopie en achteraf niets te laten verwijderen. Sluit het tabblad en het is weg.
Wil je liever platte tekst dan Markdown — geen fences, geen structuur, alleen de proza en de code — dan levert de notebook-naar-tekst-converter dat, en het bestand dat je hebt geselecteerd gaat mee.