在版本控制下使用IPython笔记本

mfo*_*bes 548 version-control ipython jupyter-notebook

IPython笔记本电脑置于版本控制之下的好策略是什么?

笔记本格式非常适合版本控制:如果想要版本控制笔记本和输出,那么这非常有效.当人们只想对输入进行版本控制时,就会产生烦恼,不包括可能是大型二进制blob的单元格输出(也就是"构建产品"),特别是对于电影和情节.特别是,我试图找到一个良好的工作流程:

  • 允许我选择包括或排除输出,
  • 如果我不想要它会阻止我意外地提交输出,
  • 允许我保持我的本地版本的输出,
  • 允许我看看当我使用我的版本控制系统更改输入时(即如果我只对版本控制输入但我的本地文件有输出,那么我希望能够看到输入是否已更改(需要提交) ).使用版本控制状态命令将始终注册差异,因为本地文件有输出.)
  • 允许我从更新的干净笔记本更新我的工作笔记本(包含输出).(更新)

如上所述,如果我选择包含输出(例如,在使用nbviewer时这是可取的),那么一切都很好.问题是,当我想要的版本控制输出.有一些工具和脚本可以剥离笔记本的输出,但我经常会遇到以下问题:

  1. 我不小心提交了一个带有输出的版本,从而污染了我的存储库.
  2. 我清除输出以使用版本控制,但实际上宁愿将输出保留在我的本地副本中(例如,有时需要一段时间来重现).
  3. Cell/All Output/Clear菜单选项相比,剥离输出的一些脚本会稍微改变格式,从而在差异中产生不必要的噪声.这可以通过一些答案来解决.
  4. 当将更改提取到文件的干净版本时,我需要找到一些方法将这些更改合并到我的工作笔记本中而无需重新运行所有内容. (更新)

我已经考虑过几个选项,我将在下面讨论,但还没有找到一个很好的综合解决方案.完整的解决方案可能需要对IPython进行一些更改,或者可能依赖于一些简单的外部脚本.我目前使用mercurial,但想要一个也适用于git的解决方案:理想的解决方案是版本控制不可知.

这个问题已经多次讨论过,但从用户的角度来看,没有明确或明确的解决方案.这个问题的答案应该提供明确的策略.如果它需要最近的(甚至开发)版本的IPython或一个易于安装的扩展,这很好.

更新:我一直在玩我修改过的笔记本版本,可以选择.clean使用Gregory Crosswhite的建议保存每次保存的版本.这满足了我的大多数约束,但是仍然没有解决以下问题:

  1. 这还不是一个标准的解决方案(需要修改ipython源.有没有办法通过简单的扩展来实现这种行为?需要某种on-save钩子.
  2. 我对当前工作流程的一个问题是拉动变化.这些将进入.clean文件,然后需要以某种方式集成到我的工作版本中.(当然,我总是可以重新执行笔记本,但这可能会很痛苦,特别是如果某些结果取决于长时间的计算,并行计算等).我还不知道如何解决这个问题.也许涉及像ipycache这样的扩展的工作流可能会起作用,但这似乎有点过于复杂.

笔记

删除(剥离)输出

  • 笔记本电脑运行时,可以使用Cell/All Output/Clear菜单选项删除输出.
  • 有一些用于删除输出的脚本,例如删除输出的脚本nbstripout.py,但不会产生与使用笔记本界面相同的输出.这最终包含在ipython/nbconvert repo中,但是已经关闭,说明这些更改现在包含在ipython/ipython中,但相应的功能似乎还没有包含在内. (更新)话虽如此,Gregory Crosswhite的解决方案显示这很容易做到,即使没有调用ipython/nbconvert因此,如果可以正确地连接它,这种方法可能是可行的.(然而,将它附加到每个版本控制系统似乎不是一个好主意 - 这应该以某种方式挂钩到笔记本机制.)

新闻组

问题

请求

Pie*_*ton 117

这是我用git的解决方案.它允许您像往常一样添加和提交(和差异):这些操作不会改变您的工作树,同时(重新)运行笔记本不会改变您的git历史记录.

虽然这可能适用于其他VCS,但我知道它不能满足您的要求(至少VSC不可知).尽管如此,它对我来说仍然是完美的,虽然没有什么特别精彩,很多人可能已经使用过它,但我没有找到关于如何通过Google搜索来实现它的明确指示.所以它可能对其他人有用.

  1. 在某个地方保存包含此内容的文件(对于以下内容,我们假设~/bin/ipynb_output_filter.py)
  2. 让它可执行(chmod +x ~/bin/ipynb_output_filter.py)
  3. ~/.gitattributes使用以下内容创建文件

    *.ipynb    filter=dropoutput_ipynb
    
    Run Code Online (Sandbox Code Playgroud)
  4. 运行以下命令:

    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
    
    Run Code Online (Sandbox Code Playgroud)

完成!

限制:

  • 它只适用于git
  • 在git中,如果你在分支中somebranch,那么git checkout otherbranch; git checkout somebranch你通常希望工作树不变.相反,您将丢失两个分支之间源不同的笔记本的输出和单元格编号.
  • 更一般地说,输出完全没有版本化,就像Gregory的解决方案一样.为了不是每次你做任何涉及结账的事情都扔掉它,可以通过将它存储在单独的文件中来改变方法(但请注意,在运行上面的代码时,提交id是未知的!),并且可能对它们进行版本控制(但请注意,这将需要的不仅仅是一个git commit notebook_file.ipynb,尽管它至少可以避免git diff notebook_file.ipynb使用base64垃圾).
  • 顺便说一下,如果你确实拉代码(即由不使用这种方法的其他人提交)包含一些输出,则输出会正常检出.只丢失本地产生的输出.

我的解决方案反映了这样一个事实,即我个人不喜欢将生成的内容保留为版本 - 请注意,涉及输出的合并几乎可以保证输出生产力无效两者兼而有之.

编辑:

  • 如果您按照我的建议采用了解决方案 - 也就是说,全局 - 您将遇到麻烦,以防某些git repo您希望版本输出.因此,如果要禁用特定git存储库的输出过滤,只需在其中创建一个.git/info/attributes文件,

    **.ipynb filter =

作为内容.显然,以相同的方式可以执行相反的操作:针对特定存储库启用过滤.

编辑:2016年5月(2017年2月更新):我的脚本有几种替代方案 - 为了完整性,这里列出了我所知道的:nbstripout(其他 变种),nbstrip,jq.

  • 您如何处理合并变更的问题?您是否只需要重新生成所有输出?(我认为这是你第二个限制的表现.) (2认同)

Ric*_*ell 58

我们有一个合作项目,产品是Jupyter笔记本电脑,我们在过去的六个月中使用了一种方法很有效:我们激活.py自动保存文件并跟踪.ipynb文件和.py文件.

这样,如果有人想查看/下载最新的笔记本,他们可以通过github或nbviewer这样做,如果有人想看看笔记本代码是如何变化的,他们可以只查看.py文件的变化.

对于Jupyter笔记本电脑服务器,可以通过添加线路来实现

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
Run Code Online (Sandbox Code Playgroud)

jupyter_notebook_config.py文件并重新启动笔记本服务器.

如果您不确定在哪个目录中查找jupyter_notebook_config.py文件,可以键入jupyter --config-dir,如果在那里找不到该文件,可以通过键入来创建jupyter notebook --generate-config.

对于Ipython 3笔记本电脑服务器,可以通过添加线路来实现

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
Run Code Online (Sandbox Code Playgroud)

ipython_notebook_config.py文件并重新启动笔记本服务器.这些行是来自github问题的答案@minrk提供,而@dror也包含在他的SO答案中.

对于Ipython 2笔记本服务器,可以通过使用以下命令启动服务器来完成:

ipython notebook --script
Run Code Online (Sandbox Code Playgroud)

或者添加该行

c.FileNotebookManager.save_script = True
Run Code Online (Sandbox Code Playgroud)

ipython_notebook_config.py文件并重新启动笔记本服务器.

如果您不确定在哪个目录中查找ipython_notebook_config.py文件,可以键入ipython locate profile default,如果在那里找不到该文件,可以通过键入来创建ipython profile create.

这是我们在github上使用这种方法的项目:这里是一个探索笔记本最近更改github示例.

我们对此非常满意.

  • **更新:**这个解决方案在iPython版本4中被破解,因为来自iPython的Jupyter的"大分裂".要将此解决方案调整为版本4,请使用命令`jupyter notebook --generate-config`来创建配置文件.命令`jupyter --config-dir`找出包含配置文件的目录.并且@Rich给出的代码片段应该添加到名为`jupyter_notebook_config.py`的文件中.其余的工作和以前一样. (10认同)
  • 除了@mobiusdumpling的要点外,还应将`check_call(['ipython'`替换为`check_call(['jupyter'`,否则,您将收到警告,提示不建议使用ipython nbconvert`,而应使用`jupyter nbconvert`。相反(Jupyter v4.1.0,iPython v4.1.2) (2认同)

kyn*_*nan 36

我创建了nbstripout基于MinRKs的gist,它支持Git和Mercurial(感谢mforbes).它既可以在命令行上单独使用,也可以作为过滤器使用,可以通过nbstripout install/ 轻松(非)安装在当前存储库中nbstripout uninstall.

PyPI或简单地获取它

pip install nbstripout
Run Code Online (Sandbox Code Playgroud)


Spe*_*her 13

这是Cyrille Rossant针对IPython 3.0的新解决方案,它坚持使用markdown文件而不是基于json的ipymd文件:

https://github.com/rossant/ipymd


Wes*_*ner 12

(2017-02)

策略

  • on_commit():
    • 剥离输出> name.ipynb(nbstripout,)
    • 剥离输出> name.clean.ipynb(nbstripout,)
    • 总是nbconvert到python:name.ipynb.py(nbconvert)
    • 总是转换为markdown:name.ipynb.md(nbconvert,ipymd)
  • vcs.configure():
    • git difftool,mergetool:来自nbdime的nbdiff和nbmerge

工具


nev*_*ves 11

我终于找到了一种高效而简单的方法让Jupyter和Git很好地融合在一起.我还在迈出第一步,但我已经认为它比其他所有复杂的解决方案都要好得多.

Visual Studio Code是Microsoft的一个很酷的开源代码编辑器.它具有出色的Python扩展,现在允许您将Jupyter Notebook作为python代码导入.

将笔记本导入python文件后,所有代码和markdown将一起放在普通的python文件中,注释中带有特殊标记.您可以在下图中看到:

VSCode编辑器用笔记本转换为python

你的python文件只包含笔记本输入单元格的内容.输出将在拆分窗口中生成.你在笔记本中有纯粹的代码,当你执行它时它不会改变.没有与您的代码混合输出.没有奇怪的Json难以理解的格式来分析你的差异.

只需纯Python代码,您可以轻松识别每个差异.

我甚至不需要再对我的.ipynb文件进行版本控制了.我可以放一条*.ipynb线.gitignore.

需要生成笔记本才能发布或与他人分享?没问题,只需单击交互式python窗口中的导出按钮即可

将python文件导出为Notebook格式

我一直在使用它只有一天,但最后我可以愉快地使用Jupyter与Git.

PS:VSCode代码完成比Jupyter好很多.


Mar*_*uts 9

在删除笔记本电脑的输出数年之后,我试图提出一个更好的解决方案。现在,我使用Jupytext,这是我设计的Jupyter Notebook和Jupyter Lab的扩展。

Jupytext可以将Jupyter笔记本转换为各种文本格式(脚本,Markdown和R Markdown)。相反。它还提供了将笔记本与以下格式之一配对的选项,并自动同步笔记本的两种表示形式(一个.ipynb和一个.md/.py/.R文件)。

让我解释一下Jupytext如何回答上述问题:

让我可以选择是包含还是排除输出,

.md/.py/.R文件仅包含输入单元格。您应该始终跟踪该文件。.ipynb仅在要跟踪输出时才对文件进行版本控制。

防止我不想要我的输出,

添加*.ipynb.gitignore

允许我将输出保持在本地版本中,

输出保留在(本地).ipynb文件中

允许我使用版本控制系统查看何时更改了输入(即,如果仅对版本进行控制,但是本地文件具有输出,那么我希望能够查看输入是否已更改(需要提交) )。由于本地文件具有输出,因此使用version control status命令将始终记录差异。)

.py/.R.md文件上的差异是您要查找的

允许我从更新的干净笔记本中更新我的工作笔记本(包含输出)。(更新)

拉到.py/.R.md文件的最新版本,然后在Jupyter(Ctrl + R)中刷新笔记本。您将从文本文件中获取最新的输入单元格,并从.ipynb文件中获取匹配的输出。内核不受影响,这意味着将保留您的局部变量-您可以在离开内核的地方继续工作。

我对Jupytext的爱是可以在您喜欢的IDE中编辑笔记本(以.py/.R.md文件的形式 )。使用这种方法,重构笔记本变得容易。完成后,您只需要在Jupyter中刷新笔记本即可。

如果想尝试一下:用安装Jupytext,pip install jupytext然后重新启动Jupyter Notebook或Lab编辑器。打开要版本控制的笔记本,然后使用Jupyter笔记本中的Jupytext菜单(或Jupyter Lab中的Jupytext命令)将与Markdown文件(或脚本)配对。保存您的笔记本,您将获得两个文件:原始文件,以及笔记本的承诺的文本表示形式,非常适合版本控制!.ipynb

对于那些可能感兴趣的人:在命令行上也可以找到Jupytext 。


Dro*_*ror 8

正如所指出的,已--script被弃用3.x.可以通过应用post-save-hook来使用此方法.特别是,将以下内容添加到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
Run Code Online (Sandbox Code Playgroud)

代码取自#8009.


Swi*_*Run 8

与2019年更好的方法相比,上述2016年非常受欢迎的答案是不一致的黑客。

存在几种选择,回答该问题的最佳选择是Jupytext。

文字

在Jupytext迈向数据科学文章

它与版本控制一起工作的方式是将.py和.ipynb文件都放入版本控制中。如果需要输入差异,请查看.py;如果需要最新的渲染输出,请查看.ipynb。

值得注意的是:VS Studio,nbconvert,nbdime,氢

我认为,通过更多的工作,VS工作室和/或氢气(或类似产品)将成为该工作流程解决方案中的主导者。


Gre*_*ite 7

不幸的是,我对Mercurial了解不多,但我可以给你一个与Git一起使用的可能解决方案,希望你能将我的Git命令转换成他们的Mercurial等价物.

对于后台,在Git中,该add命令将对文件所做的更改存储到暂存区域.完成此操作后,Git将忽略对该文件的任何后续更改,除非您告诉它也将其暂存.因此,下面的脚本,对于每个给定的文件,剥离剥离文件的所有outputsprompt_number sections,阶段,然后恢复原始:

注意:如果运行此操作会收到类似的错误消息ImportError: No module named IPython.nbformat,则使用ipython运行脚本而不是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)
Run Code Online (Sandbox Code Playgroud)

一旦脚本在您想要提交其更改的文件上运行,就运行git commit.


Alb*_*ert 6

我用一种非常务实的方法; 这对于几个笔记本电脑来说很有效.它甚至可以让我"转移"笔记本电脑.它适用于Windows作为Unix/MacOS.
Al认为很简单,就是解决上面的问题......

概念

基本上,你追踪.ipnyb-files,只有相应的.py-files.
通过使用该选项启动笔记本服务器--script,可以在保存笔记本时自动创建/保存该文件.

那些.py文件确实包含所有输入; 非代码保存到注释中,单元格边框也是如此.可以将这些文件读取/导入(并拖动)到笔记本服务器中以(重新)创建笔记本.只有输出消失了; 直到重新运行.

我个人使用mercurial版本跟踪.py文件; 并使用普通(命令行)命令添加,签入(ect).大多数其他(D)VCS将允许这样做.

现在很容易跟踪历史; 它.py是小的,文本的和简单的差异.有一段时间,我们需要一个克隆(只是分支;在那里启动第二个笔记本 - 服务器),或者旧版本(检出并导入到笔记本服务器中)等.

提示与技巧

  • *.ipynb添加到' .hgignore ',因此Mercurial知道它可以忽略这些文件
  • 创建一个(bash)脚本来启动服务器(带有--script选项)并对其进行版本跟踪
  • 保存笔记本会保存.py-file,但不会将其签入.
    • 这是一个缺点:人们可以忘记这一点
    • 它也是一个特性:可以保存笔记本(并在以后继续),而无需集群存储库历史记录.

祝福

  • 在笔记本电脑仪表板上有一个用于登记/添加/等的按钮会很不错
  • 结账(通过示例)file@date+rev.py)应该是有用的这将是很多工作来添加; 也许我会这样做一次.到现在为止,我只是手工完成.


sim*_*mon 6

只是遇到“ jupytext”,它看起来是一个完美的解决方案。它从笔记本生成一个.py文件,然后使两者保持同步。您可以通过.py文件对输入进行版本控制,区分和合并,而不会丢失输出。当您打开笔记本时,它使用.py作为输入单元格,并使用.ipynb作为输出单元。如果要在git中包含输出,则只需添加ipynb。

https://github.com/mwouts/jupytext


nik*_*nik 5

由于存在许多处理笔记本版本控制的策略和工具,我尝试创建流程图以选择合适的策略(于2019年4月创建)

决策流程以选择版本控制策略


tyo*_*213 5

我还将向其他人添加建议的https://nbdev.fast.ai/,这是一种最先进的“文学编程环境,正如 Donald Knuth 在 1983 年所设想的那样!”。

它还有一些 git hooks 可以提供一些帮助https://nbdev.fast.ai/#Avoiding-and-handling-git-conflicts以及其他命令,例如:

  • nbdev_read_nbs
  • nbdev_clean_nbs
  • nbdev_diff_nbs
  • nbdev_测试_nbs

因此,您还可以在编写库时随时创建文档,例如其中一些文档:

除了第一个链接之外,您还可以在此处观看nbdev 教程的视频。


归档时间:

查看次数:

62891 次

最近记录:

6 年,2 月 前