Markdown outputs latex code to pdf but no table, what could be wrong?

rmarkdown

#1

I am running the following example code, but the output is latex code, not a table.

The code:

---
title: "mtcars2"
output: pdf_document
---

```{r setup, include=FALSE}
library(qwraps2)
library(dplyr)
library(knitr)
```

```{r cars}
mtcars2 <-
  dplyr::mutate(mtcars,
                cyl_factor = factor(cyl,
                                    levels = c(6, 4, 8),
                                    labels = paste(c(6, 4, 8), "cylinders")),
                cyl_character = paste(cyl, "cylinders"))
our_summary1 <-
  list("Miles Per Gallon" =
         list("min" = ~ min(mpg),
              "max" = ~ max(mpg),
              "mean (sd)" = ~ qwraps2::mean_sd(mpg)),
       "Displacement" =
         list("min" = ~ min(disp),
              "max" = ~ max(disp),
              "mean (sd)" = ~ qwraps2::mean_sd(disp)),
       "Weight (1000 lbs)" =
         list("min" = ~ min(wt),
              "max" = ~ max(wt),
              "mean (sd)" = ~ qwraps2::mean_sd(wt)),
       "Forward Gears" =
         list("Three" = ~ qwraps2::n_perc0(gear == 3),
              "Four"  = ~ qwraps2::n_perc0(gear == 4),
              "Five"  = ~ qwraps2::n_perc0(gear == 5))
  )
```

```{r, results='asis'}
summary_table(group_by(mtcars2, cyl_factor), our_summary1)
```

The error message:

output file: mtcars.knit.md

! Missing $ inserted.
<inserted text> 
                $
l.165  & cyl_
             factor: 6 cylinders (N = 7) & cyl_factor: 4 cylinders (N = 11) ...

Error: Failed to compile mtcars.tex. See mtcars.log for more info.
Execution halted

I am confused, because R can output the raw latex code in console, but unable to compile it to a table?

What could possibly be wrong? Thanks in advance!


#2

When you knit the document, knitr knits your rmarkdown document to a .tex document and then uses the latex engine to compile that into PDF. The latex code for your table is being output to the .tex document, but then the compilation of the .tex document to PDF fails with the error you're seeing.

According to the error message, the offending line in the .tex document is line 165. If you save the tex document during compilation you can inspect it to see where the error is occurring. To save the .tex document, use this in your yaml:

---
title: "mtcars2"
output: 
  pdf_document:
    keep_tex: true
---

Inspecting the .tex document, the offending line is:

& cyl_factor: 6 cylinders (N = 7) & cyl_factor: 4 cylinders (N = 11) & cyl_factor: 8 cylinders (N = 14)\\

My knowledge of latex is limited, but the problem appears to be the underscores, which are being interpreted as Math Mode commands to subscript the f that follows the underscore. But Math Mode expressions have to be placed between $, hence the error (for example, if you type $x_i = 5$ (outside of an R code chunk) in your document, you'll see that it compiles to a mathematical expression).

You can either remove or escape the underscores to get the document to compile to PDF, as shown in the example below. Escaping just means adding a double backslash \\ before the underscore to denote that the underscore is a literal underscore and not a latex command. There may be better approaches. Hopefully, someone with more latex knowledge will come along and show us other options that might be available.

---
title: "mtcars2"
output: 
  pdf_document:
    keep_tex: true
---

```{r}
library(qwraps2)
library(dplyr)
library(knitr)
```

Here is a mathemtical expression: $x_i = 5$.

```{r}
# Remove underscore in cyl_factor
mtcars2 <-
  dplyr::mutate(mtcars,
                `cyl factor` = factor(cyl,
                                    levels = c(6, 4, 8),
                                    labels = paste(c(6, 4, 8), "cylinders")),
                 cyl_character = paste(cyl, "cylinders"))
our_summary1 <-
  list("Miles Per Gallon" =
         list("min" = ~ min(mpg),
              "max" = ~ max(mpg),
              "mean (sd)" = ~ qwraps2::mean_sd(mpg)),
       "Displacement" =
         list("min" = ~ min(disp),
              "max" = ~ max(disp),
              "mean (sd)" = ~ qwraps2::mean_sd(disp)),
       "Weight (1000 lbs)" =
         list("min" = ~ min(wt),
              "max" = ~ max(wt),
              "mean (sd)" = ~ qwraps2::mean_sd(wt)),
       "Forward Gears" =
         list("Three" = ~ qwraps2::n_perc0(gear == 3),
              "Four"  = ~ qwraps2::n_perc0(gear == 4),
              "Five"  = ~ qwraps2::n_perc0(gear == 5))
  )
```

```{r, results='asis'}
summary_table(group_by(mtcars2, `cyl factor`), our_summary1)
```

```{r}
# Escape underscore in cyl_factor
mtcars2 <-
  dplyr::mutate(mtcars,
                `cyl\\_factor` = factor(cyl,
                                    levels = c(6, 4, 8),
                                    labels = paste(c(6, 4, 8), "cylinders")),
                cyl_character = paste(cyl, "cylinders"))
our_summary1 <-
  list("Miles Per Gallon" =
         list("min" = ~ min(mpg),
              "max" = ~ max(mpg),
              "mean (sd)" = ~ qwraps2::mean_sd(mpg)),
       "Displacement" =
         list("min" = ~ min(disp),
              "max" = ~ max(disp),
              "mean (sd)" = ~ qwraps2::mean_sd(disp)),
       "Weight (1000 lbs)" =
         list("min" = ~ min(wt),
              "max" = ~ max(wt),
              "mean (sd)" = ~ qwraps2::mean_sd(wt)),
       "Forward Gears" =
         list("Three" = ~ qwraps2::n_perc0(gear == 3),
              "Four"  = ~ qwraps2::n_perc0(gear == 4),
              "Five"  = ~ qwraps2::n_perc0(gear == 5))
  )
```

```{r, results='asis'}
summary_table(group_by(mtcars2, `cyl\\_factor`), our_summary1)
```

Just for illustration, you could also use Math Mode, which will cause the f in factor to be subscripted. That's not what you want, but just shows that the code also compiles properly if you add the appropriate Math Mode delimiters.

```{r}
# Use math mode
mtcars2 <-
  dplyr::mutate(mtcars,
                `$cyl_factor$` = factor(cyl,
                                    levels = c(6, 4, 8),
                                    labels = paste(c(6, 4, 8), "cylinders")),
                 cyl_character = paste(cyl, "cylinders"))
our_summary1 <-
  list("Miles Per Gallon" =
         list("min" = ~ min(mpg),
              "max" = ~ max(mpg),
              "mean (sd)" = ~ qwraps2::mean_sd(mpg)),
       "Displacement" =
         list("min" = ~ min(disp),
              "max" = ~ max(disp),
              "mean (sd)" = ~ qwraps2::mean_sd(disp)),
       "Weight (1000 lbs)" =
         list("min" = ~ min(wt),
              "max" = ~ max(wt),
              "mean (sd)" = ~ qwraps2::mean_sd(wt)),
       "Forward Gears" =
         list("Three" = ~ qwraps2::n_perc0(gear == 3),
              "Four"  = ~ qwraps2::n_perc0(gear == 4),
              "Five"  = ~ qwraps2::n_perc0(gear == 5))
  )
```

```{r, results='asis'}
summary_table(group_by(mtcars2, `$cyl_factor$`), our_summary1)
```

#3

I think what's happening is the summary_table function is producing raw LaTex in the Markdown document which is getting mangled when passed into Pandoc. Pandoc isn't expecting LaTex to be coming in the front door, so it does something (I'm not sure what) but then the LaTex it blows out the backdoor is unparsable by the LaTex engine.

At any rate, it's fixable if you tell qwraps2 to output Markdown:

options(qwraps2_markup = "markdown")

Execute that option setting code at the beginning and you should be good to go!

BTW, you made an excellent Reprex and I never would have taken the time to figure this out had it not been for your very good example.


#4

Thank you, indeed the problem was,

options(qwraps2_markup = "markdown")

On a side note about format in here, both markdown and blockquote use ```. You can only use blackslash once to escape inside a blockquote.


#5

@jdlong, do you know why this approach (outputting markdown instead of latex) is necessary here, but not with, say, xtable, kableExtra or huxtable? I ask because don't those other packages also produce raw latex that is nevertheless processed properly in the knitr tool chain from rmarkdown to PDF?


#6

I really don't know. I was asking myself the same question.

One could look at the output of the tex produced by summary_table with no 'markdown' option and possibly see why... but I have not done that.

I'm really not sure. I would have expected the results='asis' to pass the latex all the way through Pandoc and into the tex code causing it to end up in the final PDF. But I suspect something is molesting it before it gets out of Pandoc.


#7

I think the problem is that summary_table is treating the underscore in cyl_factor as latex markup, which throws an error when the underscores aren't either escaped or placed inside $ for Math Mode. The other packages properly interpret the underscores as literal underscores.


#8

that makes sense to me. I'm glad you dug in there to figure that out! File bug with qwraps2, maybe?


#9

I've reported the issue at the qwraps2 development site.


#10

I'm the qwraps2 package author. My reply to the issue can be read here: https://github.com/dewittpe/qwraps2/issues/57#issuecomment-417893071

The gist of it is that I would expect that the output from qwrps2::summary_table will be in the markup language of the parent document. Thus, if writing and .Rmd the output should be markdown, and if writing and .Rnw file the output should be LaTeX.

It is my preference to not automatically escape special characters. My experience has had too many nuanced cases where I have found the easiest solution is to turn off any and auto escapements and explicitly escape the needed characters. I'm open to suggestions and other opinions on this. Anyone is welcome to submit a pull request to qwraps2 to help improve it. (I might not accept the pull request, but I will certainly review it and consider it.)