9

Is it possible to move the caption above my figure when knitting to HTML in RMarkdown? It seems that it is possible in PDF, ie when knitting to PDF, but I can't figure out how to replicate this for HTML. I am using bookdown to number figures.

When I run something like this:

```{r fig.cap= "caption"}
df <- data.frame(letter = letters[1:5], value = 1)

ggplot(df, aes(as(factor(1), value, fill = letters))) +
  geom_bar(stat = "identity")

```

the caption is displayed at the bottom of the figure, but I would like to display it above the figure.

Bjørn Kallerud
  • 979
  • 8
  • 23
  • 1
    This seems reasonable to want the option of captions above. The convention of below for figures but above for tables need not be enforced! It was discussed as an issue at [bookdown github](https://github.com/rstudio/bookdown/issues/379), but closed without resolution. – Mark Neal Feb 27 '20 at 04:06
  • 1
    Code here seems to work for me. https://stackoverflow.com/questions/22335542/knitr-figure-captions-above – dyrland Feb 28 '20 at 20:56
  • Thanks @dyrland, though the figure numbering that is provided with **bookdown** is suppressed by that solution. – Mark Neal Mar 01 '20 at 22:41

4 Answers4

11

For HTML output, you may set the chunk option fig.topcaption = TRUE to place captions above figures. Below is a minimal example (it works for both html_document and bookdown's HTML output formats):

---
title: "Reprex"
output:
  html_document: null
  bookdown::html_document2: null
---

```{r, fig.cap='A caption.', fig.topcaption=TRUE}
plot(cars)
```

Caption above the figure

Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
  • 1
    That was much easier than I imagined! Unfortunately the use of **ggplotly** to turn a `ggplot()` into a **plotly** object appears to send the title back to the bottom, though numbering is retained. I'll try and modify the example in my answer to show this. – Mark Neal Mar 04 '20 at 01:46
  • 1
    See lower section of answer [here](https://stackoverflow.com/a/60480459/4927395) with an example that shows that the caption reverts to the bottom with a **plotly** object. – Mark Neal Mar 04 '20 at 02:03
  • 1
    Solution for plotly object with some css [here](https://stackoverflow.com/a/60706888/4927395) – Mark Neal Apr 07 '20 at 23:35
  • Is the `fig.topcaption` hook documented anywhere? – camille Feb 23 '21 at 18:39
4

You can do that, but if you set echo = TRUE, the caption will appear above the code...

---
title: "Untitled"
author: "Stéphane Laurent"
date: "29 février 2020"
output: html_document
---

```{r setup, include=FALSE}
knitr::knit_hooks$set(htmlcap = function(before, options, envir) {
  if(before) {
    paste('<p class="caption">', options$htmlcap, "</p>",sep="")
  }
})
```

```{r, echo = FALSE, htmlcap="Hello Dolly"}
library(ggplot2)
ggplot(diamonds,aes(price,carat)) + geom_point()
```
Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • 1
    The answer proposed by Stéphane, and the answer from the link suggested by dyrland, both move the caption above the figure. The downside of Stephane's solution (as mentioned) is that it appears above the code if ```echo=TRUE```. A bigger downside is that with both solutions the automatic numbering provided by **bookdown** appears to be supressed. As it is not my question, I think I will make an answer that shows this. – Mark Neal Mar 01 '20 at 22:24
3

This is not a successful answer, but code output shows that the choice of a bookdown format, that would otherwise number the figure, is suppressed by inclusion of the solution proposed by Stéphane, or, as below, solution from the link suggested by dyrland.

---
title: "Untitled"
author: "Internet"
date: "29 février 2020"
output:
  bookdown::html_document2
---

```{r setup2}
#https://stackoverflow.com/a/26743812/4927395 
library(knitr)
knit_hooks$set(plot = function(x, options) {
  paste('<figure><figcaption>', options$fig.cap, '</figcaption><img src="',
        opts_knit$get('base.url'), paste(x, collapse = '.'),
        '"></figure>',
        sep = '')
}) #comment out to restore numbering

library(ggplot2)
```

```{r, echo = TRUE, fig.cap="Hello Dolly"}
ggplot(diamonds,aes(price,carat)) + geom_point()
```

Can a solution include both the numbering and the caption at the top?

N.B Author of the original question did mention bookdown, but did not provide a full working example to demonstrate this. Happy to edit original question if other think that would be more useful.

EDIT Yihui has shown in his answer that there is an easy option with fig.topcaption=TRUE - Thanks! Unfortunately that caption, though it retains correct numbering, is still pushed to the bottom (in the case of plotly figures). Example below:

---
title: "Untitled"
author: "Internet"
date: "29 février 2020"
output:
  bookdown::html_document2
---

```{r setup, message=FALSE, echo=FALSE}

library(knitr)
library(ggplot2)
library(plotly)

```

Here is a ggplot object with caption at the top as desired.


```{r, fig.cap="Hello ggplot", fig.topcaption=TRUE, message=FALSE, echo=FALSE}
ggplot(diamonds,aes(price,carat)) + geom_point()
```

Here is the previous ggplot converted to a plotly object with caption reverting to the bottom.


```{r, fig.cap="Hello plotly", fig.topcaption=TRUE, message=FALSE, echo=FALSE}
my_ggplot <- ggplot(diamonds,aes(price,carat)) + geom_point()
ggplotly(my_ggplot)
```

Caption reverts to bottom even if plotly object is not created from ggplot object

```{r, fig.cap="Hello plotly2", fig.topcaption=TRUE, message=FALSE, echo=FALSE}
plot_ly(
  x=c(1,2,3),
  y=c(5,6,7),
  type='scatter',
  mode='lines')
```

EDIT 2 Plotly caption location issue solved with css here

Mark Neal
  • 996
  • 16
  • 52
2

Hardcoding a function is function is never my preferred option, but it works (Create another function for Tables.) (Source code here: R Markdown HTML Number Figures)

Now, to figure out how to reference these...

---
title: "Untitled"
author: "Internet"
date: "29 fevrier 2020"
output:
  bookdown::html_document2
---

```{r setup2}
#https://stackoverflow.com/a/26743812/4927395 
library(knitr)
library(ggplot2)

capFigNo <- 1
capFig <- function(x){
      x <- paste0("Figure ",capFigNo,": ",x)
    capFigNo <<- capFigNo + 1
    x
}


knit_hooks$set(plot = function(x, options) {
  paste('<figure><figcaption>',
        options$fig.cap,
        '</figcaption><img src="',
        opts_knit$get('base.url'),
        paste(x, collapse = '.'),
        '"></figure>',
        sep = '')
}) #comment out to restore numbering

```


```{r echo = TRUE, fig.cap=capFig("Hello Dolly")}
#the trick is to wrap your caption in your number prefixing function.
ggplot(diamonds,aes(price,carat)) + geom_point()
```

```{r echo = TRUE, fig.cap=capFig("Hello Dolly2")}
ggplot(diamonds,aes(price,carat)) + geom_point()    
```
Mark Neal
  • 996
  • 16
  • 52
dyrland
  • 608
  • 1
  • 7
  • 17
  • 2
    Thanks @dyrland. That will work, though for numbering within chapters I'd have to modify code to number figures (eg in chapter 5 we'd have 5.1, 5.2 possibly to 5.11). I am hoping that someone more capable than I can work a general solution into the [github issue](https://github.com/rstudio/bookdown/issues/379) which is latex oriented and now closed. Though maybe it needs a new issue to specify a html solution - thoughts? – Mark Neal Mar 02 '20 at 22:41
  • 1
    The real answer is to find where the numbering is actually stored (options$something or opts_knit$get("something") and put that in front of `options$fig.cap`. I just haven't been able to find where it is stored. – dyrland Mar 03 '20 at 13:42
  • 1
    To number within a chapter add `ChapterNo <- 5` (i.e. for chapter 5), and edit second line of `capFig` function to this `x <- paste0("Figure ",ChapterNo,".",capFigNo,": ",x)`. Still hoping for a more general solution though! – Mark Neal Mar 04 '20 at 01:20