将结果写入单独的文本文件并使用\input

将结果写入单独的文本文件并使用\input

我们经常会遇到以下情况:冗长的(数据分析)计算吐出一堆结果值。想象一下 Python 或脚本或 R 脚本进行一些资源密集型计算。然后我们撰写一篇文章或报告,我们想要描述结果并在文本或表格中列出结果值。复制和粘贴值很容易出错,而且工作量很大,所以最好自动包含值

例子:这里的所有数值都是脚本的结果,需要以某种方式包含在文章中:

摘自科学文章的示例

实现这一目标的最佳方法是什么?

笔记:计算可能会持续数小时 - 甚至可能在单独的计算集群上进行。因此,计算结果应该与将结果包含在 LaTeX 文档中并对其进行编译是分开的步骤。

奖金要求:看到 Latex 编辑器(例如 Overleaf)中显示的值而不仅仅是 include 命令可能会很好。当使用 Overleaf 上的所见即所得编辑器时,这可能非常有用,但我怀疑如果不复制和粘贴值或以某种方式预处理 Latex 文件,是否可以做到这一点。

也可以看看:reddit 上的这个问题

答案1

将结果写入单独的文本文件并使用\input

最直接、最简单的解决方案是在计算过程中将每个值写入文本文件。例如,计算可以在最后收集 LaTeX 文档中所需的所有值并将时间写入文本文件:

def write_include_value(name, value):
    """Write a value to a text file for later use in a LaTeX document.
    
    Parameters
    ----------
    name : str
        name of the file, also used in the LaTeX document as name
    value : str
        the value to be stored, passed as string so formatting (e.g. number of 
        digits) needs to be done before calling the function
    
    """
    with open(OUTPUT_DIR / "include-values" / name, "w") as f:
        f.write(value + "\n")

调用函数时可以配置显示的位数:

write_include_value("average_temperature", f"{average_temperature.values:.1f}")

将以下代码片段放在 LaTeX 文档的序言中,可以轻松地将值包含在文本中:

\newcommand*{\includevalue}[1]{\input{../../data/output/include-values/#1}\unskip}

然后可以使用新命令使用该值\includevalue,例如:

\includevalue{average_temperature}

陷阱和缺点

  1. siunitx 包不适用于高级命令\input,因此\includevalue不能在命令内部使用\qty。因此我添加了一个额外的命令来处理带单位的数量:
% The package siunitx does not work with the highlevel command \input, therefore \includevalue
% cannot be used inside of a \qty command. Instead use: \qtyincludevalue{filename}{m/s^2}
% Copied and adapted from here: https://tex.stackexchange.com/a/108093/8964
\def\inputval{0}
\newread\inputFile
\newcommand*{\qtyincludevalue}[3][]{%
  \IfFileExists{../../data/output/data-values/#2}{
    \openin\inputFile=../../data/output/data-values/#2
    \read\inputFile to \inputval
    \closein\inputFile
    \qty[#1]{\inputval}{#3}%
  }{\qty[#1]{#2}{#3}}%
}
  1. 有些期刊在提交过程中会限制文件数量。使用这种通过单独文件包含值的方法意味着您很容易就会有 100 个文件,而提交门户将不允许您提交文章。我使用这个 Python 脚本作为解决方法,将所有包含的内容替换为实际值。这并不好,因为它为 LaTeX 文档的编译增加了一个额外的步骤,这会使事情更容易出错,但它确实有效。
import os
import re
import sys


def replace_placeholders(filename):
    with open(filename, "r") as f:
        contents = f.read()
    pattern = r"\\(qty)?includevalue\{([\w-]+)\}"
    matches = re.findall(pattern, contents)
    for match in matches:
        replace_string = ""
        file_path = os.path.join("data", "output", "data-values", match[1])
        with open(file_path, "r") as f:
            replace_string = f.read().strip()
        if match[0] == "qty":
            replace_string = "\\qty{" + replace_string + "}"
        contents = contents.replace(
            "\\{}includevalue{{{}}}".format(match[0], match[1]), replace_string
        )
    return contents


if __name__ == "__main__":
    print(replace_placeholders(f"{sys.argv[1]}.noreplace"))

  1. 包含值文件的文件夹路径必须指定两次 - 一次在 Python 代码中,然后再次在 LaTeX 标头中。

答案2

如果你使用 R,请使用 Knitr

针织品允许您在 LaTeX 文档中执行 R 代码片段。如果计算结果存储在文件中(也可以是二进制文件,如 NetCDF 或 CSV 文件),则可以使用 Knitr 和 R 代码加载所需的值并将其包含在 LaTeX 文件中:

<<results="asis",echo=FALSE>>=
cat(read.csv("a_csv_file.csv", sep=";")[1,2])
@

或者表格:

<<xtable, results="asis">>=
n <- 100
x <- rnorm(n)
y <- 2*x + rnorm(n)
out <- lm(y ~ x)
library(xtable)
xtable(summary(out)$coef, digits=c(0, 2, 2, 1, 2))
@

(示例取自卡尔·布罗曼

理论上,Knitr 也支持 Python,但使用 R 在 LaTeX 中执行 Python 代码片段感觉很奇怪。所以如果你不使用 R,我建议不要使用 Knitr。

最小示例

\documentclass{article}

\begin{document}

The meaning is:
<<results="asis",echo=FALSE>>=
cat(read.csv("a_csv_file.csv", sep=";")[1,2])
@

\section*{A table}

<<xtable, results="asis",echo=FALSE>>=
n <- 100
x <- rnorm(n)
y <- 2*x + rnorm(n)
out <- lm(y ~ x)
library(xtable)
xtable(summary(out)$coef, digits=c(0, 2, 2, 1, 2))
@

\end{document}

上述代码存储为knitr_test.Rnw并使用以下命令创建一个 tex 文件:

R -e 'library(knitr);knit("knitr_test.Rnw")'

该 tex 文件如下所示:

\documentclass{article}
% knitr inserts a lot of stuff in the header, omitted here for simplicity
\begin{document}

The meaning is:
42

\section*{A table}

% latex table generated in R 4.3.2 by xtable 1.8-4 package
% Wed Jan 17 17:53:32 2024
\begin{table}[ht]
\centering
\begin{tabular}{rrrrr}
  \hline
 & Estimate & Std. Error & t value & Pr($>$$|$t$|$) \\ 
  \hline
(Intercept) & 0.24 & 0.08 & 2.8 & 0.01 \\ 
  x & 2.02 & 0.09 & 21.3 & 0.00 \\ 
   \hline
\end{tabular}
\end{table}

\end{document}

这是渲染的结果:

渲染后的 LaTeX 的屏幕截图

背页

在线 LaTeX背页支持开箱即用的 Knitr。只需将您的 LaTeX 文件重命名为*.Rtex

不幸的是语法检查似乎不支持 Knitr 语法: 包含 Knitr 代码片段的 Overleaf 屏幕截图

缺点

  • 编译 LaTeX 文件时需要执行一个额外步骤。
  • 包含单个值的代码相当长。

更多资源

这是一个简短的教程关于如何将 Knitr 与 LaTeX 结合使用。

背面文件

问答讨论了如何避免使用cat()隐藏输出前缀所必需的[1]

答案3

您正在搜索的概念文学编程,这实际上不仅限于通过 knitr 使用 R 的 LaTeX,而且可能是近年来最成功的概念证明。

但对于一般的文学编程,不限于 R,也不限于 Python,也不限于 LateX,我建议四开本。Quarto 可以使用 Python、R、Julia 和 Observable 创建动态内容,以通过 LaTeX 或 ConTeXt(以及其他几种格式,但这与此无关...)生成 PDF。

如果有任何 R 块,Quarto 将默认使用 Knitr 引擎,但 Jupyter 的可执行代码是另一种语言(python、julia、bash 等)。请参阅这里 了解引擎选择的详细信息。

最后,Knitr 支持 Python 和许多其他语言,而不仅仅是“理论上”。通过 R 执行 Python 并不是什么大罪,只要它有效。此外,这还可以带来一些优势,因为它允许在同一文档中运行两种语言的代码片段,甚至可以将变量从一种语言传递到另一种语言,例如:

姆韦

---
title : A minimal working example
format: pdf
classoption: twocolumn 
header-includes: \columnsep1.5cm
---

```{r}
#| echo: false
library(reticulate)
```


##### This is Python in \LaTeX: 


```{python}
#| echo: false
#| results: asis

import matplotlib.pyplot
import pylab
lst = [11.21,22.84,33.25,44.67,55.35] 
lst2 = [5,6,7,12,24] 
print("This is a plot of list") 
print(lst)
print("and") 
print(lst2)
print(":") 

```


```{python}
#| echo: false
#| fig-cap: Pyhon plot
matplotlib.pyplot.scatter(lst,lst2)
```

\newpage

##### And this R using Python code 

The values  of  python list "`lst`" are
`r knitr::combine_words(py$lst)` with a mean 
of rougly  `r round(mean(py$lst),1)`.


```{r}
#| echo: false
#| results: asis
#| fig.cap: R plot of Python lists
#| fig-height: 4
plot(py$lst,py$lst2,ylab="",
xlab="",col="blue",pch=19)
```

您可以添加engine: jupyter到标题以避免使用 knitr(R 代码将仅显示但不执行)或者简单地删除最后一部分(从到\newpage末尾)以自动切换到 Jupyter,它将运行没有 R 或 knitr 的 python3。

但如果出于某种原因,这种工作流程让你感到困扰,那么还有运行 Python 的 LaTeX 包直接地。

答案4

为 Latex 中的结果生成新的 Latex 命令

您可以创建自己的 Latex 命令:每个结果一个命令。计算管道可以运行一个脚本,该脚本创建一个包含命令定义和结果的 Latex 头文件。然后可以将该文件包含在 Latex 文档中。

这有点类似于通过文本文件解决\input。感谢@Teepeemm 在评论中提出此解决方案的变体。

代码片段 1

在你的计算管道中添加类似这样的内容,并将所有值添加到你的 Latex 文档中所需的字典中:

LATEX_FILE_NAME = 'result_values.tex'

result_values = {}

meaning_of_life = 42
result_values["meaningoflife"] = f"{meaning_of_life:d}"

# - A unit can be passed as string as second parameter: the LaTeX package siunitx will be used to display the quantity.
# - Use .2f for rounding to two decimals.
gravity_ms2 = 9.80665
result_values["gravity"] = f"{gravity_ms2:.2f}", "m/s^2"

write_result_values(result_values, LATEX_FILE_NAME)

代码片段 2

此函数写入 Latex 标头:

import re


def format_latex_command(key, value, unit=None):
    # test if key contains invalid characters for a latex command:
    if not re.match(r"^[a-zA-Z]+$", key):
        raise ValueError(f"Invalid key '{key}': not a valid latex command name")

    if unit is not None:
        value = f"\\qty{{{value}}}{{{unit}}}"

    return f"\\newcommand{{\\{key}}}{{{value}}}"


def write_result_values(result_values, filename):
    """Write the result values to a latex header file creating new Latex command for each value.

    Parameters
    ----------
    result_values : dict
        Results to be written to the Latex header file: keys of the dictionary are the names of the
        latex commands the values are either a single value or a tuple containing the value and the
        unit.
    filename : str
        The name of the Latex header file to write the result values to.

    """
    result_values_flat = [
        (key, *value) if isinstance(value, tuple) else (key, value)
        for key, value in result_values.items()
    ]
    latex_commands = [format_latex_command(*params) for params in result_values_flat]

    with open(filename, "w") as f:
        f.write("\n".join(latex_commands) + "\n")

如何在Latex文档中使用

result_values.tex运行上述代码片段 1 后,将创建一个文件:

\newcommand{\meaningoflife}{42}
\newcommand{\gravity}{\qty{9.81}{m/s^2}}

...然后可以使用新的 Latex 命令将值添加到文本中:

\documentclass{article}
\usepackage{siunitx}

\include{result_values}

\begin{document}

\section*{Example of including result values}

The gravity constant is \gravity. And the meaning of life is \meaningoflife.

\end{document}

渲染结果如下:

渲染的 Latex 示例的屏幕截图

相关内容