在运行整个 IPython notebook 时选择要运行的单元格的简单方法

18

我有一个IPython笔记本,运行数据处理例程中的几个步骤,并在路上保存信息到文件中。这样,在开发代码时(主要在单独的.py模块中),我可以跳过并运行各种步骤。我想设置它,以便我可以Cell -> run all,但只执行某些易于选择的选定步骤。例如,我希望定义我想要在字典中运行的步骤:

process = {
    'load files':False,
    'generate interactions list':False,
    'random walk':True,
    'dereference walk':True,
    'reduce walk':True,
    'generate output':True
}

然后,步骤将基于这个字典运行。顺便说一下,每个步骤包含多个单元格。

我认为%macro不太适合我,因为每次更改任何内容或重新启动内核时,都必须重新定义宏,同时要更改单元格编号。

是否有像%skip%skipto魔法或类似的东西?或者在单元格开头放置一个干净的方式:if process[<current step>]: %dont_run_rest_of_cell


1
我有同样的需求——我使用笔记本作为自动生成报告的模板。我想能够根据某些条件(例如某个输入文件是否存在)定义执行笔记本中哪些部分(即如果提供了此文件,则运行接下来的6个单元格)。这个想法让我想起了C系语言中的#define,#ifdef编译器宏。 - Gordon Bean
8个回答

30
您可以通过自定义内核扩展来创建自己的跳过魔法。

skip_kernel_extension.py

def skip(line, cell=None):
    '''Skips execution of the current line/cell if line evaluates to True.'''
    if eval(line):
        return

    get_ipython().ex(cell)

def load_ipython_extension(shell):
    '''Registers the skip magic when the extension loads.'''
    shell.register_magic_function(skip, 'line_cell')

def unload_ipython_extension(shell):
    '''Unregisters the skip magic when the extension unloads.'''
    del shell.magics_manager.magics['cell']['skip']

在您的笔记本中加载扩展:

%load_ext skip_kernel_extension

在您想要跳过的单元格中运行跳过魔法命令:
%%skip True  #skips cell
%%skip False #won't skip

您可以使用变量来决定是否跳过单元格,使用$符号即可:
should_skip = True
%%skip $should_skip

很酷,但是“自定义内核扩展”应该放在哪里呢? - Arthur
@Arthur:你可以把你的扩展模块放在任何地方,只要它们可以通过Python的标准导入机制进行导入。然而,为了方便编写扩展,你也可以把你的扩展放在IPython目录下的extensions/文件夹中。这个文件夹会自动添加到sys.path中。 - Robbe
改用 get_ipython().run_cell(cell) 而不是 get_ipython().ex(cell)。 - H.C.Chen

8
如果您正在使用nbconvert来执行笔记本,则可以编写自定义预处理器,查看单元格元数据以知道要执行哪些单元格。
class MyExecutePreprocessor(nbconvert.preprocessors.ExecutePreprocessor):

    def preprocess_cell(self, cell, resources, cell_index):
        """
        Executes a single code cell. See base.py for details.
        To execute all cells see :meth:`preprocess`.

        Checks cell.metadata for 'execute' key. If set, and maps to False, 
          the cell is not executed.
        """

        if not cell.metadata.get('execute', True):
            # Don't execute this cell in output
            return cell, resources

        return super().preprocess_cell(cell, resources, cell_index)

通过编辑单元格元数据,您可以指定该单元格是否应该被执行。
您可以通过向笔记本元数据添加主字典来实现更高级的功能。这将类似于您示例中的字典,将部分映射到指定是否调用该部分的布尔值。
然后,在单元格元数据中,您可以使用“section”关键字将其映射到笔记本元数据中的部分ID。
在执行nbconvert时,您可以告诉它使用您的预处理器。
有关更多信息,请参见有关笔记本预处理器的文档

当回答这个相关问题时,我添加了更多的细节:https://dev59.com/clwX5IYBdhLWcg3wrRE5。 - Gordon Bean
感谢@Gordon Bean。编辑单元格元数据的最方便的方法是什么?是否有一种在Jupyter笔记本中使用命令来完成它的方式? - colorlessgreenidea
@colorlessgreenidea - 您可以通过笔记本编辑器编辑元数据。我不确定是否有一种使用命令编辑元数据的方法(尽管这听起来像是一个很棒的问题,可以在SO上提问;)。 - Gordon Bean

5
我刚开始使用Jupyter Notebook并且非常喜欢它。之前听说过IPython,但直到最近的一次咨询工作才认真了解。
我的同事给我介绍了一个技巧,即将代码块从“Code”类型更改为“Raw NBConvert”类型以禁用其执行。这样,我可以在笔记本中添加诊断块,但只有在需要运行时(使它们成为“Code”)才会打开。
虽然这种方法不完全能在脚本中动态选择,但可能适合某些需求。

4

补充Robbe上面说的(我不能评论因为我是新手),如果您不想创建可能会忘记的自定义扩展程序,您可以在第一个单元格中执行以下操作:

def skip(line, cell=None):
    '''Skips execution of the current line/cell if line evaluates to True.'''
    if eval(line):
        return

    get_ipython().ex(cell)

def load_ipython_extension(shell):
    '''Registers the skip magic when the extension loads.'''
    shell.register_magic_function(skip, 'line_cell')

def unload_ipython_extension(shell):
    '''Unregisters the skip magic when the extension unloads.'''
    del shell.magics_manager.magics['cell']['skip']
    
    
load_ipython_extension(get_ipython())

改用 get_ipython().run_cell(cell) 而不是 get_ipython().ex(cell)。 - H.C.Chen

1
你可以使用 nbconvert 和元数据中的标签选项: 在我的情况下,我编辑了单元格元数据:
{
    "deletable": true,
    "colab_type": "code",
    "id": "W9i6oektpgld",
    "tags": [
        "skip"
    ],
    "colab": {},
    "editable": true
}

创建一个名为 preprocess.py 的文件。
from nbconvert.preprocessors import Preprocessor

class RemoveCellsWithNoTags(Preprocessor):
    def preprocess(self, notebook, resources):
        executable_cells = []
        for cell in notebook.cells:
            if cell.metadata.get('tags'):
                if "skip" in cell.metadata.get('tags'):
                    continue
            executable_cells.append(cell)
        notebook.cells = executable_cells
        return notebook, resources

然后导出笔记本:

jupyter nbconvert --Exporter.preprocessors=[\"preprocess.RemoveCellsWithNoTags\"] --ClearOutputPreprocessor.enabled=True --to notebook --output=getting-started-keras-test getting-started-keras.ipynb

0
@Robbe答案后(阅读了定义自定义魔术命令教程),你还可以将其制作成一个带有注册方法的类:
# path/to/my_jupy_pkg.magics.py

from IPython.core.magic import magics_class, line_cell_magic, Magics
from ipywidgets import get_ipython

@magics_class
class SkipMagic(Magics):
    '''
    A class that provides a magic to skip the execution of a cell based on a boolean value.
    '''
    def __init__(self, shell):
        super().__init__(shell=shell)
    
    @line_cell_magic
    def skip(self, line, cell=None):
        '''
        Skips execution of the current line/cell if line evaluates to True.
        
        Parameters
        ----------
        line : str
            The line to evaluate for skipping.
        cell : str, optional
            The cell contents to execute if line evaluates to False.
            
        Returns
        -------
        None
        '''
        if eval(line, self.shell.user_ns):
            return

        if cell:  # Only execute if there's cell content to execute
            get_ipython().ex(cell)


    def load_ipython_extension(self, shell):
        '''
        Registers the skip magic when the extension loads.
        
        Parameters
        ----------
        shell : IPython InteractiveShell
            The IPython shell instance to which the magic should be registered.
            
        Returns
        -------
        None
        '''
        shell.register_magics(self)

    def unload_ipython_extension(self, shell):
        '''
        Unregisters the skip magic when the extension unloads.
        
        Parameters
        ----------
        shell : IPython InteractiveShell
            The IPython shell instance from which the magic should be unregistered.
            
        Returns
        -------
        None
        '''
        del shell.magics_manager.magics['cell']['skip']
        del shell.magics_manager.magics['line']['skip']


    @classmethod
    def register(cls) -> 'SkipMagic':
        ipy = get_ipython()
        ins = cls(shell=ipy)
        ipy.register_magics(ins)
        return ins

如以前一样,可以导入并运行:
from my_jupy_pkg.magics import SkipMagic
mgc = SkipMagic.register()

0

明确总比含蓄好。简单总比复杂好。那么为什么不使用纯Python呢?

每步只需一个单元格,您就可以做到:

if process['load files']:
    load_files()
    do_something()

并且

if process['generate interactions list']:
    do_something_else()

如果您想在跳过特定步骤时停止执行,可以使用以下方法:
if not process['reduce walk']:
    stop
else:
    reduce_walk()
    ...

stop不是一个命令,所以在使用Cell -> Run all时会生成一个异常并停止执行。

你还可以进行条件步骤,如:

if process['reduce walk'] and process['save output']:
    save_results()
    ...

但是,作为一个经验法则,我不会制定比那更复杂的条件。


0

或者从另一个角度来看,您可以跳过不想运行的单元格(例如,在需要跳过的单元格的第一行添加以下代码)。

%%script echo skipping

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