[英]Converting to (not from) ipython Notebook format
IPython Notebook 自带nbconvert
,可以将notebook导出为其他格式。 但是我如何以相反的方向转换文本? 我问是因为我已经有了不同格式的材料和良好的工作流程,但我想利用 Notebook 的交互环境。
一个可能的解决方案:可以通过导入.py
文件来创建笔记本,并且文档指出,当nbconvert
将笔记本导出为 python 脚本时,它会在可用于重新创建笔记本的注释中嵌入指令。 但是该信息附带了关于此方法局限性的免责声明,并且在我能找到的任何地方都没有记录可接受的格式。 (奇怪的是,在描述 notebook 的JSON 格式的部分中显示了一个示例)。 谁能提供更多信息或更好的选择?
编辑(2016 年 3 月 1 日):接受的答案不再有效,因为出于某种原因,Notebook API 的第 4 版不支持此输入格式。 我添加了一个自我回答,展示了如何使用当前 (v4) API 导入笔记本。 (我并不是不接受当前的答案,因为它当时解决了我的问题,并向我指出了我在自我回答中使用的资源。)
以下适用于 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 正确执行此操作。这是上面链接的更新版本,指向 IPython 3.2.1 版本中的nbpy.py
版本:
https://github.com/ipython/ipython/blob/rel-3.2.1/IPython/nbformat/v3/nbpy.py
基本上,您使用特殊注释(例如# <codecell>
或# <markdowncell>
来分隔各个单元格。 查看line.startswith
中的line.startswith
语句以PyReader.to_notebook
完整列表。
由于接受的答案中的代码不再起作用,我添加了这个自我答案,展示了如何使用当前 ( 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!
"""
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()
鉴于 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)
没有保证,但它对我有用
希望我还不算太晚。
我刚刚在 PyPI 上发布了一个名为p2j的 Python 包。 这个包从 Python 源代码.py
创建一个 Jupyter 笔记本.ipynb
。
pip install p2j
p2j script.py
从.py
文件生成的 Jupyter 笔记本示例:
PyPI: https ://pypi.org/project/p2j/
冒昧地修改了 P.Toccateli 和 alexis 的代码,使它也能像细胞标记一样与 pycharm 和 spyder 一起工作,并在github上发布。
我为 vscode 编写了一个可能有帮助的扩展。 它将 python 文件转换为 ipython 笔记本。 它处于早期阶段,因此如果发生任何错误,请随时提交问题。
对@p-toccaceli 的回答进行了一些改进。 现在,它还恢复了 Markdown 单元格。 此外,它会修剪每个单元格的空挂线。
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)
您可以使用https://github.com/sklam/py2nb 中的脚本 py2nb
您必须为 *.py 使用某种语法,但使用起来相当简单(查看“samples”文件夹中的示例)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.