version-control - Utilizzo di notebook IPython sotto controllo di versione




7 Answers

Abbiamo un progetto collaborativo in cui il prodotto è Jupyter Notebooks e negli ultimi sei mesi abbiamo utilizzato un approccio che funziona .ipynb : attiviamo il salvataggio automatico dei file .py e monitoriamo sia i file .ipynb file .py .

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

Per i server notebook Jupyter , 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 file jupyter_notebook_config.py e al riavvio del server notebook.

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

Per i server notebook Ipython 3 , questo 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 file ipython_notebook_config.py e riavvio del server notebook. Queste righe provengono da problemi github, rispondono @minrk e @dror li include anche nella sua risposta SO.

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

ipython notebook --script

o aggiungendo la linea

c.FileNotebookManager.save_script = True

al file ipython_notebook_config.py e riavvio del server notebook.

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

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

Siamo stati molto contenti di questo.

version-control ipython jupyter-notebook

Qual è una buona strategia per mantenere i taccuini IPython sotto controllo di versione?

Il formato del notebook è abbastanza adatto per il controllo della versione: se si vuole controllare la versione del notebook e delle uscite, allora funziona abbastanza bene. Il fastidio arriva quando si vuole controllare solo la versione dell'input, escludendo le uscite della cella (chiamate anche "build products") che possono essere grandi blob binari, specialmente per film e grafici. 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 l'output se non lo desidero,
  • mi permette di mantenere l'output nella mia versione locale,
  • mi permette di vedere quando ho dei cambiamenti negli input usando il mio sistema di controllo delle versioni (cioè se controllo solo le entrate ma il mio file locale ha delle uscite, allora vorrei essere in grado di vedere se gli input sono cambiati (richiede un commit ) L'uso del comando dello stato di controllo della versione registrerà sempre una differenza poiché il file locale ha delle uscite.)
  • mi consente di aggiornare il mio notebook funzionante (che contiene l'output) da un notebook pulito aggiornato. (aggiornare)

Come accennato, se ho scelto di includere gli output (che è auspicabile quando si usa nbviewer per esempio), allora tutto va 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. Per sbaglio commetto una versione con l'output, inquinando in tal modo il mio repository.
  2. Ho cancellato l'output per usare il controllo della versione, ma preferirei mantenere l'output nella mia copia locale (a volte ci vuole un po 'per riprodurlo ad esempio).
  3. Alcuni degli script che rimuovono l'output cambiano leggermente il formato rispetto all'opzione di menu Cell/All Output/Clear , creando così rumori indesiderati nelle diff. Questo è risolto da alcune delle risposte.
  4. Quando si apportano modifiche a una versione pulita del file, è necessario trovare un modo per incorporare tali modifiche nel mio blocco appunti di lavoro senza dover eseguire nuovamente tutto. (aggiornare)

Ho preso in considerazione diverse opzioni di cui discuterò in seguito, ma non ho ancora trovato una soluzione completa valida. Una soluzione completa potrebbe richiedere alcune modifiche a IPython o potrebbe basarsi su alcuni semplici script esterni. Attualmente utilizzo mercurial , ma vorrei una soluzione che funzionasse anche con git : una soluzione ideale sarebbe agnostica per il 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 installata.

Aggiornamento: Ho giocato con la mia versione per notebook modificata che facoltativamente salva una versione .clean con ogni salvataggio usando i suggerimenti di Gregory Crosswhite . Questo soddisfa la maggior parte dei miei vincoli ma lascia i seguenti non risolti:

  1. Questa non è ancora una soluzione standard (richiede una modifica del sorgente ipython. C'è un modo per ottenere questo comportamento con una semplice estensione? Richiede una sorta di hook di salvataggio.
  2. Un problema che ho con il flusso di lavoro corrente è quello di apportare modifiche. Questi entreranno nel file .clean e quindi dovranno essere integrati in qualche modo nella mia versione funzionante. (Naturalmente, riesco sempre a rieseguire il notebook, ma questo può essere un problema, specialmente se alcuni risultati dipendono da lunghi calcoli, calcoli paralleli, ecc.) Non ho una buona idea su come risolvere questo problema . Forse un flusso di lavoro che coinvolge un'estensione come ipycache potrebbe funzionare, ma sembra un po 'troppo complicato.

Gli appunti

Rimozione (rimozione) Uscita

  • Quando il notebook è in esecuzione, è possibile utilizzare l'opzione di menu Cell/All Output/Clear 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 risultato dell'utilizzo dell'interfaccia del notebook. Questo è stato infine incluso nel ipython/nbconvert , ma questo è 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 fattibile se può essere agganciato correttamente. (Collegarlo ad ogni sistema di controllo di versione, comunque , non sembra una buona idea - questo dovrebbe in qualche modo collegarsi al meccanismo del notebook.)

Newsgroup

Problemi

Pull Requests




Ecco una nuova soluzione di Cyrille Rossant per IPython 3.0, che persiste nei file markdown piuttosto che nei file ipymd basati su json:

https://github.com/rossant/ipymd




Come sottolineato, il --script è deprecato in 3.x Questo approccio può essere utilizzato applicando un post-salvataggio. In particolare, aggiungi 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 .




Sfortunatamente, non conosco molto su Mercurial, ma posso darti una possibile soluzione che funzioni 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 comando add memorizza le modifiche che sono state apportate a un file in un'area di gestione temporanea. Una volta fatto questo, eventuali modifiche successive al file vengono ignorate da Git a meno che tu non dica di metterle in scena. Quindi, il seguente script, che, per ciascuno dei file dati, rimuove tutte le prompt_number sections outputs e prompt_number sections , mette in scena il file stripped e ripristina l'originale:

NOTA: se si esegue questa operazione si ottiene un messaggio di errore come ImportError: No module named IPython.nbformat , quindi utilizzare ipython per 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 che lo script è stato eseguito sui file di cui si desidera eseguire il commit, è sufficiente eseguire git commit .




Dopo aver scavato, ho finalmente trovato questo aggancio di pre-salvataggio relativamente semplice sui documenti Jupyter . Toglie i dati di output della cella. Devi incollarlo nel file jupyter_notebook_config.py (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 file jupyter_notebook_config.py , puoi digitare jupyter --config-dir [nel prompt dei comandi / terminale], e se non trovi il file lì, puoi crearlo digitando jupyter notebook --generate-config .




Ok, quindi sembra che la migliore soluzione attuale, come in una discussione nbstripout.py , è quella di creare un filtro git per rimuovere automaticamente l'output dai file ipynb sul commit.

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

Ho modificato leggermente il file nbstripout di cfriedline per dare un errore informativo quando non riesci a importare l'ultimo IPython: https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output e lo ho aggiunto al mio repository, lascia dillo in ./relative/path/to/strip_notebook_output

Inoltre ha aggiunto il file .gitattributes alla radice del repository, contenente:

*.ipynb filter=stripoutput

E ha creato un setup_git_filters.sh contenente

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 ha eseguito source setup_git_filters.sh . La fantasia $ (git rev-parse ...) è trovare il percorso locale del repository su qualsiasi macchina (Unix).




Che ne dici dell'idea discussa nel post qui sotto, dove dovrebbe essere mantenuto l'output del notebook, con l'argomento che potrebbe richiedere molto tempo per generarlo, ed è utile dal momento che GitHub può ora rendere i notebook. Sono stati aggiunti ganci di salvataggio automatico per esportare il file .py, utilizzato per diffs e .html per la condivisione con membri del team che non utilizzano notebook o git.

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




Related