以编程方式向IPython Notebook添加单元格以生成报告

31
我看过一些iPython开发者的讲座,介绍如何将一个iPython笔记本转换为博客文章、PDF文件,甚至是整本书(约43分钟)。PDF-to-X转换器会解释Markdown或代码编写的iPython单元格,并在一步中生成新格式的文档。
我的问题是,我想生成一个大型文档,其中许多图形和部分都是通过编程生成的,类似于这个。为了在iPython中使用上述方法工作,我需要能够编写一个函数,以编写其他iPython-Code-Blocks。这种功能是否存在?
#some pseudocode to give an idea
for variable in list:
    image = make_image(variable)
    write_iPython_Markdown_Cell(variable)
    write_iPython_Image_cell(image)

我认为这可能会有用,所以我想知道:
  1. 是否可以通过iPython生成iPython单元格
  2. 如果有不良原因,我应该坚持使用像模板库(Jinja)这样的“经典”解决方案。
谢谢, 扎克cp 编辑: 根据Thomas的建议,我在ipython邮件列表上发布了一些关于这个想法的反馈。简而言之,由于一些技术难点,这个想法不是最理想的。对于一个重复性报告,在其中你想要生成markdown单元格和相应的图像/表格,通过iPython内核/浏览器工作比直接使用像Jinja这样的模板系统生成报告更加复杂。
10个回答

26

这里有一份由Fernando Perez提供的Notebook代码片段(链接),演示了如何通过编程方式创建新单元格。请注意,您还可以传入元数据,因此如果您要生成报告并希望将笔记本转换为幻灯片,您可以轻松地指示单元格应该是幻灯片、子幻灯片、片段等。

您可以添加任何类型的单元格,所以现在您想要的很简单(尽管在问题被提出时可能不是这样!)。例如,像这样的东西(未经测试的代码)应该可以工作:

from IPython.nbformat import current as nbf

nb = nbf.new_notebook()

cells = []

for var in my_list:
    # Assume make_image() saves an image to file and returns the filename
    image_file = make_image(var)
    text = "Variable: %s\n![image](%s)" % (var, image_file)
    cell = nbf.new_text_cell('markdown', text)
    cells.append(cell)

nb['worksheets'].append(nbf.new_worksheet(cells=cells))

with open('my_notebook.ipynb', 'w') as f:
        nbf.write(nb, f, 'ipynb')

首行现在可以简化/更新为import nbformat as nbf。行nb = nbf.new_notebook()应更新为nb = nbf.v4.new_notebook()。行cell = nbf.new_text_cell('markdown', text)应更新为cell = nbf.v4.new_markdown_cell(text)。最后的with上下文管理器(最后两行)可以简化/更新为nbf.write(nb, 'my_notebook.ipynb')。(这基本上涵盖了Chris Barnes在下面提到的内容,但使得比较更容易,不需要滚动太多。) - undefined

18

我不会评判它是否是一个好主意,但如果你在笔记本中调用 get_ipython().set_next_input(s),它将创建一个包含字符串s的新单元格。这是IPython内部用于其%load%recall命令的方式。


实际上没有“图像单元格”,但您可以将图像作为代码输出显示。要显示多个图像,请参见IPython.core.display中的Image类和display函数。我认为目前还没有添加Markdown单元格的方法,但如果您在邮件列表中提出此问题,可能会引起兴趣。 - Thomas K
请注意,您不需要使用“set_next_input”来显示图像 - “set_next_input”仅适用于创建带有Python代码的新单元格。 图像应该放在输出单元格中。 - Thomas K
@BND 没有。IPython 不知道笔记本文档,这只是利用最初旨在预填下一个 shell 输入提示的机制。 - Thomas K
set_next_input()函数将添加一个代码单元格,但如何添加新的Markdown单元格? - xingpei Pang
我不知道有任何“内部”的API可以添加一个Markdown单元格。但是,您可以使用IPython显示API来显示Markdown输出。或者,如果您正在从笔记本之外编程地创建/修改笔记本文件,请参阅其他关于nbformat的答案。 - Thomas K
显示剩余3条评论

9
请注意,Tal提供的答案有些过时并且越来越过时了:在ipython v3中,您可以(/应该)直接导入nbformat,之后您需要指定要创建的笔记本的版本。
因此, from IPython.nbformat import current as nbf 变成 from nbformat import current as nbf 变成 from nbformat import v4 as nbf 然而,在这个最终版本中,兼容性会断裂,因为write方法在父模块nbformat中,而Fernando Perez使用的所有其他方法都在v4模块中,尽管其中一些方法具有不同的名称(例如new_text_cell('markdown', source)变成了new_markdown_cell(source))。

这里是v3方式的一个例子:请查看generate_examples.py代码和plotstyles.ipynb输出。在撰写本文时,IPython 4非常新,因此使用Web界面并单击“新笔记本”仍会生成v3笔记本。


3
以下是一个函数的代码,它可以加载文件内容并将其插入到笔记本的下一个单元格中:
from IPython.display import display_javascript

def make_cell(s):
   text = s.replace('\n','\\n').replace("\"", "\\\"").replace("'", "\\'")
   text2 = """var t_cell = IPython.notebook.get_selected_cell()
   t_cell.set_text('{}');
   var t_index = IPython.notebook.get_cells().indexOf(t_cell);
   IPython.notebook.to_code(t_index);
   IPython.notebook.get_cell(t_index).render();""".format(text)
   display_javascript(text2, raw=True)

def insert_file(filename):
   with open(filename, 'r') as content_file:
       content = content_file.read()
   make_cell(content)

可以在我的博客中查看详情。


我正在使用Jupyterlab,并在上面的代码中遇到此错误。 Javascript错误:IPython未定义 - BND

2
from IPython.display import display, Javascript

def add_cell(text,  type='code', direct='above'):
    text = text.replace('\n','\\n').replace("\"", "\\\"").replace("'", "\\'")
    display(Javascript('''
        var cell = IPython.notebook.insert_cell_{}("{}")
        cell.set_text("{}")
        '''.format(direct, type, text)));

for i in range(3):
    add_cell(f'# heading{i}', 'markdown')
    add_cell(f'code {i}')

上面的代码将添加以下单元格: 在此输入图片描述

2
使用魔术命令也可以是另一种解决方案。例如: get_ipython().run_cell_magic(u'HTML', u'', u'<font color=red>heffffo</font>') 现在,您可以在单元格中以编程方式生成 HTML,并按任何希望的方式进行格式化。当然支持图像。如果您想要重复地将输出生成到多个单元格中,请使用上述方法多次并将字符串作为占位符。
附言:我曾经有这个需求并到达了这个线程。那时我想呈现一个表格(不是列表和元组的 ascii 输出)。后来我发现 pandas.DataFrame 非常适合我的工作。它会自动生成 HTML 格式的表格。

1
@xingpei Pang的解决方案很完美,特别是如果您想为每个数据集创建定制代码,例如有几个组。然而,javascript代码的主要问题是,如果您在一个可信任的笔记本中运行此代码,则每次加载笔记本时都会运行它。
我提出的解决方案是在执行后清除单元格输出。javascript代码存储在输出单元格中,因此通过清除输出,代码将消失,不会留下任何东西以便在可信模式下再次执行。通过使用这里的代码,解决方案如下所示。
from IPython.display import display, Javascript, clear_output

def add_cell(text,  type='code', direct='above'):
    text = text.replace('\n','\\n').replace("\"", "\\\"").replace("'", "\\'")
    display(Javascript('''
        var cell = IPython.notebook.insert_cell_{}("{}")
        cell.set_text("{}")
        '''.format(direct, type, text)));

# create cells
for i in range(3):
    add_cell(f'# heading{i}', 'markdown')
    add_cell(f'code {i}')
    

# clean the javascript code from the current cell output
for i in range(10):
    clear_output(wait=True)

请注意,clear_output()需要运行多次以确保输出已清除。

1
作为一个小更新,结合Tal上面的答案Chris Barnes的更新和对nbformat文档的一些挖掘,以下是我使用的代码:
import nbformat
from nbformat import v4 as nbf

nb = nbf.new_notebook()

cells = [
    nbf.new_code_cell(f"""print("Doing the thing: {i}")""")
    for i in range(10)
]

nb.cells.extend(cells)

with open('generated_notebook.ipynb', 'w') as f:
    nbformat.write(nb, f)

你可以启动新的人工笔记本,并将单元格复制粘贴到任何需要它们的地方。
这不太可能是做任何事情的最佳方法,但作为一种不太规范的技巧很有用。
这在以下版本中有效:
Package              Version
-------------------- ----------
ipykernel            5.3.0
ipython              7.15.0
jupyter              1.0.0
jupyter-client       6.1.3
jupyter-console      6.1.0
jupyter-core         4.6.3
nbconvert            5.6.1
nbformat             5.0.7
notebook             6.0.3
...

-1

在 Jupyter 笔记本中运行:

!pip install p2j

然后,使用命令行进入文件所在的相应目录并执行:

python p2j <myfile.py> -t <myfile.ipynb> 

-1

使用命令行进入存放myfile.py文件的目录并执行以下命令(示例): C:\MyDir\pip install p2j

然后执行以下命令: C:\MyDir\p2j myfile.py -t myfile.ipynb


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