Rmarkdown下自动化编译Latex公式输出

本文介绍了如何在Rmarkdown写作环境下,编写R函数自动化输出LaTex数学公示和符号,辅助统计或计量经济教学工作!

1 “计量经济学”形式的数学公式

本科生课程《计量经济学》会大量使用各类公式。这些公式大概有两类:

  • 理论公式:这些数学公式的呈现基本上用不到数据报告的结果。在Rmarkdown文档中处理办法相对简单,直接使用LaTex形式代码输入即可。

  • 报告公式:这些数学公式的呈现,跟某些数据分析报告紧密联系。此时,手工输入LaTex将会变得很不现实!一个系统性的解决思路是,利用Rmardown chunk 自己编写代码块。前边分析步骤负责出数据报告结果,后面Chunk里公式创造时直接调用数据报告的各类数值。

所以,如果一切顺利,“计量经济学”课程的大量报告公式,都可以自动地使用R函数调用实现,可谓一本万利,合算得很!

2 代码实现过程中的各种“梗”

实现思路倒是很清晰,Rmarkdown下的操作也是可行的。下面就是需要撸起袖子加油干,找到“聪明”的实现办法,破除各种“梗”!

2.1 格式选择真是一个大问题:关于pdf、html、word输出形式

选择Rmarkdown的一个很重要理由,就是它可以实现一份文档种格式输出。这个“理想”确实比较宏大。在数学公式领域,起码就有这几个问题:

  • 数学公式友好度差别较大。word比较另类,数学公式支持是最糟糕的;Latex(pdf)下是最灵活的。html则最通用。所以,复杂的公式,在latex(pdf)下OK,放到word里可能就吃不消了。

  • package实现各不相同。如果写书,最好就是用bookdown包;如果做演示slide可以用Xaringan包;写网站文章,可以用blogdown。对于数学公式而言,最需要注意是:是否需要交叉引用(cross reference)公式。基本就是两种情形:写slide和blog默认是不交叉应用公式的;写book则必然需要的。

2.2 数学公式的排版(alignment)

这个部分折腾我很久。因为主要使用Latex语法形式来编写R chunk代码。所以必须要熟悉Latex math equation的公式环境(environment)。

独立公式环境:

  • \begin{equation} ... \end{equation}

  • \begin{align} ... \end{align}

  • \begin{aligned} ... \end{aligned}

  • \begin{alignedat}{3} ... \end{alignedat}

组合公式环境:

cat(
"\\begin{equation}",
"\\begin{alignedat}{999}",
"&\\widehat{Y}=&&+17.81&&+0.62X\\\\",
      "&\\text{(t)}&&(1.9238)&&(11.9968)\\\\",
      "&\\text{(se)}&&(9.2556)&&(0.0516)\\\\",
      "&\\text{(fitness)}&& n=10;&& R^2=0.9473;&& \\bar{R^2}=0.9408\\\\",
      "& && F^{\\ast}=143.92;&& p=0.0000",
"\\end{alignedat}",
"\\end{equation}")

\[\begin{equation} \begin{alignedat}{999} &\widehat{Y}=&&+17.81&&+0.62X\\ &\text{(t)}&&(1.9238)&&(11.9968)\\ &\text{(se)}&&(9.2556)&&(0.0516)\\ &\text{(fitness)}&& n=10;&& R^2=0.9473;&& \bar{R^2}=0.9408\\ & && F^{\ast}=143.92;&& p=0.0000 \end{alignedat} \end{equation}\]

2.3 公式分行显示

本质上,这还是公式排版问题。但是需要结果代码语句和公式环境来共同解决。

if (k<=lm.n) {
    cat(
      "$$\\begin{equation}",
      paste0('\\begin{alignedat}{',999,'}'),
      paste0('&\\widehat{',table_lm$name.y,'}=',fun_line_eq(1,k),'\\\\'),
      paste0('&\\text{(t)}',fun_line_t(1,k),'\\\\'),
      paste0('&\\text{(se)}',fun_line_se(1,k),'\\\\'),
      paste0('&\\text{(fitness)}',
             '&& n=',table_lm$n,";",
             '&& R^2=',formatC(table_lm$R2, digits = 4,format = "f"),";",
             '&& \\bar{R^2}=',formatC(table_lm$R2.adj, digits = 4,format = "f"),
             "\\\\"),
      paste0('& ',
             '&& F^{\\ast}=',formatC(table_lm$F.star, digits = 2,format = "f"),";",
             '&& p=',formatC(table_lm$F.p, digits=4,format = "f")),
      "\\end{alignedat}",
      # default no equation label
      if (!is.null(lm.label)) {
        (paste0('(\\#eq:',lm.label,')'))
      },
      "\\end{equation}$$"
      )
  } else if {
  ...
  }

2.4 label的取舍

理论上,用一个if函数是可以搞定的。

# default no equation label
  if (!is.null(lm.label)) {
    (paste0('(\\#eq:',lm.label,')'))
  }

那么组合起来大概就是:

lm.label <- "eq-SRF"

cat(
"\\begin{equation}",
"\\begin{alignedat}{999}",
"&\\widehat{Y}=&&+17.81&&+0.62X\\\\",
      "&\\text{(t)}&&(1.9238)&&(11.9968)\\\\",
      "&\\text{(se)}&&(9.2556)&&(0.0516)\\\\",
      "&\\text{(fitness)}&& n=10;&& R^2=0.9473;&& \\bar{R^2}=0.9408\\\\",
      "& && F^{\\ast}=143.92;&& p=0.0000",
"\\end{alignedat}",
# default no equation label
  if (!is.null(lm.label)) {
    (paste0('(\\#eq:',lm.label,')'))
  },
"\\end{equation}")

\[\begin{equation} \begin{alignedat}{999} &\widehat{Y}=&&+17.81&&+0.62X\\ &\text{(t)}&&(1.9238)&&(11.9968)\\ &\text{(se)}&&(9.2556)&&(0.0516)\\ &\text{(fitness)}&& n=10;&& R^2=0.9473;&& \bar{R^2}=0.9408\\ & && F^{\ast}=143.92;&& p=0.0000 \\tag{2.1}t} (#eq:eq-SRF) \end{equation}\]

问题是blog里不支持对label进行公式交叉引用;Xaringan里也不支持。只有bookdown或bookdown::render(out= html_document2)才支持。当然这个问题也不是很大:只要在Xanringan或blog里不输label就好了!如果写书,再加上label呗!

2.5 “美元”焦虑!

Rmarkdown的公式世界里,比较喜欢铜臭味——美元符号。行内公式采用的是\(\$...\$\)形式,独立公式采用\(\$\$...\$\$\)形式。

所以,记得要时刻保持“富有”!如下的美元符号:

cat(
"$$\\begin{equation}",
"\\begin{alignedat}{999}",
"&\\widehat{Y}=&&+17.81&&+0.62X\\\\",
      "&\\text{(t)}&&(1.9238)&&(11.9968)\\\\",
      "&\\text{(se)}&&(9.2556)&&(0.0516)\\\\",
      "&\\text{(fitness)}&& n=10;&& R^2=0.9473;&& \\bar{R^2}=0.9408\\\\",
      "& && F^{\\ast}=143.92;&& p=0.0000",
"\\end{alignedat}",
"\\end{equation}$$")

\[\begin{equation} \begin{alignedat}{999} &\widehat{Y}=&&+17.81&&+0.62X\\ &\text{(t)}&&(1.9238)&&(11.9968)\\ &\text{(se)}&&(9.2556)&&(0.0516)\\ &\text{(fitness)}&& n=10;&& R^2=0.9473;&& \bar{R^2}=0.9408\\ & && F^{\ast}=143.92;&& p=0.0000 \end{alignedat} \end{equation}\]

2.6 小心猫(cat)!

理论上,cat(, sep="\n")函数里只要设置sep="\n"就能保证换行,输出也很有层次和条理。

但是你还是要保持美元“富有”,并小心猫(cat)的这个挠人小利爪(sep="\n")。

换言之,下面的代码就可能会让你“很受伤”!

cat(
"\\begin{equation}",
"\\begin{alignedat}{999}",
"&\\widehat{Y}=&&+17.81&&+0.62X\\\\",
      "&\\text{(t)}&&(1.9238)&&(11.9968)\\\\",
      "&\\text{(se)}&&(9.2556)&&(0.0516)\\\\",
      "&\\text{(fitness)}&& n=10;&& R^2=0.9473;&& \\bar{R^2}=0.9408\\\\",
      "& && F^{\\ast}=143.92;&& p=0.0000",
"\\end{alignedat}",
"\\end{equation}",
sep= "\n")

2.7 外部文档关联

如果完事顺利,你只需要在Rmardown里直接调用公式文件.R

source("external-math-equation.R")

Related