Utilizzo dei notebook IPython sotto controllo versione


569

Qual è una buona strategia per mantenere i notebook IPython sotto controllo della versione?

Il formato del notebook è abbastanza adatto per il controllo della versione: se si desidera controllare la versione del notebook e delle uscite, allora funziona abbastanza bene. Il fastidio arriva quando si vuole solo controllare la versione dell'ingresso, escludendo le uscite delle celle (ovvero "prodotti di costruzione") che possono essere grandi blocchi binari, specialmente per film e trame. In particolare, sto cercando di trovare un buon flusso di lavoro che:

  • mi permette di scegliere tra includere o escludere l'output,
  • mi impedisce di commettere accidentalmente output se non lo voglio,
  • mi consente di mantenere l'output nella mia versione locale,
  • mi permette di vedere quando ho cambiamenti negli input usando il mio sistema di controllo della versione (cioè se controllo solo gli input ma il mio file locale ha degli output, allora vorrei essere in grado di vedere se gli input sono cambiati (richiedendo un commit L'uso del comando status controllo versione registrerà sempre una differenza poiché il file locale ha output.)
  • mi permette di aggiornare il mio taccuino funzionante (che contiene l'output) da un taccuino pulito aggiornato. (aggiornare)

Come accennato, se ho scelto di includere gli output (il che è desiderabile quando si utilizza nbviewer per esempio), allora va tutto bene. Il problema è quando non voglio controllare la versione dell'output. Esistono alcuni strumenti e script per rimuovere l'output del notebook, ma spesso riscontro i seguenti problemi:

  1. Commetto accidentalmente una versione con l'output, inquinando così il mio repository.
  2. Deseleziono l'output per utilizzare il controllo versione, ma preferirei piuttosto conservare l'output nella mia copia locale (a volte ci vuole un po 'di tempo per riprodurlo, ad esempio).
  3. Alcuni degli script che rimuovono l'output cambiano leggermente il formato rispetto Cell/All Output/Clearall'opzione di menu, creando così rumore indesiderato nelle differenze. Questo è risolto da alcune delle risposte.
  4. Quando tengo le modifiche a una versione pulita del file, devo trovare un modo per incorporare quelle modifiche nel mio taccuino di lavoro senza dover rieseguire tutto. (aggiornare)

Ho preso in considerazione diverse opzioni di cui parlerò di seguito, ma devo ancora trovare una buona soluzione globale. Una soluzione completa potrebbe richiedere alcune modifiche a IPython o fare affidamento su alcuni semplici script esterni. Attualmente utilizzo mercurial , ma vorrei una soluzione che funzioni anche con git : una soluzione ideale sarebbe agnostica di controllo della versione.

Questo problema è stato discusso molte volte, ma non esiste una soluzione definitiva o chiara dal punto di vista dell'utente. La risposta a questa domanda dovrebbe fornire la strategia definitiva. Va bene se richiede una versione recente (anche di sviluppo) di IPython o un'estensione facilmente installabile.

Aggiornamento: sto giocando con la mia versione modificata del taccuino che salva facoltativamente una .cleanversione con ogni salvataggio usando i suggerimenti di Gregory Crosswhite . Ciò soddisfa la maggior parte dei miei vincoli ma lascia irrisolto quanto segue:

  1. Questa non è ancora una soluzione standard (richiede una modifica della sorgente ipython. Esiste un modo per ottenere questo comportamento con una semplice estensione? Ha bisogno di una sorta di hook on-save.
  2. Un problema che ho con l'attuale flusso di lavoro sta tirando le modifiche. Questi arriveranno nel .cleanfile e quindi dovranno essere integrati in qualche modo nella mia versione funzionante. (Certo, posso sempre rieseguire il notebook, ma questo può essere un problema, soprattutto se alcuni dei risultati dipendono da lunghi calcoli, calcoli paralleli, ecc.) Non ho ancora una buona idea su come risolverlo . Forse un flusso di lavoro che coinvolge un'estensione come ipycache potrebbe funzionare, ma sembra un po 'troppo complicato.

Appunti

Rimozione (stripping) dell'uscita

  • Quando il notebook è in esecuzione, è possibile utilizzare l' Cell/All Output/Clearopzione di menu per rimuovere l'output.
  • Esistono alcuni script per la rimozione dell'output, come lo script nbstripout.py che rimuove l'output, ma non produce lo stesso output dell'interfaccia del notebook. Questo è stato infine incluso nel repository ipython / nbconvert , ma è stato chiuso affermando che le modifiche sono ora incluse in ipython / ipython , ma la funzionalità corrispondente sembra non essere stata ancora inclusa. (aggiornamento) Detto questo, la soluzione di Gregory Crosswhite mostra che questo è abbastanza facile da fare, anche senza invocare ipython / nbconvert, quindi questo approccio è probabilmente praticabile se può essere correttamente agganciato. (Collegarlo a ciascun sistema di controllo versione, tuttavia, non sembra una buona idea - questo dovrebbe in qualche modo collegarsi al meccanismo del notebook.)

Newsgroup

Problemi

Richieste pull


Sembra un'ottima cosa aggiungere un problema su github.com/ipython/ipython o inviare una richiesta pull che ti aiuti a raggiungere questo obiettivo.
Kyle Kelley,

4
Una volta che hai uno script funzionante per rimuovere l'output, puoi usare un filtro "clean" di Git per applicarlo automaticamente prima di eseguire il commit (vedi filtri clean / smudge).
Matthias,

1
@foobaremade La domanda contiene soluzioni alternative insoddisfacenti: ognuna ha almeno una limitazione. Ora che PR 4175 è stato unito, probabilmente è possibile formulare una soluzione completa, ma è ancora necessario farlo. Non appena avrò del tempo, lo farò (come risposta) se nel frattempo qualcun altro non fornisce una soluzione soddisfacente.
mforbes,

1
@saroele Non ho ancora trovato una soluzione consigliata: stavo per scegliere l' --scriptopzione, ma è stata rimossa. Sto aspettando che vengano implementati gli hook post-salvataggio ( che sono previsti ), a quel punto penso che sarò in grado di fornire una soluzione accettabile combinando molte delle tecniche.
lunedì

1
@mforbes Sembra che PR sia stata unita solo pochi giorni dopo il tuo commento. Potresti tu o qualcuno più esperto di me pubblicare qui una risposta che mostra come utilizzare la nuova funzionalità?
KobeJohn,

Risposte:


124

Ecco la mia soluzione con git. Ti permette di aggiungere e impegnare (e diff) come al solito: quelle operazioni non modificheranno il tuo albero di lavoro e allo stesso tempo (ri) eseguire un notebook non cambierà la tua cronologia git.

Sebbene questo possa probabilmente essere adattato ad altri VCS, so che non soddisfa i tuoi requisiti (almeno l'agnosticità VSC). Tuttavia, è perfetto per me, e sebbene non sia nulla di particolarmente brillante, e molte persone probabilmente lo usano già, non ho trovato istruzioni chiare su come implementarlo facendo ricerche su Google. Quindi può essere utile ad altre persone.

  1. Salvare un file con questo contenuto da qualche parte (per quanto segue, supponiamo ~/bin/ipynb_output_filter.py)
  2. Renderlo eseguibile ( chmod +x ~/bin/ipynb_output_filter.py)
  3. Crea il file ~/.gitattributes, con il seguente contenuto

    *.ipynb    filter=dropoutput_ipynb
    
  4. Esegui i seguenti comandi:

    git config --global core.attributesfile ~/.gitattributes
    git config --global filter.dropoutput_ipynb.clean ~/bin/ipynb_output_filter.py
    git config --global filter.dropoutput_ipynb.smudge cat
    

Fatto!

limitazioni:

  • funziona solo con git
  • in git, se sei nel ramo somebranche lo fai git checkout otherbranch; git checkout somebranch, di solito ti aspetti che l'albero di lavoro rimanga invariato. Qui invece avrai perso l'output e la numerazione delle celle dei notebook la cui sorgente differisce tra i due rami.
  • più in generale, l'output non è affatto aggiornato, come con la soluzione di Gregory. Al fine di non buttarlo via ogni volta che fai qualcosa che coinvolge un checkout, l'approccio potrebbe essere cambiato memorizzandolo in file separati (ma nota che al momento dell'esecuzione del codice sopra, l'ID commit non è noto!), e possibilmente il loro controllo delle versioni (ma notate che ciò richiederebbe qualcosa di più di un git commit notebook_file.ipynb, anche se almeno si manterrebbe git diff notebook_file.ipynblibero dalla spazzatura di base64).
  • detto ciò, per inciso se si estrae il codice (ovvero commesso da qualcun altro che non utilizza questo approccio) che contiene un output, l'output viene verificato normalmente. Solo l'output prodotto localmente viene perso.

La mia soluzione riflette il fatto che personalmente non mi piace mantenere le versioni generate degli oggetti - nota che fare fusioni che coinvolgono l'output è quasi garantito per invalidare l'output o la tua produttività o entrambi.

MODIFICARE:

  • se adotti la soluzione come ti ho suggerito, ovvero a livello globale, avrai problemi nel caso in cui per qualche repository git desideri l'output della versione. Quindi, se si desidera disabilitare il filtro di output per un repository git specifico, è sufficiente creare al suo interno un file .git / info / attributi , con

    **. filtro ipynb =

come contenuto. Chiaramente, allo stesso modo è possibile fare il contrario: abilitare il filtro solo per un repository specifico.

  • il codice è ora gestito nel suo repository git

  • se le istruzioni sopra risultano in ImportErrors, prova ad aggiungere "ipython" prima del percorso dello script:

    git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
    

EDIT : Maggio 2016 (aggiornato a febbraio 2017): ci sono diverse alternative alla mia sceneggiatura - per completezza, ecco un elenco di quelli che conosco: nbstripout ( altre varianti ), nbstrip , jq .


2
Come affronti il ​​problema dell'incorporazione delle modifiche che prendi? Vivi solo per dover rigenerare tutto l'output? (Penso che questa sia una manifestazione della tua seconda limitazione.)
mforbes

1
@zhermes: questa versione estesa dovrebbe essere OK
Pietro Battiston,

1
C'è un modo per usare questo metodo di filtri git con uno strumento diff esterno? Il filtro viene applicato se utilizzo il normale strumento da riga di comando, ma non se utilizzo meld come strumento diff. stackoverflow.com/q/30329615/578770
FA

1
Per evitare di ottenere ImportErrorho modificato quanto sopra per eseguire usando ipython:git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
chris838

1
Splendida soluzione Pietro, grazie :) Ho cambiato 2 cose quando ho usato il tuo script nel mio caso: 1) Ho preferito dichiarare il filtro in .gitattributes nella radice del repository rispetto a ~/.gitattributes, altre persone hanno gli stessi filtri di me 2 ) Ho definito regexp come workdir/**/*.ipynb filter=dropoutput_ipynb, e ho messo la maggior parte dei miei notebook in workdir / => se voglio ancora inviare un notebook con l'output e godermi il rendering dei segnalibri in github, lo metto appena fuori da quella cartella.
Svend,

63

Abbiamo un progetto collaborativo in cui il prodotto è Jupyter Notebooks e negli ultimi sei mesi abbiamo utilizzato un approccio che funziona alla grande: attiviamo il salvataggio .pyautomatico dei file e tracciamo sia i .ipynbfile che i .pyfile.

In questo modo, se qualcuno vuole visualizzare / scaricare l'ultimo notebook, può farlo tramite github o nbviewer e se qualcuno vuole vedere come è cambiato il codice del notebook, può semplicemente guardare le modifiche ai .pyfile.

Per i Jupyterserver notebook , ciò può essere ottenuto aggiungendo le linee

import os
from subprocess import check_call

def post_save(model, os_path, contents_manager):
    """post-save hook for converting notebooks to .py scripts"""
    if model['type'] != 'notebook':
        return # only do this for notebooks
    d, fname = os.path.split(os_path)
    check_call(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d)

c.FileContentsManager.post_save_hook = post_save

al jupyter_notebook_config.pyfile e al riavvio del server notebook.

Se non sei sicuro in quale directory trovare il tuo jupyter_notebook_config.pyfile, puoi digitare jupyter --config-dire se non trovi il file lì, puoi crearlo digitando jupyter notebook --generate-config.

Per i Ipython 3server notebook , ciò può essere ottenuto aggiungendo le linee

import os
from subprocess import check_call

def post_save(model, os_path, contents_manager):
    """post-save hook for converting notebooks to .py scripts"""
    if model['type'] != 'notebook':
        return # only do this for notebooks
    d, fname = os.path.split(os_path)
    check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)

c.FileContentsManager.post_save_hook = post_save

al ipython_notebook_config.pyfile e al riavvio del server notebook. Queste righe provengono da una risposta ai problemi di github fornita da @minrk e @dror le include anche nella sua risposta SO.

Per i Ipython 2server notebook , ciò può essere realizzato avviando il server utilizzando:

ipython notebook --script

o aggiungendo la linea

c.FileNotebookManager.save_script = True

al ipython_notebook_config.pyfile e al riavvio del server notebook.

Se non sei sicuro in quale directory trovare il tuo ipython_notebook_config.pyfile, puoi digitare ipython locate profile defaulte se non trovi il file lì, puoi crearlo digitando ipython profile create.

Ecco il nostro progetto su github che utilizza questo approccio : ed ecco un esempio github di esplorare le recenti modifiche a un notebook .

Siamo stati molto contenti di questo.


1
Grazie per le prove aggiuntive che l'utilizzo --scriptha funzionato nella pratica. Il problema è che gli attuali notebook potrebbero essere enormi se le immagini fossero conservate. Una soluzione ideale lungo questa strada potrebbe usare qualcosa come git-annex per tenere traccia solo dell'ultimo notebook completo.
mforbes,

In Ipython 3.x --scriptè deprecato. ipython.org/ipython-doc/3/whatsnew/version3.html
Dror

Grazie @dror, ho aggiornato la mia risposta per fornire la soluzione ipython 3.x di minrk come fornito anche qui.
Rich Signell,

10
Aggiornamento: questa soluzione è rotta in iPython versione 4, a causa di "The Big Split" di Jupyter di iPython. Per adattare questa soluzione alla versione 4, utilizzare il comando jupyter notebook --generate-configper creare un file di configurazione. Il comando jupyter --config-dirrileva quale directory contiene i file di configurazione. E lo snippet di codice fornito da @Rich dovrebbe essere aggiunto al file denominato jupyter_notebook_config.py. Il resto funziona come prima.
gnocco di mobius,

2
Oltre al punto di @mobiusdumpling, sostituisci check_call(['ipython'con check_call(['jupyter', altrimenti riceverai un avviso che ipython nbconvertè deprecato e dovresti jupyter nbconvertinvece utilizzare . (Jupyter v4.1.0, iPython v4.1.2)
cutculus

36

Ho creato nbstripout, basato su sull'essenza di MinRK , che supporta sia Git che Mercurial (grazie a mforbes). È inteso per essere utilizzato autonomamente sulla riga di comando o come filtro, che può essere facilmente (dis) installato nel repository corrente tramite nbstripout install/ nbstripout uninstall.

Ricevilo da PyPI o semplicemente

pip install nbstripout

Sto prendendo in considerazione un flusso di lavoro in cui mantengo sia .ipynb che corrispondenti .py creati automaticamente utilizzando gli hook post-salvataggio descritti sopra. Vorrei usare .py per diff - nbstripout sarebbe in grado di cancellare il file .py dai contatori di esecuzione delle celle (# In [1] modificato in In [*]), in modo che non ingombrino i diff o dovrei creare un semplice script per farlo?
Krzysztof Słowiński,

1
@ KrzysztofSłowiński No, nbstripoutnon supporta facilmente questo caso d'uso poiché si basa sul formato JSON del Notebook. Probabilmente stai meglio scrivendo una sceneggiatura specializzata nel tuo caso d'uso.
kynan,


13

Dopo alcuni anni di rimozione degli output nei notebook, ho cercato di trovare una soluzione migliore. Ora uso Jupytext , un'estensione sia per Jupyter Notebook che per Jupyter Lab che ho progettato.

Jupytext può convertire i notebook Jupyter in vari formati di testo (Script, Markdown e R Markdown). E viceversa. Offre inoltre la possibilità di associare un notebook a uno di questi formati e di sincronizzare automaticamente le due rappresentazioni del notebook (an .ipynbe un .md/.py/.Rfile).

Lasciami spiegare come Jupytext risponde alle domande precedenti:

mi permette di scegliere tra includere o escludere l'output,

Il .md/.py/.Rfile contiene solo le celle di input. Dovresti sempre tenere traccia di questo file. Versione del .ipynbfile solo se si desidera tenere traccia degli output.

mi impedisce di commettere accidentalmente output se non lo voglio,

Aggiungi *.ipynba.gitignore

mi consente di mantenere l'output nella mia versione locale,

Gli output sono conservati nel .ipynbfile (locale)

mi permette di vedere quando ho cambiamenti negli input usando il mio sistema di controllo della versione (cioè se controllo solo gli input ma il mio file locale ha output, allora vorrei essere in grado di vedere se gli input sono cambiati (richiedendo un commit L'uso del comando status controllo versione registrerà sempre una differenza poiché il file locale ha output.)

Il diff sul file .py/.Ro .mdè quello che stai cercando

mi permette di aggiornare il mio taccuino funzionante (che contiene l'output) da un taccuino pulito aggiornato. (aggiornare)

Estrai l'ultima revisione del file .py/.Ro .mde aggiorna il tuo notebook in Jupyter (Ctrl + R). Otterrai le celle di input più recenti dal file di testo, con output corrispondenti dal .ipynbfile. Il kernel non è interessato, il che significa che le tue variabili locali sono preservate: puoi continuare a lavorare dove l'hai lasciato.

Quello che adoro di Jupytext è che il blocco note (sotto forma di file .py/.Ro .md) può essere modificato nel tuo IDE preferito. Con questo approccio, il refactoring di un notebook diventa facile. Una volta terminato, devi solo aggiornare il notebook in Jupyter.

Se vuoi provarlo: installa Jupytext pip install jupytexte riavvia il tuo Jupyter Notebook o Lab editor. Aprire il blocco note che si desidera controllare la versione e associarlo a un file Markdown (o uno script) utilizzando il menu Jupytext nel blocco note Jupyter (oi comandi Jupytext in Jupyter Lab). Salva il tuo notebook e otterrai i due file: l'originale .ipynb, più la promessa rappresentazione testuale del notebook, che si adatta perfettamente al controllo della versione!

Per coloro che potrebbero essere interessati: Jupytext è disponibile anche sulla riga di comando .


13

Aggiornamento : ora puoi modificare i file di Jupyter Notebook direttamente in Visual Studio Code. Puoi scegliere di modificare il blocco note o il file Python convertito.

Alla fine ho trovato un modo produttivo e semplice per far giocare insieme Jupyter e Git. Sono ancora nei primi passi, ma penso già che sia molto meglio di tutte le altre soluzioni contorte.

Visual Studio Code è un editor di codice cool e open source di Microsoft. Ha un'eccellente estensione Python che ora consente di importare un Notebook Jupyter come codice Python. Ora puoi anche modificare direttamente i Notebook Jupyter .

Dopo aver importato il tuo notebook in un file Python, tutto il codice e il markdown saranno riuniti in un normale file Python, con marcatori speciali nei commenti. Puoi vedere nell'immagine qui sotto:

Editor VSCode con un notebook convertito in python

Il tuo file Python ha solo il contenuto delle celle di input del notebook. L'output verrà generato in una finestra divisa. Hai un codice puro nel notebook, non cambia mentre lo esegui. Nessun output misto con il tuo codice. Nessuno strano formato incomprensibile JSON per analizzare i tuoi diff.

Solo codice Python puro in cui è possibile identificare facilmente ogni singolo diff.

Non ho nemmeno più bisogno di versioni dei miei .ipynbfile. Posso mettere una *.ipynblinea .gitignore.

Devi generare un blocco note per pubblicare o condividere con qualcuno? Nessun problema, basta fare clic sul pulsante Esporta nella finestra interattiva di Python

Esportazione di un file Python in formato Notebook

Se stai modificando direttamente il notebook, ora c'è un'icona Convert and save to a python script. Icone di Giove nel codice di Visual Studio

Ecco uno screenshot di un notebook all'interno del codice di Visual Studio:

Modifica del notebook all'interno del VSCode

L'ho usato solo per un giorno, ma finalmente posso usare felicemente Jupyter con Git.

PS: il completamento del codice VSCode è molto meglio di Jupyter.


12

(2017-02)

strategie

  • on_commit ():
    • eliminare l'output> name.ipynb ( nbstripout,)
    • eliminare l'output> name.clean.ipynb ( nbstripout,)
    • sempre nbconvertsu Python: name.ipynb.py ( nbconvert)
    • converti sempre in markdown: name.ipynb.md ( nbconvert, ipymd)
  • vcs.configure ():
    • git difftool, mergetool: nbdiff e nbmerge di nbdime

utensili


11

Le risposte molto popolari del 2016 sopra sono hack incoerenti rispetto al modo migliore per farlo nel 2019.

Esistono diverse opzioni, la migliore che risponde alla domanda è Jupytext.

Jupytext

Prendere l' articolo nei confronti della scienza dei dati su Jupytext

Il modo in cui funziona con il controllo versione è di mettere entrambi i file .py e .ipynb nel controllo versione. Guarda il .py se vuoi l'input diff, guarda il .ipynb se vuoi l'ultimo output renderizzato.

Menzioni importanti: VS studio, nbconvert, nbdime, idrogeno

Penso che con un po 'più di lavoro, VS studio e / o idrogeno (o simili) diventeranno i protagonisti nella soluzione a questo flusso di lavoro.


9

Basta trovare "jupytext" che sembra una soluzione perfetta. Genera un file .py dal notebook e quindi mantiene entrambi sincronizzati. Puoi controllare la versione, diff e unire gli input tramite il file .py senza perdere gli output. Quando si apre il notebook, utilizza .py per le celle di input e .ipynb per l'output. E se vuoi includere l'output in git, puoi semplicemente aggiungere l'ipynb.

https://github.com/mwouts/jupytext


9

Dal momento che esistono così tante strategie e strumenti per gestire il controllo della versione per notebook, ho cercato di creare un diagramma di flusso per scegliere una strategia adatta (creata ad aprile 2019)

Flusso decisionale per selezionare la strategia di controllo della versione


8

Come sottolineato da, --scriptè deprecato in 3.x. Questo approccio può essere utilizzato applicando un gancio post-salvataggio. In particolare, aggiungere quanto segue a ipython_notebook_config.py:

import os
from subprocess import check_call

def post_save(model, os_path, contents_manager):
    """post-save hook for converting notebooks to .py scripts"""
    if model['type'] != 'notebook':
        return # only do this for notebooks
    d, fname = os.path.split(os_path)
    check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)

c.FileContentsManager.post_save_hook = post_save

Il codice è preso da # 8009 .


Grazie per aver dimostrato l'uso di un hook post-salvataggio. Sfortunatamente, come già menzionato, tornare dal .pyfile a un notebook è problematico, quindi purtroppo non è una soluzione completa. (Vorrei che fosse molto bello per i .pyfile diff invece che per i notebook. Forse la nuova funzione diff per notebook sarà utile.
mforbes

1
Grazie! Ora sto usando questo trucco per riprodurre il --scriptcomportamento, indipendentemente dal controllo della versione. Inizialmente ho avuto dei problemi, quindi nel caso in cui potessi salvare qualcuno un po 'di tempo: 1) Se ipython_notebook_config.pymanca nella cartella del profilo, corri ipython profile createper generarlo. 2) Se sembra che il post-save-hook sia ignorato, eseguire ipython con --debugper diagnosticare il problema. 3) Se lo script non riesce con l'errore ImportError: No module named mistune- semplice installazione minstue: pip install mistune.
Joe

7

Sfortunatamente, non so molto su Mercurial, ma posso darti una possibile soluzione che funziona con Git, nella speranza che tu possa essere in grado di tradurre i miei comandi Git nei loro equivalenti Mercurial.

Per lo sfondo, in Git il addcomando memorizza le modifiche apportate a un file in un'area di gestione temporanea. Una volta fatto questo, qualsiasi successiva modifica al file viene ignorata da Git a meno che tu non gli dica di metterlo in scena. Quindi, il seguente script, che, per ciascuno dei file dati, rimuove tutti i file outputse prompt_number sections, mette in scena il file rimosso e quindi ripristina l'originale:

NOTA: se si esegue questo, viene visualizzato un messaggio di errore simile ImportError: No module named IPython.nbformat, quindi utilizzare ipythonper eseguire lo script anziché python.

from IPython.nbformat import current
import io
from os import remove, rename
from shutil import copyfile
from subprocess import Popen
from sys import argv

for filename in argv[1:]:
    # Backup the current file
    backup_filename = filename + ".backup"
    copyfile(filename,backup_filename)

    try:
        # Read in the notebook
        with io.open(filename,'r',encoding='utf-8') as f:
            notebook = current.reads(f.read(),format="ipynb")

        # Strip out all of the output and prompt_number sections
        for worksheet in notebook["worksheets"]:
            for cell in worksheet["cells"]:
               cell.outputs = []
               if "prompt_number" in cell:
                    del cell["prompt_number"]

        # Write the stripped file
        with io.open(filename, 'w', encoding='utf-8') as f:
            current.write(notebook,f,format='ipynb')

        # Run git add to stage the non-output changes
        print("git add",filename)
        Popen(["git","add",filename]).wait()

    finally:
        # Restore the original file;  remove is needed in case
        # we are running in windows.
        remove(filename)
        rename(backup_filename,filename)

Una volta eseguito lo script sui file di cui si desidera eseguire il commit delle modifiche, eseguire semplicemente git commit.


Grazie per il suggerimento Mercurial non ha realmente un'area di staging come git (anche se a questo scopo si potrebbero usare le code mercurial ). Nel frattempo, ho provato ad aggiungere questo codice a un hook di salvataggio che salva una versione pulita con .cleanun'estensione. Sfortunatamente, non riuscivo a vedere come farlo senza modificare direttamente IPython (sebbene questa modifica fosse piuttosto banale). Giocherò con questo per un po 'e vedrò se soddisfa tutti i miei bisogni.
lunedì

6

Uso un approccio molto pragmatico; che funzionano bene per diversi notebook, su più lati. E mi consente persino di "trasferire" i portatili in giro. Funziona sia per Windows come Unix / MacOS.
Al ha pensato che è semplice, è risolvere i problemi sopra ...

Concetto

Fondamentalmente, non tracciare i file .ipnyb, ma solo i file corrispondenti .py.
Avviando il notebook-server con l' --scriptopzione, quel file viene automaticamente creato / salvato quando il notebook viene salvato.

Questi file .pycontengono tutti gli input; il non codice viene salvato nei commenti, così come i bordi delle celle. Tali file possono essere letti / importati (e trascinati) nel server notebook per (ri) creare un notebook. Solo l'output è sparito; fino a quando non viene eseguito nuovamente.

Personalmente uso mercurial per tracciare la versione dei .pyfile; e usa i normali comandi (riga di comando) per aggiungere, check-in (ect) per quello. La maggior parte degli altri (D) VCS lo consentirà.

È semplice tenere traccia della storia ora; la .pysono piccoli, testuali e semplice da diff. Di tanto in tanto, abbiamo bisogno di un clone (solo branch; avvia lì un secondo notebook-sever) o una versione precedente (check-it e importazione in un notebook-server), ecc.

Suggerimenti e trucchi

  • Aggiungi * .ipynb a ' .hgignore ', in modo che Mercurial sappia che può ignorare quei file
  • Crea uno script (bash) per avviare il server (con l' --scriptopzione) ed esegui il tracciamento della versione
  • Salvataggio di un notebook non Salvare il .py-file, ma non senza il check in.
    • Questo è uno svantaggio : uno può dimenticarlo
    • È anche una caratteristica : è possibile salvare un notebook (e continuare in seguito) senza raggruppare la cronologia del repository.

Auguri

  • Sarebbe bello avere un pulsante per il check-in / aggiungere / etc nella dashboard del notebook
  • Un checkout a (per esempio) file@date+rev.py) dovrebbe essere utile Sarebbe molto lavoro aggiungere questo; e forse lo farò una volta. Fino ad ora, lo faccio solo a mano.

Come si passa dal .pyfile a un notebook? Mi piace questo approccio, ma poiché .ipynb-> .py-> .ipynbè potenzialmente in perdita, non l'ho preso sul serio.
mforbes,

È facile: caricalo, ad esempio, facendolo cadere sul pannello di Notebook. Tranne che per "dati di output" non si perde nulla
Albert,

Se questo è vero, allora penso che questo sarebbe stato vicino a idea, ma mi sembra di ricordare che IPython fatto alcun impegno a preservare completamente i dati nel passaggio da .pya .ipynbformati. C'è un problema al riguardo , quindi forse questo costituirà la base per una soluzione completa.
mforbes,

Sto riscontrando difficoltà nella conversione da .pyfile a .ipynbfile. nbconvertnon sembra ancora supportare questo, e non ho una dashboard per notebook da quando eseguo ipython notebookmanualmente. Hai suggerimenti generali su come implementare questa conversione all'indietro?
mforbes

Sicuramente la .pytrasformazione da notebook a notebook non è destinata al round trip. Quindi questa non può davvero essere una soluzione generale sebbene sia bello che funzioni per te.
holdenweb,

3

Per dare seguito all'eccellente sceneggiatura di Pietro Battiston, se ricevi un errore di analisi Unicode come questo:

Traceback (most recent call last):
  File "/Users/kwisatz/bin/ipynb_output_filter.py", line 33, in <module>
write(json_in, sys.stdout, NO_CONVERT)
  File "/Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/nbformat/__init__.py", line 161, in write
fp.write(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2014' in position 11549: ordinal not in range(128)

Puoi aggiungere all'inizio dello script:

reload(sys)
sys.setdefaultencoding('utf8')

3

Ho creato un pacchetto Python che risolve questo problema

https://github.com/brookisme/gitnb

Fornisce una CLI con una sintassi ispirata a git per tracciare / aggiornare / diff notebook all'interno del tuo repository git.

Ecco un esempio

# add a notebook to be tracked
gitnb add SomeNotebook.ipynb

# check the changes before commiting
gitnb diff SomeNotebook.ipynb

# commit your changes (to your git repo)
gitnb commit -am "I fixed a bug"

Nota che l'ultimo passaggio, in cui sto usando "gitnb commit", è il commit nel tuo repository git. È essenzialmente un involucro per

# get the latest changes from your python notebooks
gitnb update

# commit your changes ** this time with the native git commit **
git commit -am "I fixed a bug"

Esistono molti altri metodi e possono essere configurati in modo da richiedere un input maggiore o minore da parte dell'utente in ogni fase, ma questa è l'idea generale.


3

Dopo aver scavato, ho finalmente trovato questo gancio di pre-salvataggio relativamente semplice sui documenti di Jupyter . Elimina i dati di output della cella. Devi incollarlo nel jupyter_notebook_config.pyfile (vedi sotto per le istruzioni).

def scrub_output_pre_save(model, **kwargs):
    """scrub output before saving notebooks"""
    # only run on notebooks
    if model['type'] != 'notebook':
        return
    # only run on nbformat v4
    if model['content']['nbformat'] != 4:
        return

    for cell in model['content']['cells']:
        if cell['cell_type'] != 'code':
            continue
        cell['outputs'] = []
        cell['execution_count'] = None
        # Added by binaryfunt:
        if 'collapsed' in cell['metadata']:
            cell['metadata'].pop('collapsed', 0)

c.FileContentsManager.pre_save_hook = scrub_output_pre_save

Dalla risposta di Rich Signell :

Se non sei sicuro in quale directory trovare il tuo jupyter_notebook_config.pyfile, puoi digitare jupyter --config-dir[nel prompt dei comandi / terminale], e se non trovi il file lì, puoi crearlo digitando jupyter notebook --generate-config.


1
Vorrei notare che questa soluzione non salverebbe mai alcun output su disco ed è in qualche modo indipendente dal problema del controllo della versione.
bdforbes,

2

Ho fatto ciò che Albert & Rich ha fatto - Non versioni di file .ipynb (poiché possono contenere immagini, che diventano disordinate). Invece, esegui ipython notebook --scripto inserisci sempre il c.FileNotebookManager.save_script = Truetuo file di configurazione, in modo che un .pyfile (versionable) sia sempre creato quando salvi il tuo notebook.

Per rigenerare i notebook (dopo aver verificato un repository o aver cambiato un ramo) ho inserito lo script py_file_to_notebooks.py nella directory in cui memorizzo i miei notebook.

Ora, dopo aver verificato un repository, basta eseguire python py_file_to_notebooks.pyper generare i file ipynb. Dopo aver cambiato filiale, potrebbe essere necessario eseguire python py_file_to_notebooks.py -ovper sovrascrivere i file ipynb esistenti.

Solo per essere al sicuro, è bene aggiungere anche *.ipynbal tuo .gitignorefile.

Modifica: non lo faccio più perché (A) devi rigenerare i tuoi notebook da file PY ogni volta che esegui il checkout di un ramo e (B) ci sono altre cose come il markdown nei notebook che perdi. Invece rimuovo l'output dai notebook usando un filtro git. La discussione su come fare questo è qui .


Questa idea mi è piaciuta, ma dopo i test ho scoperto che la conversione dai .pyfile a .ipynbè problematica, soprattutto con i notebook versione 4 per i quali non esiste ancora un convertitore. Uno dovrebbe attualmente utilizzare l'importatore v3 quindi convertire in v4 e sono un po 'preoccupato per questo viaggio complicato. Inoltre, un .pyfile non è un'ottima scelta se il notebook è principalmente il codice Julia! Infine, --scriptè deprecato, quindi penso che i ganci siano la strada da percorrere.
mforbes,

La soluzione di filtro git nel tuo link è buona, dovresti copiare la tua risposta da qui :-)
mcarans

2

Ok, quindi sembra che l'attuale migliore soluzione, come in una discussione qui , sia quella di creare un filtro git per rimuovere automaticamente l'output dai file ipynb su commit.

Ecco cosa ho fatto per farlo funzionare (copiato da quella discussione):

Ho modificato il file nbstripout di cfriedline leggermente per dare un errore informativo quando non è possibile importare l'ultima IPython: https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output e ha aggiunto al mio repo, lascia dire in./relative/path/to/strip_notebook_output

Ha anche aggiunto il file .gitattributes alla radice del repository, contenente:

*.ipynb filter=stripoutput

E creato un setup_git_filters.shcontenimento

git config filter.stripoutput.clean "$(git rev-parse --show-toplevel)/relative/path/to/strip_notebook_output" 
git config filter.stripoutput.smudge cat
git config filter.stripoutput.required true

E corse source setup_git_filters.sh. La fantasia $ (git rev-parse ...) è trovare il percorso locale del tuo repository su qualsiasi macchina (Unix).


1

Questa estensione jupyter consente agli utenti di inviare i notebook jupyter direttamente a github.

Per favore guarda qui

https://github.com/sat28/githubcommit


puoi spiegare cosa fa questo? La documentazione non è particolarmente chiara.
Alex Monras,

@AlexMonras Questo aggiungerà direttamente un pulsante nel quaderno jupyter da cui è possibile inviare i quaderni al repository GitHub con un messaggio di commit
sabato

1

Questo è aprile-2020 e ci sono molte strategie e strumenti per il controllo della versione del notebook Jupyter. Ecco una rapida panoramica di tutti gli strumenti che puoi utilizzare,

  • nbdime : utile per la diffusione locale e la fusione di notebook

  • nbstripout - Un filtro git per rimuovere automaticamente le uscite del notebook prima di ogni commit

  • jupytext - Mantiene un file .py companion sincronizzato con ciascun notebook. Commetti solo file .py

  • nbconvert - Converte i notebook in uno script Python o HTML (o entrambi) e commette questi tipi di file alternativi

  • ReviewNB : mostra la diff del notebook (insieme all'output) per qualsiasi richiesta di commit o pull su GitHub. Si può anche scrivere commenti sulle celle del notebook per discutere le modifiche (screenshot sotto).

inserisci qui la descrizione dell'immagine

Disclaimer: ho creato ReviewNB.


0

Che ne dici dell'idea discussa nel post qui sotto, dove l'output del notebook dovrebbe essere conservato, con l'argomento che potrebbe richiedere molto tempo per generarlo, ed è utile dal momento che GitHub ora può eseguire il rendering dei notebook. Sono stati aggiunti hook di salvataggio automatico per l'esportazione del file .py, utilizzati per diff e .html per la condivisione con i membri del team che non usano notebook o git.

https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.