在我的研究中,我用 R 进行分析,用 LaTeX 撰写论文。为了避免在分析发生变化时更新论文中的值,我直接从 R 更新论文中的值。
假设RI中有变量foo <- bar
。
现在,我在 LaTeX 中有一个占位符,其中有一组括号表示foo
值,然后是一个标签,以便 R 可以找到正确的括号来放置值。
其中一个例子foo <- bar
可能是:
Lorem ipsum dolor sit amet {}\label{R.foo}, consectetur adipiscing elit.
因此,R 会找到一个预定义标签(在本例中为R.foo
),然后将第一个括号内的内容替换为foo
的值(此处为bar
)。因此,在 R 运行后,我们将得到:
Lorem ipsum dolor sit amet {bar}\label{R.foo}, consectetur adipiscing elit.
问题是,我们会得到多个定义的标签(因为\label{R.foo}
R 处理完成后文件中仍留有多个位置),我的 LaTeX 编辑器会对此发出警告。因此,我尝试创建一个完全没有 LaTeX 功能的替代方案 — 它应该只是 R 可以搜索的东西。
答案1
我会让 R 创建一个values.tex
包含内容的不同文件
\newcommand{\Rfoo}{bar}
然后你的主文件就会有
\input{values}
%\providecommand{\Rfoo}{} % avoid an error if \Rfoo wasn't defined
...
Lorem ipsum dolor sit amet \Rfoo, consectetur adipiscing elit.
答案2
方法 1:
正如 Fran 所建议的使用针织品。 有针织品从 .Rnw 文件或 .Rtex 文件为您创建 .tex 文件,其中 R 代码块和 TeX 代码合并,并且在创建 .tex 文件时,执行 R 代码块的结果将放入 .tex 文件中。(knitr 可与在线 TeX 平台配合使用背面如果要编译的文件没有扩展名“.tex”但有扩展名“.Rtex”。)
使用以下 knitr 示例(必须以文件扩展名“.Rtex”保存),R 代码块计算勾股数:
<<templates, include=FALSE, cache=FALSE, echo=FALSE, results='asis'>>=
knitr::opts_template$set(
JustResult = list(include=TRUE, cache=FALSE, echo=FALSE, results='asis')
)
PrintTriple <- function(k, s, t) {
return(cat(
"With \\(k=", k, "\\) and \\(s=", s, "\\) and \\(t=", t, "\\) you get \\(a=", (k*2*s*t), "\\), \\(b=", (k*((s*s)-(t*t))),
"\\) and \\(c=", (k*((s*s)+(t*t))), "\\) and \\mbox{\\(a^2+b^2=c^2\\leftrightarrow(",
(k*2*s*t),")^2+(",(k*((s*s)-(t*t))),")^2=(",(k*((s*s)+(t*t))),")^2","\\)}."
, sep="", fill=FALSE
))
}
@
\documentclass{article}
\usepackage{amsmath, amssymb}
\DeclareMathOperator{\ggd}{ggd}
\parindent=0ex
\parskip=\medskipamount
\begin{document}
Use the following recipe to calculate triples
\((a, b, c)\) where the condition \(((a^2+b^2=c^2)\land(\{a,b,c\}\subset\mathbb{N})\land((a\equiv0\pmod{2}))\) is fulfilled---such triples are called \textbf{Pythagorean triples}:
Choose natural numbers \(k,s,t\); \(s>t\) and set:
\begin{equation*}
\begin{aligned}
a &\longleftarrow k2st\\
b &\longleftarrow k(s^2-t^2)\\
c &\longleftarrow k(s^2+t^2)
\end{aligned}
\end{equation*}
(If and only if \((k=1)\land(\gcd(s,t) = 1)\land(s\not\equiv t \pmod{2})\), then \((a, b, c)\) is called a \textbf{primitive} Pythagorean triple.)
\noindent
<<, opts.label='JustResult' >>=
PrintTriple(2, 4, 3)
@
\noindent
<<, opts.label='JustResult' >>=
PrintTriple(2, 3, 1)
@
\noindent
<<, opts.label='JustResult' >>=
K=1
S=2
T=1
PrintTriple(K, S, T)
@
\noindent
<<, opts.label='JustResult' >>=
K=3*K
S=2
T=1
PrintTriple(K, S, T)
@
\noindent
<<, opts.label='JustResult' >>=
S=3*S
T=3*T
PrintTriple(K, S, T)
@
\end{document}
方法 2:
也许你的 R 程序可以编写一个ValuesCalculatedByR.tex
包含以下内容的.tex 文件
cathetus1={3,3},
cathetus2={4,4},
hypotenuse={5,5},
即以逗号分隔的模式列表⟨标识符⟩={⟨计算值⟩}。
(花括号中的⟨计算值⟩s 可以省略,如果⟨计算值⟩s 本身不包含逗号,如果你不介意周围的空格⟨计算值⟩正在被移除。)
并且 .tex 文件可以从中创建一个 expl3 属性列表并定义一个命令,以便\command{<identificator>}
产生 <calculated value>
:
% I don't have R available right now for creating the file, thus I let the filecontents*-
% environment do that for me:
\begin{filecontents*}{ValuesCalculatedByR.tex}
cathetus1={3,3},
cathetus2={4,4},
hypotenuse={5,5},
FirstTokenIsExpandable=\LaTeX\space is funny and the value is defined.,
\end{filecontents*}
% The <value> "\LaTeX\space is funny and the value is defined."
% of the <identificator> "FirstTokenIsExpandable" does not
% contain comma, thus surrounding curly braces can be omitted.
\ExplSyntaxOn
% -------------------------------------------------------------------------------------------------------
% \ImportIDValueList{<.tex-input-file containing list of pattern <identificator>={<calculated value>} >}
% {<command for retrieving <calculated value> associated to <identifictor> >}
% -------------------------------------------------------------------------------------------------------
\cs_new_protected:Npn \ImportIDValueList #1#2 {
% #1 = name of .tex-input-file created by the program R
% #2 = to-be-defined command for retrieving calculated value
\exp_args:Nno \use:n
{ \exp_args:Nno \use:n { \__Christian_ImportIDValueList:nn{#1} } }
{ \cs_to_str:N #2 }
}
\cs_new:Nn \__Christian_ImportIDValueList:nn{
% #1 = name of .tex-input-file created by the program R
% #2 = name of to-be-defined command for retrieving calculated value
\cs_new_protected:cpn { #2 } ##1 {
\exp:w \__Christian_ImportValue_GetValue:nnnn { #1 } { Christian_ImportValue_#2_prop } {##1} {
\msg_error:nnnnn { Christian_ImportValue } { not-available } { #1 } { ##1 }
\mbox{\textsf{?##1?}}
}
}
\cs_new:cpn { #2Expandable } ##1 ##2 {
\exp:w \__Christian_ImportValue_GetValue:nnnn { #1 } { Christian_ImportValue_#2_prop } {##1} { ##2 }
}
\file_get:nnN {#1} {} {\l_tmpa_tl}
\prop_gclear_new:c { Christian_ImportValue_#2_prop }
% Can't use \prop_gset_from_keyval:Nn because ~ needs to be before each <value> as
% - unlike unexpandable x-expansion of \prop_item:cn - expandable f-expansion of \prop_item:cn
% won't stop when \prop_item:cn has delivered the result and therefore
% must be stopped by a ~ that gets gobbled by the f-expansion-
% routine, which, I guess, is s.th. like "\romannumeral-`a"
% so that expansion during parsing the <number>-quantity
% continues until it is clear whether a to-be-discarded <optional
% space> follows the tokens "-`a" denoting the negative number -97
% while \romannumeral silently removes tokens denoting TeX-
% <number>-quantities denoting non-positive values without
% returning any tokens at all.
\exp_args:NnV \use:n {
\keyval_parse:nnn
{ \__Christian_ImportValue_pass_iii_ii_to_i {\__Christian_ImportValue_propset:nnn { Christian_ImportValue_#2_prop }}{} }
{ \__Christian_ImportValue_propset:nnn { Christian_ImportValue_#2_prop } }
} { \l_tmpa_tl }
}
\cs_new:Nn \__Christian_ImportValue_pass_iii_ii_to_i:nnn { #1 {#3} {#2} }
\cs_new:Nn \__Christian_ImportValue_propset:nnn {
\prop_gput:cnn {#1} {#2} {~#3}
}
\cs_new:Nn \__Christian_ImportValue_GetValue:nnnn {
% #1 = name of .tex-input-file created by the program R
% #2 = name of property list
% #3 = value to be retrieved from of property list
% #4 = tokens in case property is undefined
\prop_if_in:cnTF { #2 } { #3 }
{ \exp_args:Nnf \use:nn {\exp_end:} { \prop_item:cn { #2 }{ #3 } } }
{ \exp_end: #4 }
}
\msg_new:nnnn { Christian_ImportValue } { not-available }
{ Value~for~identifier~"#2"~could~not~be~retrieved }
{ Seems~the~file~"#1"~does~not~have~"#2=...,"}
% -------------------------------------------------------------------------------------------------------
\ExplSyntaxOff
\documentclass{article}
\ImportIDValueList{ValuesCalculatedByR.tex}{\GetRValue}%
% The above
% - makes a property-list of name "Christian_ImportValue_GetRValue_prop"
% from the content of the file ValuesCalculatedByR.tex and
% - defines a command \GetRValue{<property>} to check if <property> is defined
% in the property-list "Christian_ImportValue_GetRValue_prop".
% If so, the value of that property is delivered.
% If not so, an error-message is raised and ?<name of property>? is delivered.
% The command is not expandable but protected.
% - defines a command \GetRValueExpandable{<property>}{<tokens if <property> is undefined>}
% to check if <property> is defined in the property-list "Christian_ImportValue_GetRValue_prop".
% If so, the value of that property is delivered.
% If not so, <tokens if <property> is not available> is delivered.
% The command is expandable.
% The result is delivered by triggering two expansion-steps.
% This might be important in case you need to "lay hands" on the result for
% further examination. E.g., you may wish to have LaTeX examine the value
% and fork the way of typesetting the value depending on whether the value does
% or does not contain a decimal separator. Or whatever.
\begin{document}
\noindent Unexpandable commands:\medskip
\noindent Test 1)\medskip
\[ (\GetRValue{cathetus1})^2+(\GetRValue{cathetus2})^2=(\GetRValue{hypotenuse})^2 \]
%% This yields an error-message and ?undefined value?:
% \GetRValue{undefined value}
\medskip\hrule\medskip\hrule\medskip
\noindent Expandable commands:\medskip
\noindent Test a)\medskip
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\Mytempa
\expandafter\expandafter\expandafter{\GetRValueExpandable{cathetus1}{n/a}}%
\texttt{\string\Mytempa=\meaning\Mytempa}
\(\Mytempa\)
\medskip\hrule\medskip
\noindent Test b)\medskip
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\Mytempb
\expandafter\expandafter\expandafter{\GetRValueExpandable{cathetus2}{n/a}}
\texttt{\string\Mytempb=\meaning\Mytempb}
\(\Mytempb\)
\medskip\hrule\medskip
\noindent Test c)\medskip
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\Mytempc
\expandafter\expandafter\expandafter{\GetRValueExpandable{hypotenuse}{n/a}}
\texttt{\string\Mytempc=\meaning\Mytempc}
\(\Mytempc\)
\medskip\hrule\medskip
\[ (\Mytempa)^2+(\Mytempb)^2=(\Mytempc)^2 \]
\medskip\hrule\medskip
\noindent Test d)\medskip
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\Mytempd
\expandafter\expandafter\expandafter{\GetRValueExpandable
{undefined value}%
{\LaTeX\space is funny but the value
``undefined value'' is undefined.}%
}
\texttt{\string\Mytempd=\meaning\Mytempd}
\Mytempd
\medskip\hrule\medskip
\noindent Test e)\medskip
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\Mytempe
\expandafter\expandafter\expandafter{\GetRValueExpandable{FirstTokenIsExpandable}{n/a}}
\texttt{\string\Mytempe=\meaning\Mytempe}
\Mytempe
\end{document}
这样,R 程序就不需要在包含文档的 .tex 文件中进行替换。
它只需要创建一个额外的文本文件,其中包含逗号分隔的⟨标识符⟩={⟨计算值⟩}-list 然后由 LaTeX 通过以下方式加载
\ImportIDValueList{⟨file⟩}%
{⟨to‑be‑defined macro for retrieving a
⟨calculated value⟩ stored in ⟨file⟩
by specifying an ⟨identificator⟩
in the argument⟩}%
。
方法 3:
让你的 R 程序创建一个以逗号分隔值列表形式存储为 .csv 文件的数据库,其中包含“标识符”和“值”字段,并使用包数据工具用于导入该数据库并检索值:
% With older LaTeX-kernels you need to activate/uncomment the following line:
%\RequirePackage{filecontents}
% I don't have R available right now for creating the file, thus I let the filecontents*-
% environment do that for me:
\begin{filecontents*}{ValuesCalculatedByR.csv}
cathetus1,"3,3"
cathetus2,"4,4"
hypotenuse,"5,5"
\end{filecontents*}
\documentclass[a4paper]{article}
\usepackage{datatool}
\DTLloaddb[noheader,keys={identificator,value}]{ValuesCalculatedByR}{ValuesCalculatedByR.csv}
\newif\ifvaluenotfound
\newcommand\resultscratch{}%
\newcommand{\GetRValue}[1]{%
\global\valuenotfoundtrue
\DTLforeach*[\DTLiseq{\identificator}{#1}]%
{ValuesCalculatedByR}%
{\identificator=identificator,\value=value}%
{\global\valuenotfoundfalse\global\let\resultscratch=\value}%
\ifvaluenotfound
% Some error or warning-message could be here but I am too lazy at the moment.
\textsf{?#1?}%
\else
\resultscratch
\fi
}%
\begin{document}
\[ (\GetRValue{cathetus1})^2+(\GetRValue{cathetus2})^2=(\GetRValue{hypotenuse})^2 \]
%\GetRValue{Undefined}
\end{document}
答案3
我不清楚你到底想做什么,但我认为这是一种不必要的混乱方法。考虑使用knitr
插入\Sexpr{}
小的 R 输出(如数字或向量)或 R 块(<<>>=
和@
行之间的代码)以获得更复杂的代码(表格、图表等)。请注意,您可以随时更改 foo 的值, \Sexpr{foo}
并将相应更新:
\documentclass{article}
\begin{document}
<<Rchunkcode,echo=FALSE>>=
foo <- "bar"
@
Lorem ipsum dolor sit amet \Sexpr{foo}, consectetur adipiscing elit.
<<AnotherRchunkcode,echo=FALSE>>=
foo <- "baz"
@
Lorem ipsum dolor sit amet \Sexpr{foo}, consectetur adipiscing elit.
\end{document}
答案4
当我写下这个问题时,我找到了一个简单的解决方案。在\begin{document}
“如果我调用新函数”之前R
添加\newcommand{\R}[1]{}
。
例子:
Lorem ipsum dolor sit amet {bar}\R{R.foo}, consectetur adipiscing elit.