Extra curly braces when using xtable and knitr, af

2020-02-06 17:20发布

问题:

I'm creating a R Markdown document using knitr and am running into trouble using xtable to create a table. My table is very large and I'm trying to reduce the size using the size command in the print statement. The issue I'm running into is that the command seems to add two extra curly braces which show up in the PDF, one before the table and one after.

Does anyone know a way to fix this?

MWE:

---
output:
  pdf_document:
    keep_tex: yes
tables: true
---

```{r, results='asis', echo=FALSE}

library(xtable)

my.df <- data.frame(matrix(c(1:18),nrow=2))

glossaryprint <- xtable(my.df, caption="Summary of Terms")

print(glossaryprint,
      comment=FALSE,
      floating=FALSE,
      size="footnotesize"
)

```

回答1:

Note: The issue that is subject of the question and this answer has been resolved in xtable 1.8-2.


Although the question has been self-answered with a workaround, I think for other users some more details might be helpful.

What happens?

To understand what is happening here, we need to take a close look at the conversion steps the document undergoes on its way from RMD to PDF. The steps are:

RMD --> MD --> TEX --> PDF

Let's look at the files in reversed order:

  • PDF: (generated from TEX by pdflatex)

  • TEX: (generated from MD by pandoc)

    % …
    \{\footnotesize
    
    \begin{tabular}{rrrr}
      \hline
     & X1 & X2 & X3 \\ 
      \hline
    1 &   1 &   3 &   5 \\ 
      2 &   2 &   4 &   6 \\ 
       \hline
    \end{tabular}
    
    \}
     % …
    
  • MD (generated from RMD by knitr)

    ---
    output:
      pdf_document:
        keep_tex: yes
    ---
    
    {\footnotesize
    \begin{tabular}{rrrr}
      \hline
     & X1 & X2 & X3 \\ 
      \hline
    1 &   1 &   3 &   5 \\ 
      2 &   2 &   4 &   6 \\ 
       \hline
    \end{tabular}
    }
    
  • RMD: (source file)

    ---
    output:
      pdf_document:
        keep_tex: yes
    ---
    
    ```{r, results='asis', echo=FALSE}
    
    library(xtable)
    
    mytable <- xtable(data.frame(matrix(c(1:6), nrow = 2)))
    
    print(mytable,
          comment = FALSE,
          floating = FALSE,
          size = "footnotesize"
    )
    ```
    

Recall: The problem is, that there are visible curly braces in the PDF. Where do they come from?

  • They are the result of the escaped curly braces in the TEX file (\{ and \}).
  • These curly braces also exist in the MD file, but there they are not escaped.

So we know two things by now: We see the curly braces because they are escaped and they are escaped by pandoc.

But why do these curly braces exist at all? print.xtable outputs them when a size is specified. The goal is to create a group and to apply size only within that group. (With floating = TRUE, no grouping by curly braces is required because there is a figure environment whithin which the size is set. The curly braces are printed anyways.)

And why does pandoc escape that pair of curly braces but leaves all the other curly braces (e.g. in \begin{tabular}) unescaped? This is because pandoc is supposed to escape special characters that are meant literally but leave raw LaTeX unescaped. However, pandoc does not know that the outer curly braces are LaTeX commands and not text.

(With floating = TRUE the problem does not occur because the curly braces are within a figure environment which is recognized as LaTeX.)

Solutions

After having understood the problem, what can we do about it? One solution has already been posted by the OP: Abstain from spefifying size in print.xtable and insert the footnotesize command manually:

    ---
    output:
      pdf_document:
        keep_tex: yes
    ---

    ```{r, results='asis', echo=FALSE}

    library(xtable)

    mytable <- xtable(data.frame(matrix(c(1:6), nrow = 2)))

    cat("\\begin{footnotesize}")

    print(mytable,
          comment = FALSE,
          floating = FALSE
    )

    cat("\\end{footnotesize}")
    ```

However, on the long run it would be nice if xtable (current version: 1.8-0) generated LaTeX code that survives the pandoc conversion. This is quite simple: print.xtable checks if size is set and if so, inserts { before the size specification and } at the end of the table:

if (is.null(size) || !is.character(size)) {
  BSIZE <- ""
  ESIZE <- ""
}
else {
  if (length(grep("^\\\\", size)) == 0) {
    size <- paste("\\", size, sep = "")
  }
  BSIZE <- paste("{", size, "\n", sep = "")
  ESIZE <- "}\n"
}

This small modification replaces { with \begingroup and } with \endgroup:

if (is.null(size) || !is.character(size)) {
  BSIZE <- ""
  ESIZE <- ""
}
else {
  if (length(grep("^\\\\", size)) == 0) {
    size <- paste("\\", size, sep = "")
  }
  BSIZE <- paste("\\begingroup", size, "\n", sep = "")
  ESIZE <- "\\endgroup\n"
}

For LaTeX, this makes no difference, but as pandoc recognizes \begingroup (as oppsed to {) it should solve the problem. I reported this as a bug in xtable and hopefully the issue will be fixed in future versions.



回答2:

I was able to fix this by not including the size parameter in the print statement but rather directly before and after the chunk.

\begin{footnotesize}

#chunk

\end{footnotesize}