
Wir befinden uns häufig in der folgenden Situation: eine langwierige (Datenanalyse)Berechnungspuckt einen Haufen ausErgebniswerte. Stellen Sie sich ein Python- oder R-Skript vor, das einige ressourcenintensive Berechnungen durchführt. Wir schreiben dann einen Artikel oder Bericht, in dem wir die Ergebnisse beschreiben und die Ergebniswerte im Text oder in Tabellen auflisten möchten. Das Kopieren und Einfügen der Werte ist fehleranfällig und viel Arbeit, daher wäre es gut,automatisch die Werte einschließen.
Beispiel:alle hier angegebenen Zahlenwerte sind Ergebnisse aus Skripten und müssen auf die eine oder andere Weise in den Artikel einfließen:
Wie lässt sich dies am besten erreichen?
Notiz:Die Berechnung kann mehrere Stunden dauern – möglicherweise sogar auf einem separaten Rechencluster. Die Berechnung der Ergebnisse sollte daher ein getrennter Schritt sein vom Einfügen der Ergebnisse in das LaTeX-Dokument und dem Kompilieren.
Bonusvoraussetzung:Es wäre vielleicht schön, die Werte im Latex-Editor (z. B. Overleaf) angezeigt zu bekommen und nicht nur den Include-Befehl. Das könnte sehr hilfreich sein, wenn man einen Wysiwyg-Editor wie den von Overleaf verwendet, aber ich bezweifle, dass man das machen kann, ohne die Werte zu kopieren und einzufügen oder die Latex-Datei irgendwie vorzuverarbeiten.
Siehe auch:diese Frage auf Reddit.
Antwort1
Schreiben Sie die Ergebnisse in separate Textdateien und verwenden Sie\input
Die direkteste und einfachste Lösung besteht darin, die einzelnen Werte während der Berechnung in eine Textdatei zu schreiben. Die Berechnung könnte beispielsweise am Ende alle im LaTeX-Dokument benötigten Werte sammeln und diese dann in Textdateien schreiben:
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")
Beim Aufruf der Funktion kann die Anzahl der anzuzeigenden Ziffern konfiguriert werden:
write_include_value("average_temperature", f"{average_temperature.values:.1f}")
Durch Einfügen des folgenden Code-Schnipsels in die Präambel des LaTeX-Dokuments lässt sich der Wert ganz einfach in den Text einfügen:
\newcommand*{\includevalue}[1]{\input{../../data/output/include-values/#1}\unskip}
Den Wert kann man dann mit dem neuen Befehl übernehmen \includevalue
, zum Beispiel:
\includevalue{average_temperature}
Fallstricke und Nachteile
- Das Paket siunitx funktioniert nicht mit dem Highlevel-Kommando
\input
und kann daher\includevalue
nicht innerhalb eines\qty
Kommandos verwendet werden. Daher habe ich ein zusätzliches Kommando für Mengen mit Einheiten hinzugefügt:
% 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}}%
}
- Einige Zeitschriften begrenzen die Anzahl der Dateien während des Übermittlungsprozesses. Wenn Sie diese Methode verwenden, bei der Werte über separate Dateien eingefügt werden, können Sie leicht 100 Dateien haben und das Übermittlungsportal lässt Sie Ihren Artikel nicht übermitteln. Ich habe dieses Python-Skript als Workaround verwendet, um alle Includes durch die tatsächlichen Werte zu ersetzen. Das ist nicht schön, weil es der Kompilierung des LaTeX-Dokuments einen zusätzlichen Schritt hinzufügt, was die Fehleranfälligkeit erhöht, aber es funktioniert.
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"))
- Der Pfad zum Ordner mit den Include-Wertdateien muss zweimal angegeben werden – einmal im Python-Code und dann erneut im LaTeX-Header.
Antwort2
Verwenden Sie Knitr, wenn Sie R verwenden
Strickenermöglicht Ihnen die Ausführung von R-Code-Schnipseln in einem LaTeX-Dokument. Wenn die Berechnung Ergebnisse in Dateien speichert (kann auch eine Binärdatei wie NetCDF oder eine CSV-Datei sein), können Sie Knitr und R-Code verwenden, um den benötigten Wert zu laden und in die LaTeX-Datei einzufügen:
<<results="asis",echo=FALSE>>=
cat(read.csv("a_csv_file.csv", sep=";")[1,2])
@
Oder eine Tabelle:
<<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))
@
(Beispiel entnommen ausKarl Broman)
Theoretisch unterstützt Knitr auch Python, aber es fühlt sich komisch an, R zu verwenden, um Python-Snippets in LaTeX auszuführen. Ich würde also von Knitr abraten, wenn Sie nicht R verwenden.
Minimales Beispiel
\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}
Der obige Code wird wie folgt gespeichert knitr_test.Rnw
und eine Tex-Datei wird erstellt:
R -e 'library(knitr);knit("knitr_test.Rnw")'
Die Tex-Datei sieht folgendermaßen aus:
\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}
Dies ist das gerenderte Ergebnis:
Umseitig
Das Online-LaTeXUmseitigunterstütztKnitr sofort einsatzbereit. Benennen Sie Ihre LaTeX-Datei einfach um in *.Rtex
.
Leider scheint die Syntaxprüfung die Knitr-Syntax nicht zu unterstützen:
Nachteile
- Ein zusätzlicher Schritt beim Kompilieren der LaTeX-Datei ist notwendig.
- Der Code zum Einbinden eines einzelnen Wertes ist ziemlich lang.
Mehr Ressourcen
Hier ist ein schönes und kurzes Tutorialzur Verwendung von Knitr mit LaTeX.
Eine gute Übersicht zu Knitr gibt es imDokumentation von Rückseite.
DasFragen und Antwortenerläutert, wie die Verwendung von vermieden werden kann cat()
, was zum Verbergen des Ausgabepräfixes erforderlich ist [1]
.
Antwort3
Sie suchen den Begriff vonkompetente Programmierung, das zwar nicht auf LaTeX mit R durch knitr beschränkt ist, aber wahrscheinlich der erfolgreichste Proof of Concept der letzten Jahre war.
Aber für allgemeines literate programming, nicht beschränkt auf R, Python und nicht beschränkt auf LateX, schlage ich vorQuarto. Quarto kann mit Python, R, Julia und Observable dynamische Inhalte erstellen, um ein PDF über LaTeX oder ConTeXt (und mehrere andere Formate, aber das ist hier kein Thema ...) zu produzieren.
Quarto verwendet standardmäßig die Knitr-Engine, wenn ein R-Block vorhanden ist, aber Jupyter ist der ausführbare Code in einer anderen Sprache (Python, Julia, Bash usw.). SieheHier für Einzelheiten zur Motorauswahl.
Schließlich unterstützt Knitr Python undviele weitere Sprachen, nicht nur „in der Theorie“. Es ist keine Todsünde, Python über R auszuführen, solange es funktioniert. Darüber hinaus könnte dies einige Vorteile haben, da es ermöglicht, Snippets beider Sprachen im selben Dokument auszuführen und sogar Variablen von einer Sprache in die andere zu übergeben, z. B.:
---
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)
```
Sie können engine: jupyter
dem Header etwas hinzufügen, um die Verwendung von knitr zu vermeiden (der R-Code wird nur angezeigt, aber nicht ausgeführt) oder einfach den letzten Teil (von \newpage
bis zum Ende) entfernen, um automatisch zu Jupyter zu wechseln, das Python3 ohne R oder knitr ausführt.
Aber wenn dieser Workflow Sie aus irgendeinem Grund stört, gibt es auchLaTeX-Pakete zum Ausführen von Pythondirekt.
Antwort4
Generieren Sie neue Latex-Befehle für Ergebnisse in einem Latex
Sie können Ihre eigenen Latex-Befehle erstellen: einen Befehl für jedes Ergebnis. Die Berechnungspipeline kann ein Skript ausführen, das eine Latex-Headerdatei mit den Befehlsdefinitionen und den Ergebnissen erstellt. Diese Datei kann dann in das Latex-Dokument eingefügt werden.
Dies ähnelt in gewisser Weise demLösung über Textdateien und\input
. Danke an @Teepeemm für den Vorschlag dieser Lösungsvariante in den Kommentaren.
Codeausschnitt 1
Fügen Sie Ihrer Berechnungspipeline etwas wie das Folgende hinzu und fügen Sie dem Wörterbuch alle Werte hinzu, die Sie in Ihrem Latex-Dokument benötigen:
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)
Codeausschnitt 2
Diese Funktion schreibt den Latex-Header:
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")
Verwendung im Latex-Dokument
Nach dem Ausführen des obigen Codeausschnitts 1 result_values.tex
wird eine Datei erstellt:
\newcommand{\meaningoflife}{42}
\newcommand{\gravity}{\qty{9.81}{m/s^2}}
...man kann dann die neuen Latex-Befehle verwenden, um die Werte zum Text hinzuzufügen:
\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}
Das gerenderte Ergebnis sieht folgendermaßen aus: