結果を別のテキストファイルに書き込んで使用する\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 スクリプトを使用して、すべての include を実際の値に置き換えました。これは、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 コードで 1 回、LaTeX ヘッダーでもう 1 回、合わせて 2 回指定する必要があります。

答え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 もサポートしていますが、LaTeX 内で R を使用して 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 で使用する方法について説明します。

Knitrの素晴らしい概要は、裏表紙の文書

これ質疑応答cat()出力プレフィックス を非表示にするために必要な の使用を回避する方法について説明します[1]

答え3

あなたは次の概念を検索していますリテラシープログラミングこれは実際にはknitrを介したRを使用したLaTeXに限定されませんが、おそらく過去数年間で最も成功した概念実証でした。

しかし、RやPython、LateXに限らず、一般的な文芸プログラミングには、四つ折りQuarto は、Python、R、Julia、Observable を使用して動的コンテンツを作成し、LaTeX または ConTeXt (およびその他のいくつかの形式がありますが、ここでは話題になりません...) を介して PDF を生成できます。

Quartoは、Rチャンクがある場合にはデフォルトでKnitrエンジンを使用しますが、実行可能コードは別の言語(python、julia、bashなど)のJupyterです。ここ エンジン選択の詳細については、こちらをご覧ください。

最後に、KnitrはPythonをサポートしており、他の多くの言語「理論上」だけではありません。Python を R 経由で実行することは、動作する限り大罪ではありません。さらに、これにはいくつかの利点があります。同じドキュメントで両方の言語のスニペットを実行でき、ある言語から別の言語に変数を渡すこともできます。例:

ムウェ

---
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)
```

ヘッダーに追加して knitr の使用を避けることもできますengine: jupyter(R コードは表示されるだけで実行されません)。または、最後の部分 (から\newpage最後まで) を削除するだけで、R も knitr も使用せずに python3 を実行する Jupyter に自動的に切り替えることができます。

しかし、このワークフローが何らかの理由で煩わしい場合は、Pythonを実行するためのLaTeXパッケージ直接。

答え4

Latexの結果に対して新しいLatexコマンドを生成する

独自の Latex コマンドを作成できます。各結果に対して 1 つのコマンドです。計算パイプラインは、コマンド定義と結果を含む 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文書での使い方

上記のコード スニペット 1 を実行すると、次のファイルresult_values.texが作成されます。

\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 の例のスクリーンショット

関連情報