Quarto写作公式

比较Quarto多格式输出下的公式语法规则

Author

胡华平

Published

October 18, 2023

前言

“Wealth consists not in having great possessions, but in having few wants.” ― Epictetus

要求在多种输出格式下,统一代码风格地进行LaTex公式显示,这是一项具有挑战性的工作。尤其是对于复杂数学公式,其中的细节、区别和难度都会快速地放大到令人惊异的程度。

这里我们进行综合性的比较,并得到当前可行的解决路径和方案。

公式环境

(1)对于传统的LaTex写作用户,主要输出格式为.pdf。而LaTex对公式环境 (equation environments)做了多样化的定义和分类,如\begin{align} ... \end{align}\begin{aligned} ... \end{aligned}等。

\begin{align}
a  = b + c
\end{align} 

(2)对于当代的Markdown写作用户,天然要求多格式输出(.ppdf、.html、.docx)。.Rmd 文档和.qmd文档都默认以双美元符号($$...$$)对定义数学公式代码块(equation block)。

$$
a  = b + c + 5
$$

公式引用和标号

公式引用(cross-reference)的前提是正确地设定公式代码中的标签(label)指令,例如\label{eq1}

公式标号(numbering)分为手动标号和自动标号。手动标号需要设定公式代码中手动标号(tag)指令,例如\tag{1}。要实现公式自动标号,则需要考虑具体适用场景,例如:写作文档.qmd还是.rmd?输出格式htmlpdf还是docx?同样输出docx,采用不同R包bookdown::word2_document()、还是Rmarkdown::word_document()

传统LaTex写作

传统LaTex写作者会严格遵从Latex语法规则:

  • 添加标签命令:在公式代码块内,添加公式标签的语法,例如a = b + c \label{eq1}

  • 正文引用公式:引用公式的标准语法包括:\eqref{}\ref{}

Tip

这里显示的是Quarto写作 .qmd渲染为html格式的效果:

\begin{align}
a  = b + c \label{eq1}
\end{align} 

\[\begin{align} a = b + c \label{eq1} \end{align}\]

引用公式语法\eqref{eq1}显示为\(\eqref{eq1}\);或引用公式语法\ref{eq1},显示为\(\ref{eq1}\)

注意:因为这里是.qmd渲染为html格式,因此不会正确显示公式标号和公式引用。具体原因后面会进一步解释。

当代Markdown写作

当代Markdown写作者会遵从Quarto官方的说明文档语法书写公式:

  • 添加标签命令:在公式代码块的双美元符号对外使用{#eq-}语法命令,添加公式标签,例如$$a = b + c $$ {#eq-add}

  • 正文引用公式:引用公式的标准语法为@eq-,例如@eq-add

Tip
$$
a  = b + c + 5
$$ {#eq-add}

\[ a = b + c + 5 \tag{1}\]

引用公式语法@eq-add显示为 式 1

混合语法写作

混合语法写作者可能不严格遵守上述任何一个语法体系,而是更加关注便利性和最终显示效果。

$$
\begin{align}
a & = b + c \label{eq2} \\
c & = d + e \label{eq3}
\end{align}
$$

\[ \begin{align} a & = b + c \label{eq2} \\ c & = d + e \label{eq3} \end{align} \]

  • 混用情形1:双美元符号对$$...$$内部蕴含了典型的Latex语法\label{}

  • 公式引用效果1:引用方法\eqref{eq2},显示\(\eqref{eq2}\)

  • 副作用1:a.以上语法在.qmd文档内不能正常预览该公式。b.在html输出下,引用语法\eqref{eq2}无法显示,且两个子方程不会自动标号(但在pdf输出下能正常显示,具体可参看节pdf测试 )。

以下的混用情形2是禁止使用的:

$$
\begin{align}
a & = b + c +4 \label{eq4} \\
c & = d + e +5 \label{eq5}
\end{align}
$$  {#eq-hybrid}

\[ \begin{align} a & = b + c \label{eq2} \\ c & = d + e \label{eq3} \end{align} \tag{2}\]

  • 混用情形2:双美元符号对$$...$$结合#eq-hybrid标签,但是内部蕴含了典型的Latex语法\label{}

  • 公式引用效果2:引用方法\eqref{eq2},显示\(\eqref{eq2}\)。引用方法@eq-hybrid,显示 式 2

  • 副作用2:a.以上语法在.qmd文档内不能正常预览该公式。b.在html输出下,引用语法\eqref{eq2}无法显示。c.无论是html还是pdf输出,都不会显示公式!html输出会仅仅显示源代码,而pdf输出则会直接报错无法渲染!!

复杂公式

LaTex语言能很好定义复杂数学公式及版式风格

嵌套公式

(1)\begin{split} ... \end{split}嵌套于\begin{aligned} ... \end{aligned}

\begin{aligned}
  \begin{split}
a & = 0.01 b + 4c \\
c & = 1.4321 d + 2.22 e
  \end{split} 
  \text{(方程1)}
  \\
  \begin{split}
  m & =  b + c +d \\
  p & =  d + e
  \end{split}
  \text{(方程2)}
\end{aligned}
\[\begin{aligned} \begin{split} a & = 0.01 b + 4c \\ c & = 1.4321 d + 2.22 e \end{split} \text{(方程1)} \\ \begin{split} m & = b + c +d \\ p & = d + e \end{split} \text{(方程2)} \end{aligned}\]

版式风格

(1)版式风格\begin{align} ... \end{align}

\begin{align}
a & = 0.01 b + 4c  \\
c & = 1.4321 d + 2.22 e
\end{align}

\[\begin{align} a & = 0.01 b + 4c \\ c & = 1.4321 d + 2.22 e \end{align}\]

(2)版式风格\begin{alignedat}{number} ... \end{alignedat}

\begin{alignedat}{999}
&e_t^2=&& + \alpha_{1} && + \alpha_{2} lquan&& 
   + \alpha_{3} mon&& + \alpha_{4} tue\\
   & && + \alpha_{5} wed&& + \alpha_{6} thu&& 
   + \alpha_{7} stormy&& + \alpha_{8} cold\\
   & && + \alpha_{9} change&& + \alpha_{10} (lquan)^2&&+v_t\\
\end{alignedat}
\[\begin{alignedat}{999} &e_t^2=&& + \alpha_{1} && + \alpha_{2} lquan&& + \alpha_{3} mon&& + \alpha_{4} tue\\ & && + \alpha_{5} wed&& + \alpha_{6} thu&& + \alpha_{7} stormy&& + \alpha_{8} cold\\ & && + \alpha_{9} change&& + \alpha_{10} (lquan)^2 &&+v_t\\ \end{alignedat}\]
\begin{alignedat}{999}
&\widehat{lprice}=&&+0.65&&-0.10lquan_i&&-0.08mon_i&&-0.08tue_i\\ 
&(s)&&(0.4408)&&(0.0501)&&(0.1042)&&(0.1048)\\ 
&(t)&&(+1.48)&&(-1.95)&&(-0.80)&&(-0.80)\\ 
&(cont.)&&-0.07wed_i&&+0.05thu_i&&+0.29stormy_i&&+0.09cold_i\\ 
&(s)&&(0.1077)&&(0.1008)&&(0.0815)&&(0.0713)\\ 
&(t)&&(-0.68)&&(+0.53)&&(+3.60)&&(+1.21)\\ 
&(cont.)&&-0.15change_i && && &&\\ 
&(s)&&(0.0738) && && &&\\ 
&(t)&&(-2.00) && && &&
\end{alignedat}
\[\begin{alignedat}{999} &\widehat{lprice}=&&+0.65&&-0.10lquan_i&&-0.08mon_i&&-0.08tue_i\\ &(s)&&(0.4408)&&(0.0501)&&(0.1042)&&(0.1048)\\ &(t)&&(+1.48)&&(-1.95)&&(-0.80)&&(-0.80)\\ &(cont.)&&-0.07wed_i&&+0.05thu_i&&+0.29stormy_i&&+0.09cold_i\\ &(s)&&(0.1077)&&(0.1008)&&(0.0815)&&(0.0713)\\ &(t)&&(-0.68)&&(+0.53)&&(+3.60)&&(+1.21)\\ &(cont.)&&-0.15change_i && && &&\\ &(s)&&(0.0738) && && &&\\ &(t)&&(-2.00) && && && \end{alignedat}\]

MathJax版本

对于数学公式的支持,RStudio的 .Rmd 文档和Quarto的.qmd文档都默认调用MathJax进行公示渲染和呈现。

二者对于MathJax版本及调用是不同的:

公式渲染测试

xmerit包公式测试

xmerit包说明

xmerit包在版本>0.0.12以后,增加了对Quarto文档公式风格的支持。主要使用的函数包括:

  • xmerit::px.psm()函数,书写特定回归下,总体或样本回归模型的理论表达式。默认形式为嵌套结构\begin{aligned}内含\begin{split}

  • xmerit::px.est()函数,书写特定回归下,估计结果的方程数值表达式。默认形式为嵌套结构\begin{alignedat}{999}内含\begin{split}

Warning: replacing previous import 'stats::filter' by 'dplyr::filter' when
loading 'xmerit'
Warning: replacing previous import 'stats::lag' by 'dplyr::lag' when loading
'xmerit'

Call:
lm(formula = mod, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.9159 -1.2484 -0.3566  1.4719  5.9253 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 43.539847   4.860059   8.959 1.42e-09 ***
cyl         -1.784296   0.613889  -2.907  0.00722 ** 
disp         0.006944   0.012007   0.578  0.56782    
wt          -3.792867   1.081819  -3.506  0.00161 ** 
gear        -0.490445   0.790285  -0.621  0.54007    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.624 on 27 degrees of freedom
Multiple R-squared:  0.835, Adjusted R-squared:  0.8105 
F-statistic: 34.15 on 4 and 27 DF,  p-value: 3.36e-10

xmerit::qx.psm

xmerit::qx.psm()函数默认形式为嵌套结构\begin{aligned}内含\begin{split}

Show the code
```{r}
#| results: asis
#| code-fold: show
#| eval: false

qx.out <- xmerit::qx.psm(
  x = xvars, y = yvars,
  begin = 0,
  #greek.n = length(xvars)+1,
  n.row = 3,
  lm.label = "lx-psm",
  lm.tag = "lx.psm",
  no_dollar = FALSE)
```
$$
\begin{aligned}
\begin{split}
mpg_i=&+\beta_{0}+\beta_{1}cyl_i+\beta_{2}disp_i\\&+\beta_{3}wt_i+\beta_{4}gear_i+u_i
\end{split}
\quad \text{(lx.psm)}\quad
\end{aligned}
$$ {#eq-lx-psm}

引用语法`见 @eq-lx-psm` 显示为见 @eq-lx-psm 。

\[ \begin{aligned} \begin{split} mpg_i=&+\beta_{0}+\beta_{1}cyl_i+\beta_{2}disp_i\\&+\beta_{3}wt_i+\beta_{4}gear_i+u_i \end{split} \quad \text{(lx.psm)}\quad \end{aligned} \tag{3}\]

引用语法见 @eq-lx-psm 显示为见 式 3

xmerit::qx.est

xmerit::qx.est()函数默认形式为嵌套结构\begin{alignedat}{999}内含\begin{split}

Show the code
```{r}
#| results: asis
#| code-fold: show
#| eval: false

qx.out <- qx.est(
  lm.mod = mod, lm.dt = df,
  lm.n = 3, lm.label = "lx-est", 
  lm.tag = "lx.est",
  no_dollar = FALSE)
```
$$
\begin{alignedat}{999}
\begin{split}
&\widehat{mpg}=&&+43.54&&-1.78cyl_i&&+0.01disp_i\\ 
&(s)&&(4.8601)&&(0.6139)&&(0.0120)\\ 
&(t)&&(+8.96)&&(-2.91)&&(+0.58)\\ 
&(cont.)&&-3.79wt_i&&-0.49gear_i &&\\ 
&(s)&&(1.0818)&&(0.7903) &&\\ 
&(t)&&(-3.51)&&(-0.62) &&
\end{split}
\quad \text{(lx.est)}\quad
\end{alignedat}
$$ {#eq-lx-est}

引用语法`见 @eq-lx-est` 显示为见 @eq-lx-est 。

\[ \begin{alignedat}{999} \begin{split} &\widehat{mpg}=&&+43.54&&-1.78cyl_i&&+0.01disp_i\\ &(s)&&(4.8601)&&(0.6139)&&(0.0120)\\ &(t)&&(+8.96)&&(-2.91)&&(+0.58)\\ &(cont.)&&-3.79wt_i&&-0.49gear_i &&\\ &(s)&&(1.0818)&&(0.7903) &&\\ &(t)&&(-3.51)&&(-0.62) && \end{split} \quad \text{(lx.est)}\quad \end{alignedat} \tag{4}\]

引用语法见 @eq-lx-est 显示为见 式 4

总结与展望

概括小结

  • Quarto的Markdown公式语法对于复杂公式的支持比较有限。例如对一个公式环境下的两个子公式分别标号和分别引用就难以轻松做到。简单地,Quarto的Markdown公式语法的简洁性、统一性,需要付出对复杂公式情形良好支持的代价!尽管Quarto团队还在进行更多努力改进。

  • 明确自己的最终写作输出格式(.html.pdf.docx)是最为关键的。例如,LaTex写作风格下,添加\label命令,html输出不会显示公式标号,但是pdf输出下则会正常显示公式标号。概括地说,简单而明确的输出需求——例如尽量不采用多格式同时如安然输出——会让事情更加容易掌控!

  • .html格式输出,对于公式语法要求相对最为宽松。

  • .pdf格式和.docx格式输出,对于公式语法有各自的要求。但是总体而言,能够在.pdf输出正常显示的公式语法,也能在.docx输出下正常显示。

  • eeholmes经验法则(参看eeholmes的问答):

If you only need to render to both html and PDF, then I would say never use $$ $$ and @eq- for equations. Stick to the equation environments (which btw is what LaTeX users would normally use) and use \eqref{} and \ref{}. If you need to render to word though….only $$ $$ is recognized; equations in equation environments are completely removed!

后续工作

(1)对于R包xmerit的开发,在Quarto写作工具下的公式显示支持,可以考虑设定新的参数quarto = TRUE

(2)R包xmerit的开发,建议使用的语法情景为:

  • 坚持Quarto的语法指引,使用双美元符号对$${#eq-}风格

  • 嵌入公式使用:split嵌入aligned;或者split嵌入alignedat。目前不要使用alignalignatequation这样的公式环境(Quarto的语法指引下,pdf输出将不支持这类公式环境)!

  • 不适用复杂的公式系统。任何复杂公式系统,都可以考虑拆分转换为简单的多个公式,并分别加以书写、渲染和呈现。

参考资料

  • referencing equations inside $$ \begin{align} environment. 参看github问题提交 #2275

  • enhancement: add MathJax equation numbering for math environments in html. 参看github问题提交#4136

  • Specify LaTeX Parameters in R Markdow. 参看网页链接