为论文编写代码以便读者可以清楚地将结果与生成它们的代码相匹配的最有用的方法是什么?

计算科学 Python 软件 再现性
2021-12-02 00:41:25

我正在写一篇可重复的论文,该论文具有由 Python 脚本生成的计算结果(类似的 MATLAB 脚本生成几乎相同的结果)。我觉得如果读者能够将论文中的计算与代码中的计算相匹配,那么这篇论文对于读者来说会更容易理解。该作品提出了一种抽象的形式主义,论文中的示例旨在使这种形式主义对读者(其中许多将是工程师)更加具体;该代码可能是如何执行计算的最详细记录,并且清楚地表明可以在审查过程中帮助我们。

有没有人对如何使代码和计算结果(数字、方程)之间的对应关系更清晰有任何建议?

例如,我在想,当涉及到实现论文中各个步骤的代码行时,我可以引用方程式编号(如果我可以在代码和 LaTeX 之间交叉引用,那就太棒了,但是手工标记它们很好) ,我可以编写对应于各种示例和图形的函数,例如

def example_1():
    # Insert code corresponding to first example
    pass

def figure_1():
    # Insert code that generates Figure 1
    pass

如果代码很大,而且我不想解释工程中使用的一堆不同的数学方法实际上是如何相同的,我可能不会费心使代码清晰,但考虑到代码的抽象性质论文和小型代码库,似乎这个练习可能有价值。

4个回答
  1. 您可以考虑在Noweb中撰写整篇论文。设置起来有点乏味,但它是混合代码和 LaTeX 格式的文本、方程式和图形的一种非常强大的方式。对于长程序,它倾向于将你的代码变成一本书而不是一篇文章,但对于短程序,它可能会很好地工作。

  2. 如果您不想走那么远,使用 LaTeX 格式化代码清单的注释部分仍然应该相当简单。listings软件包可以帮助您完成此任务。这是一个简短的示例:

\documentclass{文章}
\使用包{amsmath}
\使用包{列表}
\开始{文档}
\开始{方程}
  \标签{当量:一个}
  轴=b
\end{方程}
\begin{lstlisting}[escapechar=\%]
  # 注释引用方程式%~\eqref{eq:one}%
  定义 f(a):
    返回一个+1
\end{lstlisting}
\结束{文档}

通过一些额外的操作,您应该能够让您引用的方程式编号出现在它用于列出方程式的等宽字体中。

Bill 提到的 noweb 方法已经有了很大的发展,无论是在它的原始精神上,在literate programming术语下记录代码(而不是科学出版物) ,现在有多种风格(我猜 noweb 最初是 cweb 的概括),它doxygen和各种特定语言的版本可以生成 TeX、HTML 和其他格式的文档。

更重要的是,noweb 已经在R社区中开发了一段时间(最初是S社区,因此得名),标题为“Sweave”,目的是提供“可重复的研究”论文,其中代码实际运行时乳胶文件被编译(并且也可以选择显示)。相当多的学术论文是用 Sweave 写的(包括,我相信,所有的 R-journal;但另见生物统计学杂志及其可重复论文的政策)。

虽然 Sweave 仍然是任何基本 R 安装的一部分,但它正在被knitr取代,它现在与语言无关,使其成为 Python 代码的可能选择。Knitr 支持用 LaTeX 或 markdown 编写,支持语法高亮、缓存、从源 Latex 中外部化代码以及许多其他此类工作所需的功能。

Python 有自己的类似解决方案ipython notebooks,它可以呈现为 HTML,也许是 LaTeX,但我对此知之甚少。

另一个绝对值得一看的项目是dexyit,这是另一个与 LaTeX 和 HTML 配合得非常好的与语言无关的程序。虽然它在记录代码方面的示例比在撰写科学论文方面的示例更多,但在 LaTeX 中工作应该是直截了当的。

两者都knitrdexyit完全按照您在 LaTeX 中的描述进行操作,包括指向外部 python 脚本和读取代码。类似的事情可以在 DocBook 和 XML 中完成,尽管我对这种方法不太熟悉。

LaTeX 包minted提供了非常广泛的语法突出显示(基于 Pygments)并允许双向交叉引用。您可以从代码部分(铸造部分)中转义到 LaTeX,并且可以在正文中引用代码行。最重要的是,它提供了一个列表环境,以便您可以生成“列表列表”(如表格列表)并允许引用整个列表。请参阅下面的 LaTeX MWE 及其与 LuaLaTeX 的输出(不要判断代码:-))。

另一种选择是使用来自同一作者/维护者的PythonTeX,它允许在编译 LaTeX 源代码的同时运行计算,因此论文和代码结果总是一起生成,因此总是连贯的。在此处查看PythonTeX 库

\documentclass[a4paper,notitlepage,11pt]{article}

\usepackage{amsmath}
\usepackage{cases}
\usepackage{minted}

\begin{document}

The mathematical definition of the Fibonacci
series is given in~Equations~(\ref{eq:fibdef:init1}--\ref{eq:fibdef:rule})
It can be implemented using either a recursive or iterative algorithm
in Python.

\begin{numcases}{f(n)=}
  \label{eq:fibdef}
    0               & n = 0 \label{eq:fibdef:init1}\\
    1               & n = 1 \label{eq:fibdef:init2}\\
    f(n-1) + f(n-2) & \text{otherwise} \label{eq:fibdef:rule}
\end{numcases}

The algorithms below are an implementation of both variants.
Listing~\ref{alg:fib_recursive} shows the recursive variant (see
line~\ref{alg:fibo_rec:line_rec} in listing~\ref{alg:fib_recursive}) while
listing~\ref{alg:fib_iterative} shows the iterative variant. Both can be
optimized, of course.

\begin{listing}[ht]
  \begin{minted}[linenos, escapeinside=||]{python}
def fibo_rec(N):
    if N == 0:
        result = 1 |[Comment: See case (\ref{eq:fibdef:init1})]|
    elif N == 1:
        result = 1 |[Comment: See case (\ref{eq:fibdef:init2})]|
    else:
        result = fibo_rec(N-1) + fibo_rec(N-2) |\label{alg:fibo_rec:line_rec}[Comment: See case (\ref{eq:fibdef:rule})]|

    return result
  \end{minted}
\caption{Fibonacci recursive}
\label{alg:fib_recursive}
\end{listing}

\begin{listing}[ht]
  \begin{minted}[linenos, escapeinside=||]{python}
def fibo_iter(N):
    if N == 0:
        fib_N = 1
    elif N == 1:
        fib_N = 1
    else:
        fib_Nmin2 = 1
        fib_Nmin1 = 1
        for i in range(2,N+1):
            fib_N = fib_Nmin2 + fib_Nmin1
            fib_Nmin2 = fib_Nmin1
            fib_Nmin1 = fib_N
    return fib_N
  \end{minted}
\caption{Fibonacci iterative}
\label{alg:fib_iterative}
\end{listing}

\end{document}

在此处输入图像描述

使用org-mode的文学编程功能。

大多数 org-mode 用户倾向于专注于内置项目/时间管理功能或将文档从易于维护的文本文件导出为多种流行文件格式的能力,例如PDF

然而,org-mode 的最佳特性是能够创建超过 30 种语言的识字程序,并且开源社区每月都会添加更多语言。

下面是使用 Ruby 和 Python 的简单代码示例:

 #+NAME: trivial-code-ex1
 #+BEGIN_SRC ruby 
   "hello world!"
 #+END_SRC

 #+RESULTS: trivial-code-ex1
 : hello world!
 
 
 #+NAME: func-of-x-and-y
 #+BEGIN_SRC python :var x=1 :var y=2 :session
   x + y
 #+END_SRC

 #+RESULTS: func-of-x-and-y
 : 3

优点

  • 支持超过30 种编程语言,包括 R、Python、Ruby、Perl、C、C++、Java、Clojure、Javascript、Common Lisp、Shell、SQL...

  • 的能力:

    • 捕获 SRC块结果作为输出和/或值。
    • 将块结果格式化 SRC为代码、列表、表格、乳胶、html
    • SRC对块的变量同时使用外部和内部数据。
    • 使用命名SRC块的输出到SRC块中作为变量。
    • 在块 内使用noweb语法。SRC

      专业提示:您可以使用noweb语法:

      • 从命名SRC块插入代码,例如func-of-x-and-y,在另一个SRC块内。

        #+BEGIN_SRC python :session :noweb yes 
          x=2
          y=3
          "f(x,y) is\n\n <<func-of-x-and-y>> \n\nso f({0},{1}) equals\n\n {2}".format(x,y,<<func-of-x-and-y>>)
        #+END_SRC
        
        #+RESULTS:
        : f(x,y) is
        : 
        :  x + y 
        : 
        : so f(2,3) equals
        : 
        :  5
        
      • 插入命名SRC块的结果,例如func-of-x-and-y在另一个SRC块内

        #+BEGIN_SRC python :session :noweb yes 
          "f(x,y) is\n\n <<func-of-x-and-y>> \n\nso f(3,4) equals\n\n <<func-of-x-and-y(x=3,y=4)>>"
        #+END_SRC
        
        #+RESULTS:
        : f(x,y) is
        : 
        :  x + y 
        : 
        : so f(3,4) equals
        : 
        :  7
        
      • 将命名SRC块放在 org-mode 文件中的任何位置以提高可读性,并使用:tangle标头或将代码导出到外部源文件中。

  • 开源项目——既像啤酒一样免费,又像自由一样免费。

  • 文本文件格式适用于 git 等版本控制软件。

  • 由于这个答案越来越长,我不会介绍其他功能。

缺点

  • 需要安装并配置 gnu emacs 以使用 org-mode。

    注意:大多数人在阅读 gnu emacs 后停止阅读此答案。对于剩下的勇敢的灵魂,您可以使用您最喜欢的文本编辑器,只需调用 emacs 从命令行处理您的 org-mode 文件。

  • 需要安装和配置您需要的所有编程软件。

  • 如果要制作 PDF,需要安装和配置 LaTeX 实用程序。

  • org-mode 没有那么广为人知,ipython notebooks因此Sweave即使在 2008 年添加了 Literate Programming 功能,您也可能不会看到那么多的职位发布。

  • 学习组织模式标记语法

  • 如果想充分利用与 org-mode 配合使用的其他酷工具,可能会学习如何使用 gnu emacs 或 spacemacs。


全面披露:我是Atom 编辑器的 org-mode包的主要维护者。


此答案中的代码已使用:
emacs version: GNU Emacs 25.2.1
org-mode version: 9.1.2进行验证