how to cross reference tables and plots in rmarkdown?
Asked Answered
K

1

13

I am using the following template

---
title: "Nice try buddy"
author: "SpaceMan"
date: "13 December 2057"
output:
  bookdown::pdf_document2
header-includes:
- \usepackage{booktabs}
- \usepackage{longtable}
- \usepackage{array}
- \usepackage{multirow}
- \usepackage[table]{xcolor}
- \usepackage{wrapfig}
- \usepackage{float}
- \usepackage{colortbl}
- \usepackage{pdflscape}
- \usepackage{tabu}
- \usepackage{threeparttable}
- \usepackage{threeparttablex}
- \usepackage[normalem]{ulem}
- \usepackage{makecell}  
---
---
references:
- id: fenner2012a
  title: One-click science marketing
  container-title: Nature Materials
  volume: 11
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Title

\begin{equation}
f\left(k\right)=\binom{n}{k}p^k\left(1-p\right)^{n-k} \label{eq:binom}
\end{equation}

You may refer to it using `\@ref(eq:binom)`, e.g., see Equation \@ref(eq:binom).
and not a nice citation! @fenner2012a


## Including Tables

You can also embed tables, for example:  \@ref(tab:tw)

```{r tw, echo=FALSE}
mytable
```

## References

where mytable is stored in R session and is generated with

mytable <- head(cars) %>% kable(format = "latex", 
                                booktabs = T, 
                                caption = "Demo Table", 
                                escape = F) %>%
kable_styling(latex_options = 'HOLD_position')

Now, this is supposed to work, but when I knit the document using

rmarkdown::render('C:\\Users\\john\\Documents\\bbv.Rmd')

  • the cross-reference for the table is not there! I only see ??
  • and the table has this weird #tab thing - how to get rid of it ?
  • the TOC is here even though I did not ask for it

enter image description here

Any ideas how to fix these issues? Thanks!

EDIT: the weird #tab thing disappeared after a reboot.

Kilroy answered 23/9, 2018 at 3:39 Comment(11)
Sorry if my memory is vague since I was dealing with a similar issue 8+ years ago. I think I reran the document 'weaving' twice. In what I can remember, latex, on the first run, creates an index and inserts the values into the document only on the second run. This was outside R, so you may need to do some magic here, e.g. keep the intermediate files when rendering.Callimachus
thanks but there has to be a fix using rmarkdown or bookdown.. some weird option or somethingRoxie
Why did you roll back the changes? Tried to remove a lot of the extra details which weren't relevant to the questionSuperelevation
@MichaelHarper sorry buddy but you removed way too many things. The question is fine as is, and the screenshot is useful. I appreciate your initiatiive, but leave it beRoxie
What else is relevant? At least remove all the LaTeX packages other than floatSuperelevation
who cares bro? the question is OK like that frankly. its a nice template for other people to useRoxie
Its just misleading that it's about a very particular issue, not about cross-referencing in general. I'll make sure not to edit your questions in the future :)Superelevation
haha ok lets settle with that tiny revision thenRoxie
but please dfirst check that it compiles like in my example when you use render()Roxie
sorry bro but these packages are needed for render !Roxie
It rendered fine for me without the added packages (imgur.com/8a9EQGR). Potentially one of your packages is out-of-date, but fair enough, revert the questionSuperelevation
B
6

The problem is that you are working against the intentions of kable by using it outside of an R chunk:

The kable() function will automatically generate a label for a table environment, which is the prefix tab: plus the chunk label.

https://bookdown.org/yihui/bookdown/tables.html

So the following workaround is definitely on the hacky side. Using a file foo.Rmd with

---
output:
  bookdown::pdf_document2:
    toc: no
header-includes:
- \usepackage{float}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```


## Including Tables

You can also embed tables, for example:  \@ref(tab:tw)

```{r tw, echo=FALSE}
mytable
```

You can also embed tables, for example:  \@ref(tab:tw2)

```{r tw2, echo=FALSE}
mytable2
```

Referencing images is easier: \@ref(fig:plt)

```{r plt, echo=FALSE, fig.cap = 'hello', fig.height=3} 
myplot 
``` 

one can process this file with a second file foo.R:

library(knitr)
library(kableExtra)
# add the label to the options that would normally be populated from the chunk options
opts_current$append(list(label = "tw"))
mytable <- head(cars) %>% kable(format = "latex", 
                                booktabs = T, 
                                caption = "Demo Table", 
                                escape = F) %>%
  kable_styling(latex_options = 'HOLD_position')
opts_current$restore()

opts_current$append(list(label = "tw2"))
mytable2 <- tail(cars) %>% kable(format = "latex", 
                                booktabs = T, 
                                caption = "Demo Table", 
                                escape = F) %>%
  kable_styling(latex_options = 'HOLD_position')
opts_current$restore()

myplot <- ggplot(cars, aes(x = dist, y = speed)) + geom_point()

rmarkdown::render("foo.Rmd")

In principle, you can do these commands also just at the R prompt, but I try to not use the prompt directly. BTW, I do not get the (#tab) output with your code.

However, I think it makes more sense to not work against the workings of kable. I can understand that it can make sense to separate the data manipulation fro the presentation. However, creating the table is presentation from my point of view. So instead of creating the table externally I would just create the data externally. To make this concrete, let's use a file bar.Rmd:

---
output:
  bookdown::pdf_document2:
    toc: no
header-includes:
- \usepackage{float}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(kableExtra)
```

## Including Tables

You can also embed tables, for example:  \@ref(tab:tw)

```{r tw, echo=FALSE}
mydata %>% kable(format = "latex", 
                 booktabs = T, 
                 caption = "Demo Table", 
                 escape = F) %>%
  kable_styling(latex_options = 'HOLD_position')
```

together with a file bar.R:

# insert data processing here
mydata <- head(cars)
rmarkdown::render("bar.Rmd")

This gives me the same output and the data processing is (initially!) separated from the presentation.

Baecher answered 23/9, 2018 at 20:2 Comment(13)
on a side note, rendering indirectly via render is really really useful. Embedding my full code into each chunk is really cumbersone and pretty inefficient at the exploratory analysis stage. That is why I am using this. Thanks Ralf!Roxie
also Ralf, do you know how to get rid of the toc? I tried with toc:false without successRoxie
Ralf I cannot get it to work. Where you do you run the knitr:::opts_current$append(list(label = "label1", label = "label2"))? In the rmarkdown or in the external R code?Roxie
very nice, thanks. and I guess if I have multiple tables and figures I just keep adding them like knitr:::opts_current$append(list(label = "label1", label = "label2", label = "label3")) ?Roxie
ha actually just tried that and it does not work. Should I add the labels one by one with this command? EDIT. yes :)Roxie
EDIT 2. ha actually just tried that and it does not work. that seems to add some extra Table 1 labels in the plot. Should the opts_current be reset at some point? I feel the only viable solution might be to run the kable line within the chunk, as you suggest in the second part of the answer. Still a good solution thoughRoxie
@Kilroy See the updated answer. I also found out that opts_current isn't internal after all, but I still prefer the second approach.Baecher
thanks Ralf. I actually tried to show a plot in the second case (instead of another table), and the restore_ trick does not seem to work here. Do you see a way?Roxie
say using opts_current$append(list(label = "plt")) where plt is ````{r plt, echo=FALSE} myplot ``` `Roxie
and myplot is simply myplot <- ggplot(cars, aes(x = dist, y = speed)) + geom_point() Roxie
@Kilroy With figures you use the code chunk option fig.cap to set the caption, which will also assign the label. all this happens within the Rmd file.Baecher
I just tried that {r plt, echo=FALSE, fig.cap = 'hello'} with no success. do you see the \@ref(tab:plt) call working for you?Roxie
@Kilroy Works for me. See updated answer. Of course, you have to use the right prefix, i.e. \@ref(fig:plt).Baecher

© 2022 - 2024 — McMap. All rights reserved.