'knitr/rmarkdown/Latex: How to cross-reference figures and tables?

I'm trying to cross-reference figures and tables in a PDF produced with knitr/rmarkdown. There are some questions on SO and tex.stackexchange (here and here, for example), that suggest the way to do this inline is to add \ref{fig:my_fig}, where my_fig is the chunk label. However, when I try that in my rmarkdown document, I get ?? where the figure number should be. I'd like to find out how to get cross-referencing to work properly.

A reproducible example is below. There are two files: the rmarkdown file plus a header.tex file that I've included just in case it affects the answer (though I have the same problem whether I include the header.tex file or not).

In the rmarkdown file there are three cross-reference examples. Example 1 is a figure for which cross-referencing fails (?? is displayed instead of the figure number). There's also a second, commented-out attempt (based on this SO answer), where I try setting the figure environment, label, and caption with latex markup before and after the chunk, but this results in a pandoc error when I try to knit the document. The error is:

! Missing $ inserted.
<inserted text> 
                $
l.108 ![](testCrossRef_

Example 2 uses xtable and cross-referencing works. Example 3 uses kable and cross-referencing fails.

A screenshot of the PDF output is included at the bottom of this post.

rmarkdown file

---
title: | 
  | My Title  
author: | 
  | eipi10  
  | Department of Redundancy Department  
date: "`r format(Sys.time(), '%B %e, %Y')`"
output: 
  pdf_document:
    fig_caption: yes
    includes:
      in_header: header.tex
    keep_tex: yes
fontsize: 11pt
geometry: margin=1in
graphics: yes
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message=FALSE, warning=FALSE, fig.height=2, fig.width=4)
```

# Example 1. Figure

This is a report. Take a look at Figure \ref{fig:fig1}.  

```{r fig1, echo=FALSE, fig.cap="This is a caption"}
plot(mtcars$wt, mtcars$mpg)
```

<!-- Now, let's take a look at this other plot in Figure \ref{fig:fig2}. -->

<!-- \begin{figure} -->
<!-- ```{r fig2, echo=FALSE} -->
<!-- plot(mtcars$cyl, mtcars$mpg) -->
<!-- ``` -->
<!-- \caption{This is another caption} -->
<!-- \label{fig:fig2} -->
<!-- \end{figure} -->

# Example 2: `xtable`

Some more text. See Table \ref{tab:tab1} below. 

```{r echo=FALSE, results="asis"}
library(xtable)
print.xtable(
  xtable(mtcars[1:3,1:4], label="tab:tab1", caption="An xtable table"), 
  comment=FALSE)
```

# Example 3: `kable`

Some more text. See Table \ref{tab:tab2} below. 

```{r tab2, echo=FALSE}
library(knitr)
kable(mtcars[1:3,1:4], caption="A `kable` table")
```

header.tex file

% Caption on top
% https://tex.stackexchange.com/a/14862/4762
\usepackage{floatrow}
\floatsetup[figure]{capposition=top}
\floatsetup[table]{capposition=top}

PDF output

enter image description here



Solution 1:[1]

You can use the output format bookdown::pdf_document2 instead of pdf_document, and the syntax for referencing a figure is \@ref(fig:chunk-label); see the documentation for details: https://bookdown.org/yihui/bookdown/figures.html

Solution 2:[2]

Following I can't generate \label{fig:mwe-plot} with knitr, adding \label{...} to the caption arguments will produce labels in the underlying tex file, i.e.

```{r fig1, echo=FALSE, fig.cap="\\label{fig:fig1}This is a caption"}
plot(mtcars$wt, mtcars$mpg)
```

and

```{r tab2, echo=FALSE}
library(knitr)
kable(mtcars[1:3,1:4], caption="\\label{tab:tab2}A `kable` table")
```

Solution 3:[3]

You can try the captioner package. You can find examples in this link.

In my case, I include a code chunk with:

table_captions <- captioner::captioner(prefix="Tab.")
figure_captions <- captioner::captioner(prefix="Fig.")

t.ref <- function(label){
  stringr::str_extract(table_captions(label), "[^:]*")
}

f.ref <- function(label){
  stringr::str_extract(figure_captions(label), "[^:]*")
}

I include captions in tables and figures when defining code chunks, like this:

```{r chunk_creating_one_figure, echo=FALSE, fig.cap=figure_captions("one_figure", "figure label")}
plot(1)
```

or

```{r chunk_creating_one_table, echo=FALSE, fig.cap=table_captions("one_table", "table label")}
knitr::kable(data.frame(col="something"), format="markdown")
```

References are included as inline_text all across my Rmarkdown with:

As shown in figure `r f.ref("one_figure")`
Data is shown on table `r t.ref("one_table")`

Solution 4:[4]

After much searching I stumbled on a solution that seems to work (link for this approach), even though I never found it in any of the bookdown standard references (I'll keep looking). Just above the chunk that creates the plot in the .rmd file, define the caption as a string, here called "crossref2" and put the Figure reference in the string in the normal way, \@ref(fig:myChunkLabel). No quotes are needed. Here's an example where Fig207 is the chunk label for the figure being referenced:

(ref:crossref2) Variations on boxplot using data from Figure \@ref(fig:Fig207) a) plain violin plot, b) enchanced violin plot, c) add outliers, d) Beeswarm plot

Then in the chunk header for the figure where you want Fig207 to be included in the caption, the figure caption is written as

fig.cap='(ref:crossref2)'  

After doing this, the Figure number connected to ref(fig:Fig207) is displayed nicely in the caption.

Note that the same procedure allows you to put a bibliography reference in a figure caption by replacing \@ref(fig:Fig207) with [@bibref] where bibref is the keyword for the reference as defined in your .bib file of literature citations.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Yihui Xie
Solution 2 Community
Solution 3 Gitanoqevaporelmundoentero
Solution 4 Dr Dave