如何拆分一个IPython Notebook

17

我的 Jupyter 笔记本越来越长了,这让导航变得困难。

我想将每个章节(以 Heading 1 开始的单元格)保存到不同的文件中。我该怎么做?似乎无法在多个笔记本之间剪切和粘贴多个单元格。


你的代码在哪里? - Raptor
在JupyterLab中,选择并拖动一系列单元格到另一个笔记本是可能的,参见“使用JupyterLab手动拖动一系列单元格到新笔记本”(https://dev59.com/srP3oIgBc1ULPQZFhfZH#71244733)中的底部。还有[nbformat](https://nbformat.readthedocs.io/en/latest/api.html#),它允许您以编程方式解析笔记本并创建新的笔记本,如果您需要超出`nbmanips`(请参见tturbo的优秀回答)可以轻松处理的定制级别。 - Wayne
4个回答

14

这是我使用的方法 - 有些棘手,但它有效:

  1. 使用菜单中的“文件->制作副本”功能制作主笔记本的多个副本。为每个要提取的章节制作一个副本。
  2. 重命名每个章节的副本:例如将“master-copy0”重命名为“第1章”。
  3. 删除不属于第1章的每个单元格 - 例如在命令模式下使用'dd'。
  4. 保存缩写文件。
  5. 为每个章节重复步骤3和4。

我相信开发人员可能正在为未来的版本开发更好的解决方案。


我只回答了你的第一个问题 - 我认为你只被允许提出一个问题。我建议将关于超链接内容的第二个问题单独提出。 - David Smith
嗨,David,谢谢你的回答。你的方法是有效的,但如果你需要经常这样做,那就太费力了。我的笔记本包含10个章节,每个章节大约有100个块。选择多个块是不可能的。这意味着要选中单个块并按下dd键90000次,即10 * 9 * 100 = 90000次。也许可以以某种方式自动化处理。 - sjdh
也许没有你想象的那么糟糕。 - David Smith
1
也许没有你想象的那么糟。首先,你犯了一个算术错误。你只需要删除9000个单元格,而不是90000个。其次,你不需要选择每个单元格,在删除后,下一个单元格会自动选中。第三,按住"d"键使其自动重复并依次删除单元格(非常快!)。你可能需要调整按键重复速率,以免意外删除未打算删除的块。我估计你可以在不到一个小时内完成整个一次性重组。我不知道你想要定期进行——你的问题说“我的笔记本之一”。 - David Smith
虽然这不是一个编程解决方案,但我认为对于大多数开发者来说,这个解决方案最适合处理 少量 笔记本电脑。 - Seabass77

2
一个笔记本文件是JSON格式,因此我将所有数据作为JSON格式获取并自动拆分成几个文件。
这是我编写的代码。
如果您稍微查看一下,代码似乎很复杂,但只要您花点时间检查它,您就会发现它很简单。这是一个单独文件的示例,链接为http://www.fun-coding.org/DS&AL4-1.html,在我拆分后,我还将其转换为HTML格式。
import json
from pprint import pprint
import re

def notebook_spliter(FILENAME, chapter_num):

    with open(FILENAME + '.ipynb') as data_file:    
        data = json.load(data_file)

    copy_cell, chapter_in = list(), False

    regx = re.compile("## [0-9]+\. ")
    for num in range(len(data['cells'])):
        if chapter_in and data['cells'][num]['cell_type'] != 'markdown':
            copy_cell.append(data['cells'][num])
        elif data['cells'][num]['cell_type'] == 'markdown':
            regx_result = regx.match(data['cells'][num]['source'][0])

            if regx_result:
                print (regx_result.group())
                regx2 = re.compile("[0-9]+")
                regx2_result = regx2.search(regx_result.group())
                if regx2_result:
                    print (int(regx2_result.group()))
                    if chapter_in == False:
                        if chapter_num == int(regx2_result.group()):
                            chapter_in = True
                            copy_cell.append(data['cells'][num])
                    else:
                        if chapter_num != int(regx2_result.group()):
                            break
            elif chapter_in:
                copy_cell.append(data['cells'][num])

    copy_data["cells"] = copy_cell
    copy_data["metadata"] = data["metadata"]
    copy_data["nbformat"] = data["nbformat"]
    copy_data["nbformat_minor"] = data["nbformat_minor"]
    with open(FILENAME + '-' + str(chapter_num) + '.ipynb', 'w') as fd:
        json.dump(copy_data, fd, ensure_ascii=False)

这是一个用于检查笔记本文件中章节编号的函数。我在Markdown单元格中使用“## 1. 章节名称”添加了章节编号,因此只需使用正则表达式检查##数字模式即可。
接下来的代码将把单元格数据复制到这个章节编号中,并将仅复制的单元格和其他元数据(metadata)、nbformat和nbformat_minor保存到单独的文件中。
copy_data = dict()
FILENAME = 'DS&AL1' 
CHAPTERS = list()
with open(FILENAME + '.ipynb') as data_file:    
    data = json.load(data_file)

for num in range(len(data['cells'])):
    if data['cells'][num]['cell_type'] == 'markdown':
        regx_result = regx.match(data['cells'][num]['source'][0])

        if regx_result:
            regx2 = re.compile("[0-9]+")
            regx2_result = regx2.search(regx_result.group())
            if regx2_result:
                CHAPTERS.append(int(regx2_result.group()))
print (CHAPTERS)

for chapternum in CHAPTERS:
    notebook_spliter(FILENAME, chapternum)

2

2023更新

几年后,幸运的是有一个库可以为您完成这样的事情:

pip install nbmanips
nb select has_html_tag h1 | nb split -s nb.ipynb
  • 命令的第一部分 (nb select has_html_tag h1) 指定了在哪些单元格上执行拆分操作。

  • 第二部分 (nb split -s nb.ipynb) 基于管道选择拆分笔记本。 -s 标志告诉 nbmanips 使用选择而不是单元格索引。

来源: https://towardsdatascience.com/split-your-jupyter-notebooks-in-2-lines-of-code-de345d647454

库: https://pypi.org/project/nbmanips/


1
这看起来是一个非常有用的工具。对于那些希望进行更多编程定制的人,超出了nbmanips所能轻松实现的范围,我建议看一下[https://nbformat.readthedocs.io/en/latest/api.html#]。`nbformat`作为Jupyter的一部分提供,因此它可以在你运行笔记本的任何地方运行,而无需额外的包。也许一个好的开始位置是[这里](https://dev59.com/srP3oIgBc1ULPQZFhfZH#71244733),因为问题是相关的。我已经在[这里](https://discourse.jupyter.org/search?q=nbformat%20%40fomightez)发布了带有代码的示例。 - Wayne

2
最简单的方法可能是在文本编辑器中编辑.ipnb文件。 下面列出了一个非常简单笔记本的内容。
笔记本电脑看起来像
第1章 输入[1]: 1+1 输出[1]: 2 第2章 输入[2]: 2+2 输出[2]: 4
要取出第1章并将其放在第2章后面,您可以这样做
1.搜索“级别”:1 2.您会发现: {      “cell_type”:“标题”,      “级别”:1,  “metadata”:{},      “source”:[  “第1章” ] }, 和 {  “cell_type”:“标题”,  “级别”:1,  “metadata”:{},  “source”:[  “第2章” ] }, 3.从第一个搜索结果的开头开始移动,刚好在第二个搜索结果的结尾下方 4.注意逗号
您可以以类似的方式操作多个笔记本。
这是示例的.ipnb文件。
{
 "metadata": {
  "name": "",
  "signature": ""
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "heading",
     "level": 1,
     "metadata": {},
     "source": [
      "Chapter 1"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "1+1"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 1,
       "text": [
        "2"
       ]
      }
     ],
     "prompt_number": 1
    },
    {
     "cell_type": "heading",
     "level": 1,
     "metadata": {},
     "source": [
      "Chapter 2"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "2+2"
     ],
     "language": "python",
     "metadata": {},
     "outputs": [
      {
       "metadata": {},
       "output_type": "pyout",
       "prompt_number": 2,
       "text": [
        "4"
       ]
      }
     ],
     "prompt_number": 2
    }
   ],
   "metadata": {}
  }
 ]
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接