73

IPython Notebook 自带nbconvert,可以将 notebook导出为其他格式。但是如何以相反的方向转换文本?我问是因为我已经有了不同格式的材料和良好的工作流程,但我想利用 Notebook 的交互环境。

一个可能的解决方案:可以通过导入.py文件来创建笔记本,并且文档指出,当nbconvert将笔记本导出为 python 脚本时,它会在可用于重新创建笔记本的注释中嵌入指令。但是该信息附带了关于此方法局限性的免责声明,并且在我能找到的任何地方都没有记录可接受的格式。(奇怪的是,在描述笔记本的JSON 格式的部分中显示了一个示例)。谁能提供更多信息或更好的选择?

编辑(2016 年 3 月 1 日):接受的答案不再有效,因为由于某种原因,Notebook API 版本 4 不支持这种输入格式。 我添加了一个自我回答,展示了如何使用当前 (v4) API 导入笔记本。(我并没有不接受当前的答案,因为它当时解决了我的问题,并指出了我在自我回答中使用的资源。)

4

10 回答 10

45

由于接受的答案中的代码不再起作用,我添加了这个自我答案,显示如何使用当前 ( v4) API 导入笔记本。

输入格式

IPython Notebook API 的第 2 版和第 3 版可以导入带有特殊结构注释的 python 脚本,并根据需要将其分解为单元格。这是一个示例输入文件(此处为原始文档)。前两行被忽略,并且是可选的。(实际上,阅读器会忽略文件中的任何位置coding:并换<nbformat>行。)

# -*- coding: utf-8 -*-
# <nbformat>3.0</nbformat>

# <markdowncell>

# The simplest notebook. Markdown cells are embedded in comments, 
# so the file is a valid `python` script. 
# Be sure to **leave a space** after the comment character!

# <codecell>

print("Hello, IPython")

# <rawcell>

# Raw cell contents are not formatted as markdown

(API 还接受过时的指令<htmlcell><headingcell level=...>,它们会立即转换为其他类型。)

如何导入

由于某种原因,Notebook API 版本 4 不支持这种格式。它仍然是一种不错的格式,因此通过导入版本 3 并升级来支持它是值得的。原则上它只是两行代码,加上 i/o:

from IPython.nbformat import v3, v4

with open("input-file.py") as fpin:
    text = fpin.read()

nbook = v3.reads_py(text)
nbook = v4.upgrade(nbook)  # Upgrade v3 to v4

jsonform = v4.writes(nbook) + "\n"
with open("output-file.ipynb", "w") as fpout:
    fpout.write(jsonform)

但没那么快!实际上,notebook API 有一个讨厌的 bug:如果输入的最后一个单元格是 markdown 单元格,v3.reads_py()则会丢失它。最简单的解决方法是在<markdown>最后添加一个虚假单元格:错误将删除它,每个人都很高兴。text因此,在传递给之前执行以下操作v3.reads_py()

text += """
# <markdowncell>

# If you can read this, reads_py() is no longer broken! 
"""
于 2016-03-01T10:08:52.853 回答
43

以下适用于 IPython 3,但不适用于 IPython 4。

IPython API 具有读写笔记本文件的功能。您应该使用此 API 而不是直接创建 JSON。例如,以下代码片段将脚本test.py转换为笔记本test.ipynb

import IPython.nbformat.current as nbf
nb = nbf.read(open('test.py', 'r'), 'py')
nbf.write(nb, open('test.ipynb', 'w'), 'ipynb')

关于 nbf.read 理解的 .py 文件格式,最好简单查看解析器类 IPython.nbformat.v3.nbpy.PyReader。代码可以在这里找到(不是很大):

https://github.com/ipython/ipython/blob/master/jupyter_nbformat/v3/nbpy.py

编辑:这个答案最初是为 IPyhton 3 编写的。我不知道如何使用 IPython 4 正确执行此操作。这是上面链接的更新版本,指向nbpy.py来自 IPython 3.2.1 版本的版本:

https://github.com/ipython/ipython/blob/rel-3.2.1/IPython/nbformat/v3/nbpy.py

基本上,您使用特殊注释,例如# <codecell># <markdowncell>来分隔各个单元格。查看完整列表中的line.startswith语句。PyReader.to_notebook

于 2014-04-25T12:08:56.543 回答
40

很老的问题,我知道。但是有jupytext(也可以在pypi上使用)可以转换ipynb为多种格式并返回。

什么时候jupytext安装可以使用

$ jupytext --to notebook test.py

为了生成test.ipynb.

jupytext有很多有趣的功能可以在使用笔记本时派上用场。


这是关于该主题的更新的问题

于 2018-11-20T07:09:12.910 回答
11

Python代码示例如何构建IPython notebook V4:

# -*- coding: utf-8 -*-
import os
from base64 import encodestring

from IPython.nbformat.v4.nbbase import (
    new_code_cell, new_markdown_cell, new_notebook,
    new_output, new_raw_cell
)

# some random base64-encoded *text*
png = encodestring(os.urandom(5)).decode('ascii')
jpeg = encodestring(os.urandom(6)).decode('ascii')

cells = []
cells.append(new_markdown_cell(
    source='Some NumPy Examples',
))


cells.append(new_code_cell(
    source='import numpy',
    execution_count=1,
))

cells.append(new_markdown_cell(
    source='A random array',
))

cells.append(new_raw_cell(
    source='A random array',
))

cells.append(new_markdown_cell(
    source=u'## My Heading',
))

cells.append(new_code_cell(
    source='a = numpy.random.rand(100)',
    execution_count=2,
))
cells.append(new_code_cell(
    source='a = 10\nb = 5\n',
    execution_count=3,
))
cells.append(new_code_cell(
    source='a = 10\nb = 5',
    execution_count=4,
))

cells.append(new_code_cell(
    source=u'print "ünîcødé"',
    execution_count=3,
    outputs=[new_output(
        output_type=u'execute_result',
        data={
            'text/plain': u'<array a>',
            'text/html': u'The HTML rep',
            'text/latex': u'$a$',
            'image/png': png,
            'image/jpeg': jpeg,
            'image/svg+xml': u'<svg>',
            'application/json': {
                'key': 'value'
            },
            'application/javascript': u'var i=0;'
        },
        execution_count=3
    ),new_output(
        output_type=u'display_data',
        data={
            'text/plain': u'<array a>',
            'text/html': u'The HTML rep',
            'text/latex': u'$a$',
            'image/png': png,
            'image/jpeg': jpeg,
            'image/svg+xml': u'<svg>',
            'application/json': {
                'key': 'value'
            },
            'application/javascript': u'var i=0;'
        },
    ),new_output(
        output_type=u'error',
        ename=u'NameError',
        evalue=u'NameError was here',
        traceback=[u'frame 0', u'frame 1', u'frame 2']
    ),new_output(
        output_type=u'stream',
        text='foo\rbar\r\n'
    ),new_output(
        output_type=u'stream',
        name='stderr',
        text='\rfoo\rbar\n'
    )]
))

nb0 = new_notebook(cells=cells,
    metadata={
        'language': 'python',
    }
)

import IPython.nbformat as nbf
import codecs
f = codecs.open('test.ipynb', encoding='utf-8', mode='w')
nbf.write(nb0, f, 4)
f.close()
于 2015-07-26T17:21:34.863 回答
8

希望我不会太晚。

我刚刚在 PyPI 上发布了一个名为p2j的 Python 包。.ipynb这个包从 Python 源代码创建一个 Jupyter 笔记本.py

pip install p2j
p2j script.py

.py从文件生成的 Jupyter 笔记本示例:

从 .py 文件生成的 .ipynb 示例

PyPI:https ://pypi.org/project/p2j/

GitHub:https ://github.com/remykarem/python2jupyter

于 2019-03-07T04:42:23.527 回答
7

鉴于 Volodimir Kopey 的示例,我整理了一个简单的脚本来将通过从 .ipynb 导出获得的 .py 转换回 V4 .ipynb。

当我编辑(在适当的 IDE 中)从 Notebook 导出的 .py 时,我一起破解了这个脚本,我想回到 Notebook 逐个单元地运行它。

该脚本仅处理代码单元。无论如何,导出的 .py 不包含太多其他内容。

import nbformat
from nbformat.v4 import new_code_cell,new_notebook

import codecs

sourceFile = "changeMe.py"     # <<<< change
destFile = "changeMe.ipynb"    # <<<< change


def parsePy(fn):
    """ Generator that parses a .py file exported from a IPython notebook and
extracts code cells (whatever is between occurrences of "In[*]:").
Returns a string containing one or more lines
"""
    with open(fn,"r") as f:
        lines = []
        for l in f:
            l1 = l.strip()
            if l1.startswith('# In[') and l1.endswith(']:') and lines:
                yield "".join(lines)
                lines = []
                continue
            lines.append(l)
        if lines:
            yield "".join(lines)

# Create the code cells by parsing the file in input
cells = []
for c in parsePy(sourceFile):
    cells.append(new_code_cell(source=c))

# This creates a V4 Notebook with the code cells extracted above
nb0 = new_notebook(cells=cells,
                   metadata={'language': 'python',})

with codecs.open(destFile, encoding='utf-8', mode='w') as f:
    nbformat.write(nb0, f, 4)

没有保证,但它对我有用

于 2015-10-07T13:56:40.240 回答
4

冒昧地采用和修改 P.Toccateli 和 alexis 的代码,以便它也可以与 pycharm 和 spyder 之类的细胞标记一起使用,并在github上发布。

于 2016-02-23T12:08:22.357 回答
4

我为 vscode 编写了一个可能有帮助的扩展。它将 python 文件转换为 ipython 笔记本。它处于早期阶段,因此如果发生任何错误,请随时提交问题。

Jupyter 笔记本转换器

于 2018-02-27T17:05:23.583 回答
1

对@p-toccaceli 答案的一些改进。现在,它还恢复了降价单元格。此外,它会修剪每个单元格的空挂线。

    import nbformat
    from nbformat.v4 import new_code_cell,new_markdown_cell,new_notebook

    import codecs

    sourceFile = "changeMe.py"     # <<<< change
    destFile = "changeMe.ipynb"    # <<<< change


    def parsePy(fn):
        """ Generator that parses a .py file exported from a IPython notebook and
    extracts code cells (whatever is between occurrences of "In[*]:").
    Returns a string containing one or more lines
    """
        with open(fn,"r") as f:
            lines = []
            for l in f:
                l1 = l.strip()
                if l1.startswith('# In[') and l1.endswith(']:') and lines:
                    yield ("".join(lines).strip(), 0)
                    lines = []
                    continue
                elif l1.startswith('# ') and l1[2:].startswith('#') and lines:
                    yield ("".join(lines).strip(), 0)

                    yield (l1[2:].strip(), 1)
                    lines = []
                    continue
                lines.append(l)
            if lines:
                yield ("".join(lines).strip(), 0)

    # Create the code cells by parsing the file in input
    cells = []
    for c, code in parsePy(sourceFile):
        if len(c) == 0:
            continue
        if code == 0:
            cells.append(new_code_cell(source=c))
        elif code == 1:
            cells.append(new_markdown_cell(source=c))

    # This creates a V4 Notebook with the code cells extracted above
    nb0 = new_notebook(cells=cells,
                       metadata={'language': 'python',})

    with codecs.open(destFile, encoding='utf-8', mode='w') as f:
        nbformat.write(nb0, f, 4)
于 2020-01-28T09:08:00.610 回答
0

您可以使用来自https://github.com/sklam/py2nb的脚本 py2nb

您必须为您的 *.py 使用某种语法,但它使用起来相当简单(查看“samples”文件夹中的示例)

于 2018-03-08T11:18:49.823 回答