將結果寫入單獨的文字檔案並使用\input

將結果寫入單獨的文字檔案並使用\input

我們經常會遇到以下情況:冗長(數據分析)計算吐出一堆結果值。想像 Python 或腳本或 R 腳本正在執行一些資源密集型計算。然後,我們撰寫一篇文章或報告,在其中描述結果並在文字或表格中列出結果值。複製和貼上這些值很容易出錯且工作量很大,因此最好自動包含值

例子:這裡的所有數值都是腳本的結果,需要以某種方式包含在文章中:

摘自科學文章的範例

實現這一目標的最佳方法是什麼?

筆記:計算可能會運行幾個小時——甚至在單獨的計算集群上也是可能的。因此,計算結果應該是將結果包含在 LaTeX 文件中並對其進行編譯的分離步驟。

獎金需求:看到 Latex 編輯器(例如 Overleaf)中顯示的值而不僅僅是 include 命令可能會很高興。當使用像 Overleaf 上的 Wysiwyg 編輯器時,這可能非常有幫助,但我懷疑是否可以在不複製和貼上值或以某種方式預處理 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,請使用 Knit

針織品允許您在 LaTeX 文件中執行 R 程式碼片段。如果計算結果儲存在檔案中(也可以是二進位文件,如 NetCDF 或 CSV 檔案),您可以使用 Knit 和 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))
@

(範例取自卡爾布羅曼

理論上,Knit 也支援 Python,但在 LaTeX 中使用 R 執行 Python 片段感覺很奇怪。因此,如果您不使用 R,我建議不要使用 Knit。

最小的例子

\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背頁支援針織開箱即用。只需將您的 LaTeX 檔案重新命名為*.Rtex.

不幸的是,語法檢查似乎不支援 Knit 語法: Overleaf 的螢幕截圖,包含 Knit 程式碼片段

缺點

  • 編譯 LaTeX 檔案期間需要執行額外的步驟。
  • 包含單一值的代碼相當長。

更多資源

這是一個簡短的教程關於如何將 Knit 與 LaTeX 結合使用。

背面的文檔

問答討論如何避免使用cat()隱藏輸出前綴所必需的[1]

答案3

您正在搜尋的概念識字程式設計,這實際上不限於帶有 R 槽 knit 的 LaTeX,但可能是近年來最成功的概念證明。

但對於一般的文學編程,不限於 R,也不限於 Python,也不限於 LateX,我建議四開本。 Quarto 可以使用 Python、R、Julia 和 Observable 創建動態內容,以透過 LaTeX 或 ConTeXt(以及其他幾種格式,但這不是這裡的主題...)產生 PDF。

如果有任何 R 區塊,Quarto 將預設使用 Knit 引擎,但 Jupyter 是另一種語言(python、julia、bash 等)的可執行程式碼。看這裡 了解引擎選型的詳細資訊。

最後,Knit 支援 Python 和許多其他語言,不只是「理論上」。在 R 運行時透過 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到標頭以避免使用 knit(R 程式碼將僅顯示但不會執行),或者只需刪除最後一部分(從\newpage到最後)以自動切換到Jupyter,它將在沒有R 和knit 的情況下運行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 範例的螢幕截圖

相關內容