Uso de portátiles IPython bajo control de versiones

569

¿Cuál es una buena estrategia para mantener las notebooks IPython bajo control de versiones?

El formato de la notebook es bastante adecuado para el control de versiones: si uno quiere controlar la versión de la notebook y las salidas, entonces esto funciona bastante bien. La molestia se produce cuando uno solo quiere controlar la entrada de la versión, excluyendo las salidas de celda (también conocidas como "productos de compilación") que pueden ser grandes bloques binarios, especialmente para películas y tramas. En particular, estoy tratando de encontrar un buen flujo de trabajo que:

  • me permite elegir entre incluir o excluir resultados,
  • me impide comprometer la salida accidentalmente si no la quiero,
  • me permite mantener la salida en mi versión local,
  • me permite ver cuándo tengo cambios en las entradas usando mi sistema de control de versiones (es decir, si solo controlo las entradas de las versiones pero mi archivo local tiene salidas, entonces me gustaría poder ver si las entradas han cambiado (lo que requiere una confirmación El uso del comando de control de versión siempre registrará una diferencia, ya que el archivo local tiene salidas).
  • me permite actualizar mi cuaderno de trabajo (que contiene la salida) desde un cuaderno limpio actualizado. (actualizar)

Como se mencionó, si elegí incluir las salidas (lo cual es deseable cuando uso nbviewer, por ejemplo), entonces todo está bien. El problema es cuando no quiero controlar la versión de la salida. Hay algunas herramientas y scripts para eliminar la salida del cuaderno, pero con frecuencia encuentro los siguientes problemas:

  1. Accidentalmente confirmo una versión con la salida, contaminando así mi repositorio.
  2. Borro la salida para usar el control de versiones, pero realmente prefiero mantener la salida en mi copia local (a veces lleva un tiempo reproducirla, por ejemplo).
  3. Algunas de las secuencias de comandos que eliminan la salida cambian el formato ligeramente en comparación con la Cell/All Output/Clearopción del menú, creando así un ruido no deseado en las diferencias. Esto se resuelve con algunas de las respuestas.
  4. Al realizar cambios en una versión limpia del archivo, necesito encontrar alguna forma de incorporar esos cambios en mi cuaderno de trabajo sin tener que volver a ejecutar todo. (actualizar)

He considerado varias opciones que analizaré a continuación, pero aún no he encontrado una buena solución integral. Una solución completa puede requerir algunos cambios en IPython, o puede depender de algunos scripts externos simples. Actualmente uso mercurial , pero me gustaría una solución que también funcione con git : una solución ideal sería agnóstico de control de versiones.

Este problema se ha discutido muchas veces, pero no existe una solución definitiva o clara desde la perspectiva del usuario. La respuesta a esta pregunta debería proporcionar la estrategia definitiva. Está bien si requiere una versión reciente (incluso de desarrollo) de IPython o una extensión fácil de instalar.

Actualización: He estado jugando con mi versión de cuaderno modificada que opcionalmente guarda una .cleanversión con cada guardado usando las sugerencias de Gregory Crosswhite . Esto satisface la mayoría de mis limitaciones pero deja lo siguiente sin resolver:

  1. Esta todavía no es una solución estándar (requiere una modificación de la fuente de ipython. ¿Hay alguna forma de lograr este comportamiento con una extensión simple? Necesita algún tipo de enlace de guardado.
  2. Un problema que tengo con el flujo de trabajo actual es sacar los cambios. Estos entrarán en el .cleanarchivo y luego deberán integrarse de alguna manera en mi versión de trabajo. (Por supuesto, siempre puedo volver a ejecutar el cuaderno, pero esto puede ser un problema, especialmente si algunos de los resultados dependen de cálculos largos, cálculos paralelos, etc.) Todavía no tengo una buena idea sobre cómo resolver esto. . Quizás un flujo de trabajo que implique una extensión como ipycache podría funcionar, pero eso parece un poco demasiado complicado.

Notas

Eliminar (pelar) la salida

  • Cuando la computadora portátil se está ejecutando, se puede usar la Cell/All Output/Clearopción de menú para eliminar la salida.
  • Hay algunas secuencias de comandos para eliminar la salida, como la secuencia de comandos nbstripout.py que elimina la salida, pero no produce la misma salida que con la interfaz de la notebook. Esto finalmente se incluyó en el repositorio de ipython / nbconvert , pero se cerró indicando que los cambios ahora se incluyen en ipython / ipython , pero la funcionalidad correspondiente parece no haberse incluido todavía. (actualización) Dicho esto, la solución de Gregory Crosswhite muestra que esto es bastante fácil de hacer, incluso sin invocar ipython / nbconvert, por lo que este enfoque probablemente sea viable si se puede conectar correctamente. (Sin embargo, adjuntarlo a cada sistema de control de versiones no parece una buena idea, esto de alguna manera debería engancharse en el mecanismo del portátil).

Grupos de noticias

Cuestiones

Solicitudes de extracción

mforbes
fuente
Suena genial agregarlo como un problema en github.com/ipython/ipython o enviar una solicitud de extracción que lo ayude a alcanzar este objetivo.
Kyle Kelley
44
Una vez que tenga un script de trabajo para eliminar la salida, puede usar un filtro "limpio" de Git para aplicarlo automáticamente antes de confirmar (ver filtros de limpieza / borrones).
Matthias
1
@foobarbecue La pregunta contiene soluciones alternativas insatisfactorias: cada una tiene al menos una limitación. Ahora que se ha fusionado el PR 4175, probablemente se pueda formular una solución completa, pero esto aún debe hacerse. Tan pronto como tenga algo de tiempo, lo haré (como respuesta) si alguien más no proporciona una solución satisfactoria mientras tanto.
mforbes
1
@saroele Todavía no he encontrado una solución recomendada: iba a ir con la --scriptopción, pero se ha eliminado. Estoy esperando hasta que se implementen los ganchos posteriores al guardado ( que están planificados ), momento en el que creo que podré proporcionar una solución aceptable que combine varias de las técnicas.
mforbes
1
@mforbes Parece que las relaciones públicas se fusionaron unos días después de tu comentario. ¿Podría usted o alguien más informado que yo publicar aquí una respuesta que muestre cómo usar la nueva función?
KobeJohn

Respuestas:

124

Aquí está mi solución con git. Le permite simplemente agregar y confirmar (y diff) como de costumbre: esas operaciones no alterarán su árbol de trabajo, y al mismo tiempo (re) ejecutar un cuaderno no alterará su historial de git.

Aunque esto probablemente se pueda adaptar a otros VCS, sé que no satisface sus requisitos (al menos la agnosticidad de VSC). Aún así, es perfecto para mí, y aunque no es nada particularmente brillante, y muchas personas probablemente ya lo usan, no encontré instrucciones claras sobre cómo implementarlo buscando en Google. Por lo tanto, puede ser útil para otras personas.

  1. Guarde un archivo con este contenido en algún lugar (para lo siguiente, supongamos ~/bin/ipynb_output_filter.py)
  2. Hazlo ejecutable ( chmod +x ~/bin/ipynb_output_filter.py)
  3. Crea el archivo ~/.gitattributescon el siguiente contenido

    *.ipynb    filter=dropoutput_ipynb
    
  4. Ejecute los siguientes comandos:

    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
    

¡Hecho!

Limitaciones:

  • funciona solo con git
  • en git, si está en una rama somebranchy lo hace git checkout otherbranch; git checkout somebranch, generalmente espera que el árbol de trabajo no cambie. Aquí, en cambio, habrá perdido la salida y la numeración de las celdas de los cuadernos cuya fuente difiere entre las dos ramas.
  • más en general, la salida no está versionada en absoluto, como con la solución de Gregory. Con el fin de no tirarlo cada vez que haga algo que implique un proceso de pago, el enfoque podría cambiarse almacenándolo en archivos separados (¡pero tenga en cuenta que en el momento en que se ejecuta el código anterior, no se conoce el ID de confirmación!), y posiblemente versionarlos (pero git commit notebook_file.ipynbtenga en cuenta que esto requeriría algo más que un , aunque al menos se mantendría git diff notebook_file.ipynblibre de basura base64).
  • Dicho esto, dicho sea de paso, si utiliza un código de extracción (es decir, cometido por otra persona que no utiliza este enfoque) que contiene alguna salida, la salida se desprotege normalmente. Solo se pierde la producción producida localmente.

Mi solución refleja el hecho de que personalmente no me gusta mantener las cosas generadas versionadas; tenga en cuenta que hacer fusiones que involucren la salida casi seguramente invalidará la salida o su productividad o ambas.

EDITAR:

  • si adopta la solución como la sugerí, es decir, a nivel mundial, tendrá problemas en caso de que haya algún repositorio de git que desee que muestre . Entonces, si desea deshabilitar el filtrado de salida para un repositorio git específico, simplemente cree dentro de él un archivo .git / info / atributos , con

    **. filtro ipynb =

como contenido Claramente, de la misma manera es posible hacer lo contrario: habilitar el filtrado solo para un repositorio específico.

  • el código ahora se mantiene en su propio repositorio git

  • si las instrucciones anteriores resultan en ImportErrors, intente agregar "ipython" antes de la ruta del script:

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

EDITAR : mayo de 2016 (actualizado en febrero de 2017): hay varias alternativas a mi script: para completar, aquí hay una lista de las que conozco: nbstripout ( otras variantes ), nbstrip , jq .

Pietro Battiston
fuente
2
¿Cómo abordas el problema de incorporar los cambios que realizas? ¿Simplemente vive con tener que regenerar toda la salida? (Creo que esta es una manifestación de su segunda limitación.)
mforbes
1
@zhermes: esta versión extendida debería estar bien
Pietro Battiston
1
¿Hay alguna manera de usar este método de filtros git con una herramienta de diferencia externa? El filtro se aplica si uso la herramienta de línea de comando normal pero no si estoy usando meld como herramienta de diferencia. stackoverflow.com/q/30329615/578770
FA
1
Para evitar tener ImportErrorque alterar lo anterior para ejecutar usando ipython:git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
chris838
1
Impresionante solución Pietro, gracias :) Cambié 2 cosas al usar su script en mi caso: 1) Preferí declarar el filtro en .gitattributes en la raíz del repositorio en lugar de ~/.gitattributes, otras personas tienen los mismos filtros que yo 2 ) workdir/**/*.ipynb filter=dropoutput_ipynbDefiní la expresión regular como , y puse la mayoría de mis cuadernos en workdir / => si todavía quiero empujar un cuaderno con la salida y disfrutar del renderizado marcado en github, simplemente lo puse fuera de esa carpeta.
Svend
63

Tenemos un proyecto de colaboración en el que el producto es Jupyter Notebooks, y hemos utilizado un enfoque durante los últimos seis meses que funciona muy bien: activamos guardar los .pyarchivos automáticamente y rastreamos tanto los .ipynbarchivos como los .pyarchivos.

De esa manera, si alguien quiere ver / descargar la última libreta, puede hacerlo a través de github o nbviewer, y si alguien quiere ver cómo ha cambiado el código de la libreta, simplemente puede ver los cambios en los .pyarchivos.

Para los Jupyterservidores portátiles , esto se puede lograr agregando las líneas

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.pyarchivo y reiniciando el servidor portátil.

Si no está seguro de en qué directorio encontrar su jupyter_notebook_config.pyarchivo, puede escribir jupyter --config-dir, y si no encuentra el archivo allí, puede crearlo escribiendo jupyter notebook --generate-config.

Para los Ipython 3servidores portátiles , esto se puede lograr agregando las líneas

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.pyarchivo y reiniciando el servidor portátil. Estas líneas provienen de una respuesta de problemas de github que @minrk proporcionó y @dror también las incluye en su respuesta SO.

Para los Ipython 2servidores portátiles , esto se puede lograr iniciando el servidor usando:

ipython notebook --script

o agregando la línea

c.FileNotebookManager.save_script = True

al ipython_notebook_config.pyarchivo y reiniciando el servidor portátil.

Si no está seguro de en qué directorio encontrar su ipython_notebook_config.pyarchivo, puede escribir ipython locate profile default, y si no encuentra el archivo allí, puede crearlo escribiendo ipython profile create.

Aquí está nuestro proyecto en github que utiliza este enfoque : y aquí hay un ejemplo de github de explorar los cambios recientes en un cuaderno .

Estamos muy contentos con esto.

Rich Signell
fuente
1
Gracias por la evidencia adicional de que el uso --scriptha funcionado en la práctica. El problema con esto es que los portátiles reales pueden ser enormes si se mantienen las imágenes. Una solución ideal en este camino podría usar algo como git-annex para hacer un seguimiento de la última computadora portátil completa.
mforbes
En Ipython 3.x, --scriptestá en desuso. ipython.org/ipython-doc/3/whatsnew/version3.html
Dror
Gracias @dror, he actualizado mi respuesta para proporcionar la solución ipython 3.x de minrk como también proporcionaste aquí.
Rich Signell
10
Actualización: Esta solución está rota en iPython versión 4, debido a "The Big Split" de Jupyter de iPython. Para ajustar esta solución a la versión 4, use el comando jupyter notebook --generate-configpara crear un archivo de configuración. El comando jupyter --config-dirdescubre qué directorio contiene los archivos de configuración. Y el fragmento de código proporcionado por @Rich debe agregarse al archivo nombrado jupyter_notebook_config.py. El resto funciona como antes.
mobius dumpling
2
Además del punto por @mobiusdumpling, reemplace check_call(['ipython'con check_call(['jupyter', de lo contrario, recibirá una advertencia que ipython nbconvertestá en desuso y debe usar jupyter nbconverten su lugar. (Jupyter v4.1.0, iPython v4.1.2)
cutculus
36

He creado nbstripout, basado en MinRKs gist , que admite Git y Mercurial (gracias a mforbes). Está destinado a ser utilizado de forma independiente en la línea de comandos o como filtro, que se instala (des) fácilmente en el repositorio actual a través de nbstripout install/ nbstripout uninstall.

Obténgalo de PyPI o simplemente

pip install nbstripout
kynan
fuente
Estoy considerando un flujo de trabajo donde mantengo tanto .ipynb como .py correspondiente creados automáticamente usando los ganchos posteriores al guardado descritos anteriormente. Me gustaría usar .py para diffs: ¿nbstripout podría borrar el archivo .py de los contadores de ejecución de celda (# In [1] cambiado a In [*]), para que no saturen los diffs o debería hacerlo? crear un script simple para hacer eso?
Krzysztof Słowiński
1
@ KrzysztofSłowiński No, nbstripoutno admite este caso de uso fácilmente, ya que se basa en el formato JSON de Notebook. Probablemente sea mejor escribir un script especializado para su caso de uso.
kynan
13

Aquí hay una nueva solución de Cyrille Rossant para IPython 3.0, que persiste en rebajar archivos en lugar de archivos ipymd basados ​​en json:

https://github.com/rossant/ipymd

Spencer Boucher
fuente
No es compatible con Jupyter todavía, parece.
K.-Michael Aye
Estoy usando ipymd con éxito con el último Jupyter: ¿recibe algún problema específico o mensaje de error?
Cyrille Rossant
13

Después de unos años de eliminar los resultados en los portátiles, he intentado encontrar una solución mejor. Ahora uso Jupytext , una extensión para Jupyter Notebook y Jupyter Lab que he diseñado.

Jupytext puede convertir los cuadernos Jupyter a varios formatos de texto (Scripts, Markdown y R Markdown). Y por el contrario. También ofrece la opción de emparejar un cuaderno a uno de estos formatos, y para sincronizar automáticamente las dos representaciones de la portátil (una .ipynby una .md/.py/.Rarchivo).

Permítanme explicar cómo Jupytext responde las preguntas anteriores:

me permite elegir entre incluir o excluir resultados,

El .md/.py/.Rarchivo solo contiene las celdas de entrada. Siempre debe rastrear este archivo. Versione el .ipynbarchivo solo si desea rastrear las salidas.

me impide comprometer la salida accidentalmente si no la quiero,

Añadir *.ipynba.gitignore

me permite mantener la salida en mi versión local,

Las salidas se conservan en el .ipynbarchivo (local)

me permite ver cuándo tengo cambios en las entradas usando mi sistema de control de versiones (es decir, si solo controlo las entradas de las versiones pero mi archivo local tiene salidas, entonces me gustaría poder ver si las entradas han cambiado (lo que requiere una confirmación El uso del comando de control de versión siempre registrará una diferencia, ya que el archivo local tiene salidas).

La diferencia en el archivo .py/.Ro .mdes lo que está buscando.

me permite actualizar mi cuaderno de trabajo (que contiene la salida) desde un cuaderno limpio actualizado. (actualizar)

Obtenga la última revisión del archivo .py/.Ro .mdy actualice su computadora portátil en Jupyter (Ctrl + R). Obtendrá las últimas celdas de entrada del archivo de texto, con salidas coincidentes del .ipynbarchivo. El kernel no se ve afectado, lo que significa que sus variables locales se conservan; puede continuar trabajando donde lo dejó.

Lo que me encanta de Jupytext es que el cuaderno (en forma de archivo .py/.Ro .mdarchivo) se puede editar en su IDE favorito. Con este enfoque, refactorizar una computadora portátil se vuelve fácil. Una vez que haya terminado, solo necesita actualizar el cuaderno en Jupyter.

Si desea probarlo: instale Jupytext con pip install jupytexty reinicie su editor Jupyter Notebook o Lab. Abrir el bloc de notas que desea el control de versiones, y sincronizarlo a un archivo de rebajas (o una secuencia de comandos) utilizando el menú Jupytext en Jupyter portátil (o los comandos Jupytext en Jupyter Lab). Guarde su computadora portátil y obtendrá los dos archivos: el original .ipynb, más la representación de texto prometida de la computadora portátil, ¡que es un ajuste perfecto para el control de versiones!

Para aquellos que puedan estar interesados: Jupytext también está disponible en la línea de comando .

Marc Wouts
fuente
13

Actualización : ahora puede editar archivos Jupyter Notebook directamente en Visual Studio Code. Puede elegir editar el cuaderno o el archivo python convertido.

Finalmente encontré una manera productiva y simple de hacer que Jupyter y Git jueguen bien juntos. Todavía estoy en los primeros pasos, pero ya creo que es mucho mejor que todas las otras soluciones complicadas.

Visual Studio Code es un editor de código fuente genial y abierto de Microsoft. Tiene una excelente extensión de Python que ahora le permite importar un Jupyter Notebook como código de Python. Ahora también puede editar directamente los cuadernos Jupyter .

Después de importar su computadora portátil a un archivo de Python, todo el código y las rebajas estarán juntas en un archivo de Python normal, con marcadores especiales en los comentarios. Puedes ver en la imagen a continuación:

Editor de VSCode con un cuaderno convertido a python

Su archivo de Python solo tiene el contenido de las celdas de entrada del cuaderno. La salida se generará en una ventana dividida. Tiene código puro en el cuaderno, no cambia mientras lo ejecuta. No hay salida mezclada con su código. Ningún extraño formato JSON incomprensible para analizar sus diferencias.

Solo código python puro donde puede identificar fácilmente cada diferencia.

Ya ni siquiera necesito versionar mis .ipynbarchivos. Puedo poner una *.ipynblínea adentro .gitignore.

¿Necesita generar un cuaderno para publicar o compartir con alguien? No hay problema, simplemente haga clic en el botón de exportación en la ventana interactiva de Python

Exportar un archivo python a formato Notebook

Si está editando el cuaderno directamente, ahora hay un icono Convert and save to a python script. Iconos de Jupyter en Visual Studio Code

Aquí una captura de pantalla de un cuaderno dentro de Visual Studio Code:

Edición del cuaderno dentro de VSCode

Lo he estado usando solo por un día, pero finalmente puedo usar Jupyter con Git.

PD: la finalización del código VSCode es mucho mejor que Jupyter.

neves
fuente
12

(2017-02)

estrategias

  • on_commit ():
    • pele la salida> nombre.ipynb ( nbstripout,)
    • pele la salida> nombre.clean.ipynb ( nbstripout,)
    • siempre nbconverta python: name.ipynb.py ( nbconvert)
    • siempre convierta a markdown: name.ipynb.md ( nbconvert, ipymd)
  • vcs.configure ():
    • git difftool, mergetool: nbdiff y nbmerge de nbdime

herramientas

Wes Turner
fuente
11

Las muy populares respuestas de 2016 anteriores son hacks inconsistentes en comparación con la mejor manera de hacerlo en 2019.

Existen varias opciones, la mejor que responde a la pregunta es Jupytext.

Jupytext

Coger el artículo hacia la ciencia de datos en Jupytext

La forma en que funciona con el control de versiones es poner los archivos .py y .ipynb en el control de versiones. Mire el .py si desea la entrada diff, mire el .ipynb si desea la última salida renderizada.

Menciones notables: VS studio, nbconvert, nbdime, hidrógeno

Creo que con un poco más de trabajo, VS studio y / o hidrógeno (o similar) se convertirán en los actores dominantes en la solución de este flujo de trabajo.

SwimBikeRun
fuente
9

Simplemente encuentre "jupytext" que parece una solución perfecta. Genera un archivo .py desde el cuaderno y luego mantiene ambos sincronizados. Puede controlar las versiones, diferenciar y combinar entradas a través del archivo .py sin perder las salidas. Cuando abre el cuaderno, utiliza el .py para las celdas de entrada y el .ipynb para la salida. Y si desea incluir la salida en git, puede agregar el ipynb.

https://github.com/mwouts/jupytext

Simón
fuente
9

Dado que existen tantas estrategias y herramientas para manejar el control de versiones de las notebooks, traté de crear un diagrama de flujo para elegir una estrategia adecuada (creado en abril de 2019)

Flujo de decisiones para elegir la estrategia de control de versiones

nik
fuente
8

Como se señaló, el --scriptestá en desuso en 3.x. Este enfoque se puede utilizar aplicando un gancho posterior al guardado. En particular, agregue lo siguiente 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

El código está tomado del # 8009 .

Dror
fuente
Gracias por demostrar el uso de un gancho posterior al guardado. Desafortunadamente, como se mencionó anteriormente, volver del .pyarchivo a un cuaderno es problemático, por lo que desafortunadamente no es una solución completa. (Me gustaría que fuera así, ya que es muy agradable diferir .pyarchivos en lugar de cuadernos. Quizás la nueva función de diferencias de cuaderno será útil.
mforbes
1
¡Gracias! Ahora estoy usando este truco para reproducir el --scriptcomportamiento, independientemente del control de versión. Al principio tuve algunos problemas, así que en caso de que pueda salvar a alguien en algún momento: 1) Si ipython_notebook_config.pyfalta en la carpeta de perfil, ejecute ipython profile createpara generarlo. 2) Si parece que se ignora el post-save-hook, ejecute ipython con --debugpara diagnosticar el problema. 3) Si el script falla con el error ImportError: No module named mistune- sencillo instalar minstue: pip install mistune.
Joe
7

Desafortunadamente, no sé mucho sobre Mercurial, pero puedo darle una posible solución que funcione con Git, con la esperanza de que pueda traducir mis comandos de Git a sus equivalentes de Mercurial.

Para el fondo, en Git, el addcomando almacena los cambios que se han realizado en un archivo en un área de ensayo. Una vez que hayas hecho esto, Git ignorará cualquier cambio posterior en el archivo a menos que le digas que también lo haga. Por lo tanto, la siguiente secuencia de comandos, que, para cada uno de los archivos dados, elimina todos los outputsy prompt_number sections, organiza el archivo eliminado y luego restaura el original:

NOTA: Si ejecuta esto, obtiene un mensaje de error como ImportError: No module named IPython.nbformat, luego use ipythonpara ejecutar el script en lugar de 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 vez que la secuencia de comandos se haya ejecutado en los archivos cuyos cambios desea confirmar, simplemente ejecútelos git commit.

Gregory Crosswhite
fuente
Gracias por la sugerencia. Mercurial realmente no tiene un área de preparación como git (aunque uno podría usar colas mercuriales para este propósito). Mientras tanto, intenté agregar este código a un enlace de guardado que guarda una versión limpia con una .cleanextensión. Desafortunadamente, no pude ver cómo hacer esto sin modificar directamente IPython (aunque este cambio fue bastante trivial). Jugaré con esto por un tiempo y veré si satisface todas mis necesidades.
mforbes
6

Yo uso un enfoque muy pragmático; que funcionan bien para varios cuadernos, en varios lados. E incluso me permite 'transferir' cuadernos. Funciona tanto para Windows como Unix / MacOS.
Al pensó que es simple, es resolver los problemas anteriores ...

Concepto

Básicamente, no.ipnyb rastree los archivos -files, solo los .pyarchivos- correspondientes .
Al iniciar el servidor portátil con la --scriptopción, ese archivo se crea / guarda automáticamente cuando se guarda el portátil.

Esos .pyarchivos contienen toda la entrada; el no código se guarda en comentarios, al igual que los bordes de las celdas. Esos archivos se pueden leer / importar (y arrastrar) al servidor de la notebook para (re) crear una notebook. Solo la salida se ha ido; hasta que se vuelva a ejecutar.

Personalmente uso mercurial para rastrear la versión de los .pyarchivos; y use los comandos normales (línea de comandos) para agregar, registrar (ect) para eso. La mayoría de los otros (D) VCS lo permitirán.

Es simple rastrear la historia ahora; el .pyson pequeños, textual y fácil de diff. De vez en cuando, necesitamos un clon (solo bifurcación; inicie un segundo servidor portátil allí), o una versión anterior (compruébelo e impórtelo en un servidor portátil), etc.

Consejos y trucos

  • Agregue * .ipynb a ' .hgignore ', para que Mercurial sepa que puede ignorar esos archivos
  • Cree un script (bash) para iniciar el servidor (con la --scriptopción) y realice un seguimiento de versión
  • Guardar un cuaderno guarda el .pyarchivo, pero no lo registra.
    • Este es un inconveniente : se puede olvidar que
    • También es una característica : es posible guardar un cuaderno (y continuar más tarde) sin agrupar el historial del repositorio.

Deseos

  • Sería bueno tener botones para registrar / agregar / etc. en el Tablero del cuaderno
  • Un checkout para (por ejemplo) file@date+rev.py) debería ser útil. Sería mucho trabajo agregar eso; y tal vez lo haga una vez. Hasta ahora, solo lo hago a mano.
Albert
fuente
¿Cómo se pasa del .pyarchivo a un cuaderno? Me gusta este enfoque, pero debido a que .ipynb-> .py-> .ipynbes potencialmente con pérdidas, no lo consideré en serio.
mforbes
Eso es fácil: cárguelo, por ejemplo, dejándolo caer en el tablero del Notebook. A excepción de los "datos de salida", no se pierde nada
Albert
Si eso es cierto, entonces creo que esto estaría cerca de la idea, pero parece recordar que IPython no se comprometió a preservar completamente los datos en la transición de .pya .ipynbformatos. Hay un problema al respecto , por lo que quizás esto constituirá la base para una solución completa.
mforbes
Estoy teniendo dificultades para convertir .pyarchivos a .ipynbarchivos. nbconverttodavía no parece admitir esto, y no tengo un panel de control portátil porque lo ejecuto ipython notebookmanualmente. ¿Tiene alguna sugerencia general sobre cómo implementar esta conversión hacia atrás?
mforbes
Seguramente la .pytransformación de-a-notebook no está pensada para un viaje de ida y vuelta. Por lo tanto, esto no puede ser una solución general, aunque es bueno que funcione para usted.
holdenweb
3

Para seguir el excelente guión de Pietro Battiston, si obtiene un error de análisis Unicode como este:

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)

Puede agregar al comienzo del script:

reload(sys)
sys.setdefaultencoding('utf8')
Guillaume Dumas
fuente
3

He creado un paquete de Python que resuelve este problema.

https://github.com/brookisme/gitnb

Proporciona una CLI con una sintaxis inspirada en git para rastrear / actualizar / notebooks diff dentro de su repositorio git.

Heres 'un ejemplo

# 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"

Tenga en cuenta que el último paso, donde estoy usando "gitnb commit" es comprometerse con su repositorio de git. Es esencialmente un envoltorio para

# 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"

Hay varios métodos más, y se pueden configurar para que requieran más o menos aportes del usuario en cada etapa, pero esa es la idea general.

arroyo
fuente
3

Después de investigar, finalmente encontré este gancho de pre-guardado relativamente simple en los documentos de Jupyter . Despoja los datos de salida de la celda. Debe pegarlo en el jupyter_notebook_config.pyarchivo (consulte las instrucciones a continuación).

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

De la respuesta de Rich Signell :

Si no está seguro de en qué directorio encontrar su jupyter_notebook_config.pyarchivo, puede escribir jupyter --config-dir[en el símbolo del sistema / terminal], y si no encuentra el archivo allí, puede crearlo escribiendo jupyter notebook --generate-config.

binaryfunt
fuente
1
Me gustaría señalar que esta solución nunca guardaría ninguna salida en el disco, y es algo independiente del problema de control de versiones.
bdforbes
2

Hice lo que hicieron Albert y Rich: no versionen archivos .ipynb (ya que pueden contener imágenes, lo que se vuelve desordenado). En cambio, siempre ejecute ipython notebook --scripto coloque c.FileNotebookManager.save_script = Truesu archivo de configuración, de modo que .pysiempre se cree un archivo (versionable) cuando guarde su computadora portátil.

Para regenerar cuadernos (después de revisar un repositorio o cambiar una rama) puse el script py_file_to_notebooks.py en el directorio donde guardo mis cuadernos.

Ahora, después de revisar un repositorio, simplemente ejecute python py_file_to_notebooks.pypara generar los archivos ipynb. Después de cambiar de rama, es posible que deba ejecutar python py_file_to_notebooks.py -ovpara sobrescribir los archivos ipynb existentes.

Solo para estar seguro, también es bueno agregarlo *.ipynba su .gitignorearchivo.

Editar: ya no hago esto porque (A) tienes que regenerar tus cuadernos a partir de archivos py cada vez que pagas una rama y (B) hay otras cosas como rebajas en los cuadernos que pierdes. En cambio, elimino la salida de los portátiles con un filtro git. La discusión sobre cómo hacer esto está aquí .

Peter
fuente
Me gustó esta idea, pero después de las pruebas, descubrí que la conversión de .pyarchivos de nuevo a .ipynbes problemática, especialmente con los portátiles de la versión 4 para los que aún no hay un convertidor. En la actualidad, sería necesario usar el importador v3 y luego convertir a v4 y estoy un poco preocupado por este complicado viaje. Además, un .pyarchivo no es una muy buena opción si el cuaderno es principalmente código Julia. Finalmente, --scriptestá en desuso, así que creo que los ganchos son el camino a seguir.
mforbes
La solución de filtro git en su enlace es buena, debe copiar su respuesta desde aquí aquí :-)
mcarans
2

Ok, parece que la mejor solución actual, según una discusión aquí , es hacer un filtro git para eliminar automáticamente la salida de los archivos ipynb al confirmar.

Esto es lo que hice para que funcione (copiado de esa discusión):

Modifiqué ligeramente el archivo nbstripout de cfriedline para dar un error informativo cuando no puede importar el último IPython: https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output re, y lo agregué a my decir en./relative/path/to/strip_notebook_output

También se agregó el archivo .gitattributes a la raíz del repositorio, que contiene:

*.ipynb filter=stripoutput

Y creó un setup_git_filters.shcontenedor

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

Y corrió source setup_git_filters.sh. Lo elegante de $ (git rev-parse ...) es encontrar la ruta local de su repositorio en cualquier máquina (Unix).

Peter
fuente
1

Esta extensión jupyter permite a los usuarios empujar portátiles jupyter directamente a github.

Por favor mira aquí

https://github.com/sat28/githubcommit

se sentó
fuente
¿Puedes explicar qué hace esto? La doumentation no es especialmente clara.
Alex Monras
@AlexMonras Esto agregará directamente un botón en el cuaderno de Jupyter desde donde puede empujar los cuadernos a su repositorio de GitHub con un mensaje de confirmación
sábado
1

Esto es abril de 2020 y hay muchas estrategias y herramientas para el control de la versión del portátil Jupyter. Aquí hay una descripción rápida de todas las herramientas que puede usar,

  • nbdime - Agradable para la difusión y fusión local de cuadernos

  • nbstripout : un filtro git para eliminar automáticamente las salidas del cuaderno antes de cada confirmación

  • jupytext : mantiene un archivo complementario .py sincronizado con cada computadora portátil. Solo comprometes archivos .py

  • nbconvert - Convierte cuadernos a un script de python o HTML (o ambos) y confirma estos tipos de archivos alternativos

  • ReviewNB : muestra la diferencia del cuaderno (junto con la salida) para cualquier solicitud de confirmación o extracción en GitHub. También se pueden escribir comentarios en las celdas del cuaderno para discutir los cambios (captura de pantalla a continuación).

ingrese la descripción de la imagen aquí

Descargo de responsabilidad: construí ReviewNB.

amirathi
fuente
0

¿Qué tal la idea discutida en la publicación a continuación, donde se debe mantener la salida del cuaderno, con el argumento de que podría tomar mucho tiempo generarlo, y es útil ya que GitHub ahora puede renderizar cuadernos. Se agregaron ganchos de guardado automático para exportar archivos .py, utilizados para diffs y .html para compartir con miembros del equipo que no usan cuadernos o git.

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

Krzysztof Słowiński
fuente