Introducing kfigr

a streamlined, knitr-integrated cross-referencing system for HTML documents

What does it do?

kfigr provides cross-referencing functionality for Rmarkdown documents by creating HTML anchor tags for code chunks. kfigr is designed to provide a simple and flexible indexing system integrated seamlessly with knitr.

How does it work?

kfigr provides just one function and one hook (code chunk option) to anchor a chunk. Any chunk can be anchored regardless of the output. Chunk labels are used for indexing, so there is no way to get confused about what you are referencing. Defining anchors as a chunk option forces users to write distinct chunks for any referenced output, which improves readability of the source document. Users have complete flexibility to define numbering groups for distinguishing between e.g. figures and tables.

kfigr defines a custom hook, anchor, which is used to decide how to track the chunk. We use this hook to define a “type” for the chunk. For instance, if I create a plot and want to refer it later I can pass the chunk option anchor="figure". kfigr uses the chunk label to generate an HTML anchor tag above the chunk and assigns a number to the chunk based on its type. As an example, consider the following chunk.

require(ggplot2)
qplot(rnorm(100), geom="histogram")

I named the chunk “first-chunk” and used the chunk option anchor="figure". Now, I will use the function figr('first-chunk') in an inline chunk to reference the chunk here: 1. The figr function returns the number of the referenced chunk as a markdown link, e.g. [1](#first-chunk). kfigr keeps track of reference numbers by tracking the chunk placement sequence separately for each “type”. Note that the value of the anchor option is case sensitive, so “Figure” is different from “figure”.

If you need to, you can reference a chunk before you define it. You must specify the “type” when referencing a later chunk, and it is up to you to ensure every chunk you reference is defined. Furthermore, if you want to refer to e.g. the sixth “figure” chunk, you must first reference chunks 1-5 of type “figure”. This can be done inline using invisible(figr(...)). This limitation is due to the way markdown documents are rendered; if you need smarter referencing capabilities, consider using LaTeX (Rnw) instead of markdown (Rmd).

You can pass prefix=TRUE or change the global options to get a full label, e.g. figure 2.

qplot(runif(100), geom="density")

You can reference any kind of chunk output, and specify any “type” of chunk. For example, look at table 1:

kable(head(iris, 6))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

Anchoring even works for chunks that use the ref.label option. For instance, consider the following code:

x = 1:20
y = x + rnorm(20)
lm(y~x)

I have not anchored the above chunk and the code was not evaluated because I used the chunk option eval=FALSE. If I had used echo=FALSE you would have no idea that the chunk even existed! But I can reproduce the chunk by creating an empty chunk with the same name (see the knitr documentation for more details) and anchor the empty chunk, as shown in block 1.

x = 1:20
y = x + rnorm(20)
lm(y~x)
## 
## Call:
## lm(formula = y ~ x)
## 
## Coefficients:
## (Intercept)            x  
##     -0.6464       1.0136

If you want to reference both the code and the output of a chunk separately you can use the chunk option ref.label and specify an anchor for each referring chunk. Below, I create a hidden chunk and reference it using eval=FALSE to produce the code block. I then reference the hidden chunk a second time using echo=FALSE to produce only the output.

df <- data.frame(x=x, y=y)
ggplot(df, aes(x=x, y=y)) + geom_smooth(method="lm") + 
geom_point(pch=21, color="black", fill="red")

You can verify that block 2 and figure 3 are distinct.

kfigr tracks references internally. If you want to get a list of indexed chunks, you can use anchors() to return a structure that lists the labels by type, a reference history, and an index, as shown in block 3. The history attribute can be helpful for troubleshooting problems with document references.

anchors("index")
##          label   type number
## 1  first-chunk figure      1
## 2 second-chunk figure      2
## 3  third-chunk  table      1
## 4 fourth-chunk  block      1
## 5   fifth-code  block      2
## 6   fifth-plot figure      3
## 7   last-chunk  block      3
anchors("history")
##           label   type number referenced.by
## 1   first-chunk figure      1   hook_anchor
## 2   first-chunk figure      1          figr
## 3  second-chunk figure      2          figr
## 4  second-chunk figure      2   hook_anchor
## 5   third-chunk  table      1          figr
## 6   third-chunk  table      1   hook_anchor
## 7  fourth-chunk  block      1          figr
## 8  fourth-chunk  block      1   hook_anchor
## 9    fifth-code  block      2   hook_anchor
## 10   fifth-plot figure      3   hook_anchor
## 11   fifth-code  block      2          figr
## 12   fifth-plot figure      3          figr
## 13   last-chunk  block      3          figr
## 14   last-chunk  block      3   hook_anchor

### Setting global options ###

kfigr uses knitr options to maintain global default settings. kfigr option names are identified by the prefix “kfigr”. Two options are available: kfigr.link to display a link to the anchor in citations; and kfigr.prefix to include the prefix when referencing. These options can be changed using opts_knit$get and opts_knit$set. Any global options set within a chunk will not be available in that particular chunk (see the knitr documentation for more details).

A note on tangling

An important limitation of tangle is that inline code chunks are not evaluated. Under most circumstances the tangled code should produce the same reference numbers as the knitted document, but discrepancies will appear if you have forgotten to invisibly reference intermediate chunks, or have anchored chunks in a different order than referenced.

Still confused?

Take a look at the .Rmd source file for this vignette and all should become clear.