将IPython笔记本保留在版本控制之下的好策略是什么?
笔记本格式非常适合版本控制:如果想要版本控制笔记本和输出,那么这很有效。当人们只想对输入进行版本控制时,就会产生烦恼,除了可以是大型二进制blob的单元输出(也就是#34;构建产品"),特别是对于电影和情节。特别是,我正在努力寻找一个良好的工作流程:
如上所述,如果我选择包含输出(例如,在使用nbviewer时这是可取的),那么一切都很好。问题是当我不想要版本控制输出时。有一些工具和脚本用于剥离笔记本的输出,但我经常遇到以下问题:
Cell/All Output/Clear
菜单选项相比,剥离输出的某些脚本会稍微改变格式,从而在差异中产生不必要的噪音。这可以通过一些答案来解决。我已经考虑过几个选项,我将在下面讨论,但尚未找到一个很好的综合解决方案。完整的解决方案可能需要对IPython进行一些更改,或者可能依赖于一些简单的外部脚本。我目前使用mercurial,但想要一个也适用于git的解决方案:理想的解决方案是与版本控制无关。
此问题已多次讨论,但从用户的角度来看,没有明确或明确的解决方案。这个问题的答案应该提供明确的策略。如果它需要最近(甚至开发)版本的IPython或一个易于安装的扩展名,那就没问题了。
更新:我一直在使用my modified notebook版本,可选择使用Gregory Crosswhite's suggestions保存每次保存的.clean
版本。这满足了我的大多数约束条件,但仍未解决以下问题:
.clean
文件,然后需要以某种方式集成到我的工作版本中。 (当然,我总是可以重新执行笔记本,但这可能很痛苦,特别是如果某些结果取决于长时间的计算,并行计算等)。我还不知道如何解决这个问题。也许涉及像ipycache这样的扩展的工作流可能会起作用,但这似乎有点过于复杂。Cell/All Output/Clear
菜单选项删除输出。答案 0 :(得分:115)
这是我用git的解决方案。它允许你像往常一样添加和提交(和diff):这些操作不会改变你的工作树,同时(重新)运行一个笔记本不会改变你的git历史。
虽然这可能适用于其他VCS,但我知道它不能满足您的要求(至少VSC不可知)。尽管如此,它对我来说仍然是完美的,虽然没有什么特别精彩,很多人可能已经使用过它,但我没有找到关于如何通过Google搜索来实现它的明确指示。所以它可能对其他人有用。
~/bin/ipynb_output_filter.py
)chmod +x ~/bin/ipynb_output_filter.py
)使用以下内容
创建文件~/.gitattributes
*.ipynb filter=dropoutput_ipynb
运行以下命令:
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
完成!
<强>限制:强>
somebranch
而你做git checkout otherbranch; git checkout somebranch
,你通常希望工作树不变。相反,您将失去两个分支之间源不同的笔记本电脑的输出和单元格编号。git commit notebook_file.ipynb
,尽管它至少可以使git diff notebook_file.ipynb
免于base64垃圾。)我的解决方案反映了这样一个事实:我个人不喜欢将生成的内容保留为版本 - 请注意,执行涉及输出的合并几乎可以保证输出或无效或< / em>两者。
修改强>
如果您按照我的建议采用了解决方案 - 也就是说,全局 - 您将遇到麻烦,以防某些git repo 想要到版本输出。因此,如果您想禁用特定git存储库的输出过滤,只需在其中创建一个文件 .git / info / attributes ,并使用
**。ipynb filter =
作为内容。显然,以相同的方式可以执行相反的操作:为特定存储库启用仅过滤。
代码现在保留在自己的git repo
如果上述说明导致ImportErrors,请尝试在脚本路径前添加“ipython”:
git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
编辑:2016年5月(2017年2月更新):我的脚本有多种替代方案 - 为了完整性,以下是我所知道的列表:nbstripout({{3} } other),variants,nbstrip。
答案 1 :(得分:56)
我们有一个合作项目,其产品是Jupyter笔记本电脑,我们在过去六个月中使用了一种方法很有效:我们激活自动保存.py
文件并跟踪.ipynb
文件1}}文件和.py
文件。
这样,如果有人想查看/下载最新的笔记本,他们可以通过github或nbviewer这样做,如果有人想看看笔记本代码是如何变化的,他们可以看看{{1文件。
对于.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
文件并重新启动笔记本服务器。
如果您不确定在哪个目录中找到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
文件并重新启动笔记本服务器。这些行来自github问题答案@minrk provided,而@dror也包含在他的SO答案中。
对于ipython_notebook_config.py
笔记本服务器,可以通过以下方式启动服务器来完成:
Ipython 2
或添加行
ipython notebook --script
到c.FileNotebookManager.save_script = True
文件并重新启动笔记本服务器。
如果您不确定在哪个目录中找到ipython_notebook_config.py
文件,可以输入ipython_notebook_config.py
,如果您在那里找不到该文件,则可以创建该文件输入ipython locate profile default
。
此处our project on github that is using this approach:此处是github example of exploring recent changes to a notebook。
我们对此非常满意。
答案 2 :(得分:36)
我创建了nbstripout
,基于MinRKs gist,支持Git和Mercurial(感谢mforbes)。它既可以在命令行上单独使用,也可以作为过滤器使用,可以通过nbstripout install
/ nbstripout uninstall
轻松(非)安装在当前存储库中。
从PyPI或只是
获取pip install nbstripout
答案 3 :(得分:13)
以下是Cyrille Rossant针对IPython 3.0的新解决方案,该解决方案坚持使用markdown文件而不是基于json的ipymd文件:
答案 4 :(得分:11)
(2017-02)
<强>策略强>
nbstripout
,)nbstripout
,)nbconvert
到python:name.ipynb.py(nbconvert
)nbconvert
,ipymd
)工具强>
nbstripout
:从笔记本中删除输出
pip install nbstripout; nbstripout install
ipynb_output_filter
:从笔记本中删除输出
ipymd
:转换{Jupyter,Markdown,O'Reilly Atlas Markdown,OpenDocument,.py}
nbdime
:“用于区分和合并Jupyter笔记本的工具。” (2015)
nbdiff
:以对终端友好的方式比较笔记本
nbmerge
:笔记本电脑与自动冲突解决方案的三向合并
nbdiff-web
:向您展示笔记本电脑的丰富渲染差异nbmerge-web
:为您提供基于网络的笔记本电脑三向合并工具nbshow
:以对终端友好的方式呈现单个笔记本答案 5 :(得分: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
代码取自#8009。
答案 6 :(得分:8)
我终于找到了一种使Jupyter和Git完美配合的有效且简单的方法。我仍处于起步阶段,但我认为它比所有其他复杂解决方案都要好。
Visual Studio Code是Microsoft的一款很酷的开源代码编辑器。它具有出色的Python扩展,现在允许您import a Jupyter Notebook作为python代码。
将笔记本导入到python文件后,所有代码和markdown都将放在一个普通的python文件中,并在注释中带有特殊标记。您可以在下图中看到:
您的python文件仅包含笔记本输入单元格的内容。输出将在拆分窗口中生成。您的笔记本中有纯代码,仅在执行时它不会更改。没有与您的代码混合输出。没有奇怪的Json难以理解的格式来分析差异。
仅是纯python代码,您可以在其中轻松识别每个差异。
我什至无需再对.ipynb
文件进行版本控制。我可以在*.ipynb
中放入.gitignore
行。
需要生成一个笔记本来与某人发布或共享吗?没问题,只需在交互式python窗口中click the export button
我已经使用了一天,但最终我可以很高兴地将Jupyter与Git一起使用。
P.S.:VSCode代码完成比Jupyter好很多。
答案 7 :(得分:6)
我采用非常务实的方法;这对于几个笔记本电脑来说很有效。它甚至可以让我转移&#39;笔记本周围。它适用于Windows作为Unix / MacOS Al认为这很简单,就是解决上面的问题......
基本上,不跟踪.ipnyb
- 文件,只跟踪相应的.py
- 文件。
通过使用--script
选项启动 notebook-server ,可以在保存笔记本时自动创建/保存该文件。
那些.py
- 文件确实包含所有输入;非代码保存到注释中,单元格边框也是如此。可以将这些文件读取/导入(并拖动)到笔记本服务器中以(重新)创建笔记本。只有输出消失了;直到重新运行。
我个人使用 mercurial 来跟踪.py
个文件;并使用普通(命令行)命令添加,签入(ect)。大多数其他(D)VCS将允许这样做。
现在跟踪历史很简单; .py
是小的,文本的和简单的差异。有一段时间,我们需要一个克隆(只是分支;在那里启动第二个笔记本 - 服务器),或旧版本(检出并导入到笔记本服务器中)等。
--script
选项)并对其进行版本跟踪.py
- 文件,但不会将其签入。
file@date+rev.py
)应该会有所帮助
添加它会有很多工作要做;也许我会这样做一次。到现在为止,我只是手工完成。答案 8 :(得分:6)
不幸的是,我对Mercurial了解不多,但我可以给你一个可以与Git一起使用的解决方案,希望你能将我的Git命令翻译成他们的Mercurial等价物。
对于后台,在Git中,add
命令将对文件所做的更改存储到暂存区域。完成此操作后,Git将忽略对该文件的任何后续更改,除非您告诉它也将其暂存。因此,以下脚本为每个给定文件删除所有outputs
和prompt_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)
在您要提交更改的文件上运行脚本后,只需运行git commit
。
答案 9 :(得分:4)
在删除笔记本电脑的输出几年后,我试图提出一个更好的解决方案。现在,我使用Jupytext,这是我设计的Jupyter Notebook和Jupyter Lab的扩展。
Jupytext可以将Jupyter笔记本转换为各种文本格式(脚本,Markdown和R Markdown)。相反。它还提供了将笔记本与这些格式之一进行配对,并自动同步笔记本的两种表示形式(.ipynb
和.md/.py/.R
文件)的选项。 / p>
让我解释一下Jupytext如何回答上述问题:
允许我在包含或排除输出之间进行选择,
.md/.py/.R
文件仅包含输入单元格。您应该始终跟踪该文件。仅在要跟踪输出时才对.ipynb
文件进行版本控制。
防止我不想要的输出,
将*.ipynb
添加到.gitignore
允许我将输出保留在本地版本中,
输出保留在(本地).ipynb
文件中
允许我使用版本控制系统查看何时更改了输入(即,如果仅对版本进行控制,但是本地文件具有输出,那么我希望能够查看输入是否已更改(需要提交)。由于本地文件具有输出,因此使用版本控制状态命令将始终记录差异。)
您正在寻找.py/.R
或.md
文件上的差异
允许我从更新的清洁笔记本中更新工作的笔记本(包含输出)。 (更新)
拉出.py/.R
或.md
文件的最新版本,然后在Jupyter(Ctrl + R)中刷新笔记本。您将从文本文件中获得最新的输入单元格,并从.ipynb
文件中获得匹配的输出。内核不受影响,这意味着将保留您的局部变量-您可以在离开内核的地方继续工作。
我对Jupytext的爱是可以在您喜欢的IDE中编辑笔记本(以.py/.R
或.md
文件的形式)。使用这种方法,重构笔记本变得容易。完成后,您只需要在Jupyter中刷新笔记本即可。
如果想尝试一下:用pip install jupytext
安装Jupytext,然后重新启动Jupyter Notebook或Lab编辑器。使用Jupyter笔记本中的Jupytext Menu(或Jupyter Lab中的Jupytext commands),打开您要进行版本控制的笔记本,并配对与Markdown文件(或脚本)。 )。保存您的笔记本,您将获得两个文件:原始.ipynb
,再加上笔记本的承诺文本表示形式,非常适合版本控制!
对于那些可能感兴趣的人:command line上也提供Jupytext。
答案 10 :(得分:4)
只需遇到“ jupytext”,它看起来就是一个完美的解决方案。它从笔记本生成一个.py文件,然后使两者保持同步。您可以通过.py文件对输入进行版本控制,区分和合并,而不会丢失输出。打开笔记本时,它使用.py作为输入单元格,并使用.ipynb作为输出单元。而且,如果要将输出包含在git中,则只需添加ipynb。
答案 11 :(得分:3)
我已经构建了解决这个问题的python包
https://github.com/brookisme/gitnb
它提供了一个带有git启发语法的CLI,用于在git仓库中跟踪/更新/区分笔记本。
继承人的一个例子
# 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"
请注意,我使用“gitnb commit”的最后一步是提交到你的git repo。它本质上是
的包装# 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"
还有其他几种方法,可以进行配置,以便在每个阶段都需要更多或更少的用户输入,但这就是一般的想法。
答案 12 :(得分:3)
与2016年更好的方法相比,上述2016年非常受欢迎的答案是不一致的黑客行为。
存在几种选择,回答该问题的最佳选择是Jupytext。
赶上Towards Data Science article on Jupytext
它与版本控制一起工作的方式是将.py和.ipynb文件都放入版本控制中。如果需要输入差异,请查看.py,如果需要最新的渲染输出,请查看.ipynb。
值得注意的是:VS studio,nbconvert,nbdime,氢
我认为,通过更多的工作,VS Studio和/或氢气(或类似产品)将成为此工作流程解决方案中的主导者。
答案 13 :(得分:3)
在挖掘之后,我终于找到了this relatively simple pre-save hook on the Jupyter docs。它剥离单元输出数据。您必须将其粘贴到 INCLUDE=TABLE:"IN (select table_name from TAB_LIST)"
文件中(有关说明,请参阅下文)。
jupyter_notebook_config.py
如果您不确定在哪个目录中找到
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
文件,可以在命令提示符/终端中键入jupyter_notebook_config.py
,如果在那里找不到该文件,则为可以通过键入jupyter --config-dir
来创建它。
答案 14 :(得分:3)
如果您收到像这样的Unicode解析错误,请跟进Pietro Battiston的优秀脚本:
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)
您可以在脚本的开头添加:
reload(sys)
sys.setdefaultencoding('utf8')
答案 15 :(得分:2)
好的,按照讨论here,看起来当前最好的解决方案是使git过滤器在提交时自动从ipynb文件中删除输出。
这是我为使其工作所做的工作(从该讨论中复制):
我稍微修改了cfriedline的nbstripout文件,以便在您无法导入最新的IPython时提供信息错误:
https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e47b870a8d799e92b/notebooks/config/strip_notebook_output
并将其添加到我的仓库中,让我们在./relative/path/to/strip_notebook_output
还将文件.gitattributes文件添加到repo的根目录,其中包含:
*.ipynb filter=stripoutput
并创建了一个包含<{p}的setup_git_filters.sh
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
然后跑source setup_git_filters.sh
。花哨的$(git rev-parse ...)就是在任何(Unix)机器上找到你的仓库的本地路径。
答案 16 :(得分:2)
答案 17 :(得分:2)
我做了艾伯特&amp; amp; Rich做了 - 不要版本.ipynb文件(因为这些文件可能包含混乱的图像)。相反,要么始终运行ipython notebook --script
,要么将c.FileNotebookManager.save_script = True
放入配置文件中,以便在保存笔记本时始终创建(可版本化的).py
文件。
要重新生成笔记本(在签出仓库或切换分支后),我将脚本py_file_to_notebooks.py放在我存储笔记本的目录中。
现在,在签出回购后,只需运行python py_file_to_notebooks.py
即可生成ipynb文件。切换分支后,您可能必须运行python py_file_to_notebooks.py -ov
来覆盖现有的ipynb文件。
为了安全起见,还可以添加
*.ipynb
个.gitignore
文件。
编辑:我不再这样做了,因为(A)你必须在每次结账时都从py文件中重新生成你的笔记本,而且(B)还有其他东西,例如你丢失的笔记本中的降价。我改为使用git过滤器从笔记本中删除输出。关于如何执行此操作的讨论是here。
答案 18 :(得分:1)
答案 19 :(得分:1)
这是2020年4月,有许多用于Jupyter笔记本版本控制的策略和工具。这是您可以使用的所有工具的快速概述,
nbdime-非常适合笔记本的本地差异和合并
nbstripout-一个git过滤器,可在每次提交之前自动删除笔记本的输出
jupytext-保持.py随播文件同步到每个笔记本。您只提交.py文件
nbconvert-将笔记本转换为python脚本或HTML(或两者)并提交这些备用文件类型
ReviewNB-显示GitHub上任何提交或请求请求的笔记本差异(以及输出)。人们还可以在笔记本电脑的单元格上写评论以讨论更改(以下屏幕截图)。
免责声明:我建立了ReviewNB。
答案 20 :(得分:1)
我还将向其他人添加https://nbdev.fast.ai/的支持,这是最先进的“唐纳德·克努斯(Donald Knuth)在1983年所设想的文学编程环境!”。
它也有一些git钩子,可以帮助一点https://nbdev.fast.ai/#Avoiding-and-handling-git-conflicts和其他命令,例如:
因此,您也可以在编写库时随时随地创建文档,例如其中一些:
除了第一个链接之外,您还可以在nbdev tutorial这里观看视频。
答案 21 :(得分:0)
在下面的帖子中讨论的想法如何,应该保留笔记本的输出,并且可能需要很长时间来生成它,并且它很方便,因为GitHub现在可以渲染笔记本。为导出.py文件添加了自动保存挂钩,用于差异和.html与不使用笔记本或git的团队成员共享。
https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d