更好地使用版本控制系统追踪.ipynb文件

更好地使用版本控制系统追踪.ipynb文件

jupyter-notebook是一款优秀的用于交互式编程的编程环境,jupyter-notebook中文件默认采用.ipynb格式保存,其内容包括程序代码以及代码的输出结果.然而,当使用版本控制系统跟踪.ipynb文件时,用户通常不需要保存代码输出结果.此外,即使对代码文件未作任何更改,当重新运行.ipynb文件时,每一代码单元(cell)的编号可能会发生变化,而代码内容并未发生任何变化,此时对这类更改的提交会使提交历史变得混乱.如何处理这两个问题对版本控制系统跟踪.ipynb文件时带来的不变呢?

本文汇总的方法均来自stackoverflow上关于该问题的相关回答,感兴趣可点击查看具体回答.

#####1.不保存文件中的输出内容即代码单元编号

手动清除:可在在每次保存文件前,手动删除其中的输出内容及代码单元编号,方法是点击Cell->All Output->Clear,然后保存文件.

自动清除:为了在每次出保存文件时,自动清除其中的输出内容及代码单元编号,可以配置jupyter-notebook 保存文件前的钩子代码. 首先输入jupyter --config-dir查看当前jupyte-notebook配置文件路径,然后在** 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-notebook,使上述配置文件生效.

2.添加git过滤器代码,该过滤器可实现与方法一中代码相同的删除输出即代码单元格编号功能

minrk在gist中提供了钩子代码的具体实现,详见链接.用户可下载该代码,然后将其配置为.ipynb类型文件的过滤器.kynan进一步将代码完善,并制作了python包nbstripout.点击详情查询链接.用户可按照如下步骤下载并配置git.

pip install nbstripout

#进入当前git版本库进行配置
cd cur_path #请根据实际情况替换cur_path为自己的代码文件夹
nbstripout --install #添加过滤器
#添加过滤器后,可查看当前git配置文件中,[filter]选项增加了nbstripout
#ls .git/config

nbstripout --install --attributes .gitattributes #配置过滤器作用的文件类型
#配置完成后,可查看新增文件.gitattributes
#ls .gitattributes

配置完成过滤器后,每次仍正常追踪.ipynb文件,不过过滤器会在实际追踪前对文件进行过滤,因此.ipynb文件中的输出不会被跟踪,且所有代码单元格的编号均为空.

3. 保存.ipynb文件对应的脚本文件,使用版本控制系统跟踪该脚本文件.

手动方法:点击Download as可保存.ipynb文件对应的脚本文件,若为python代码,则保存.py文件,若为scala代码,则可保存.scala文件.

自动方法:配置jupyter-notebook保存文件后的钩子代码,在保存.ipynb文件后,保存对应的脚本代码.将如下代码片段添加到jupyter_notebook_config.py文件中.关于jupyter_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(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d)

c.FileContentsManager.post_save_hook = post_save

为防止版本控制系统跟踪.ipynb文件,可以在.gitignore文件中添加"*.ipynb".

注意到以上两种方法中,生成的脚本文件中虽然不包括.ipynb文件中的输出部分,但在注释中仍包含代码单元格的编号.因此再次运行代码可能导致代码单元格编号发生变化,由此脚本文件中对应的注释部分会提示发生变动,需要重新添加,并提交.因此这种转化是不彻底的.

4.使用jupytext包实现脚本文件与.ipynb文件之间的相互转化

jupytext包的主要思想同方法三一致,即使用版本控制系统追踪.ipynb文件对应的脚本文件(对于python代码,即为.py文件),相比于方法三,使用jupytext包转化得到的脚本文件中不包含代码单元格的编号,因此这种方法的转化是"干净"的.相较于第三种方法,jupytext包的最大优势在于其保存的脚本文件可以以notebook的方式打开,这在第三种方法中是无法实现的.这一功能极大地便利了多人合作共同编辑一个notebook文件,具体功能见后文讨论.

jupytext包最简单的使用方法是安装jupytext包,然后打开jupyter-notebook,点击File->Jupyext,单击选中弹出菜单中的Pair notebook with light Script,随后,当用户保存.ipynb文件时,该对应的脚本文件会自动保存在同一目录下.此外,jupytext包还提供了终端命令用于文件格式转化,用户可在命令行编写命令实现文件的批量格式转化.

jupyext包的手册页同时描述了基于jupytext包的git工作流程:

  1. 编写.ipynb文件,使用jupytext生成该文件对应的脚本文件.
  2. 将脚本文件上传至git版本控制系统服务器,如github,gitlab等
  3. 合作伙伴 配置jupytext以支持脚本文件自动转化,从服务器拉取其他人上传的脚本文件,使用jupyter-notebook打开脚本文件,此时脚本文件会以notebook形式打开.重新执行各代码单元,查看他人当前工作内容.
  4. 修改当前代码, 将当前代码的脚本文件提交至版本控制系统,并上传至服务器.
  5. 你从服务器拉取合作者上传的脚本文件,并在jupyter-notebook中打开最新的脚本文件,此时合作者未作修改的单元格仍显示原有输出,且单元格编号未发生变化.而被合作者删除的单元格将会为空,合作者新添加的单元格或作出部分修改的单元格其输出内容将为空,且该代码单元格无编号.然后你可以在此基础上重新执行被更改的代码单元,保存脚本文件,并进行提交.
5. 使用vscode生成.ipynb文件对应的脚本文件

vscode是微软开发的一款开源编辑器,通过丰富的扩展可提供多语言编程支持,近年来发展迅猛,得到越来越多人的喜爱.使用vscode安装python扩展,可支持在vscode中编辑,运行jupyter-notebook文件.

vscode支持.ipynb文件与脚本文件间的相互转化,且转化得到的脚本文件不包含代码单元格编号.相较于jupytext,vscode不能比较脚本文件与当面目录下.ipynb文件的相似部分.第4方法中提到,若当前存在与脚本文件同名的.ipynb文件,则在打开脚本文件后,未更改的单元格将显示.ipynb文件对应的输出内容.被更改的代码单元格其输出则为空.而vscode则无法实现此功能.顺便提一下,jupytext包所支持的.ipynb与脚本文件差异对比功能仍然是存在缺陷的.其仅对比了各代码单元格代码文本的差异,而未考虑代码上下文相关所引起的潜在差异性.例如如下代码:

#In[1]
x = [1, 2, 3, 4]
x
#out[1] [1, 2, 3, 4]
#In[2]
x.append(6)
#out[2] [1, 2, 3, 4, 6]

若某用户将代码单元格1更改为x = [1, 2, 3, 4, 5, 则jupytext仅能对比发现代码单元格1中代码文本发生了变化,而实际上,代码单元格2的输出内容此时也与修改后文件不一致.这一问题应当引起注意.

6. 对.ipynb文件提供特别支持的类git工具

由于.ipynb为json文件,用户生成的图片则是使用base64编码的文本内容,因此当想要直接比较两个不同版本的.ipynb文件时,其文件对比结果通常并不直观.为了对.ipynb文件提供特别支持,可基于.ipynb文件中json格式的特别含义,修改文件差异比较结果,这类工具可称为类git工具,因为其提供了一些与git类似的功能,如git diff, git merge,但其针对.ipynb文件进行了功能上的修订,使返回结果更加直观.

有两个与该方法相关的包, nbdime,gitnb.此处,简要介绍nbdime包,关于gitnb包,感兴趣用户可以查看其github仓库主页,及相关文档,了解更多信息.

nbdime主要功能实现了类似git diffgit merge的功能,以对比两文件差异为例,用户可直接使用终端命令nbdiff file1 file2显示两文件不同,也可将该命令与git版本控制系统结合,使用nbdime config-git --enable配置当前git版本 控制系统后,git diff file1 file2可实现与nbdiff file1 file2相同的功能.此外,用户还可使用nbdiff-web file1 file2对比两文件差异,执行该命令后,将在浏览器中以可视化的方式对比两文件,这对于包括图形,表格等输出内容的.ipynb文件非常有用.此外,用户还可将nbdime作为jupyter-notebook的插件进行配置,详细内容参加nbdime手册页进行了解.

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值