rmarkdown resize plot inside of code chunk - r

I have an rmarkdownfile with a chunck that has a loop that creates many pages. Below is a toy example. See the "loop_chunk" code chunk. The "loop_chunk" has fig.width=9, fig.height=6, results="asis" and I am running into a problem where i need to reduce the size of a plot inside loop_chunk. All plots are 9x6 but I need to adjust one plot. I found the codee below: http://michaeljw.com/blog/post/subchunkify/
and I tried using it below but when you run the code you can see that there are 2 plots on pages 3 and 5 and there should not be. it is somehow not keeping the \newpages. There should be 1 plot on pages 2,3,4 and 5. There should only be 5 pages.
Any idea how to fix this?
---
title: "Untitled"
output: pdf_document
toc: yes
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE , comment = NA, message= FALSE, warning = TRUE)
subchunkify <- function(g, fig_height=7, fig_width=5) {
g_deparsed <- paste0(deparse(
function() {g}
), collapse = '')
sub_chunk <- paste0("
`","``{r sub_chunk_", floor(runif(1) * 10000), ", fig.height=", fig_height, ", fig.width=", fig_width, ", echo=FALSE}",
"\n(",
g_deparsed
, ")()",
"\n`","``
")
cat(knitr::knit(text = knitr::knit_expand(text = sub_chunk), quiet = TRUE))
}
data = data.frame(group= c("A","A"), value = c(1,3))
```
```{r loop_chunk, fig.width=9, fig.height=6, results="asis", message= FALSE, warning = FALSE}
for(i in 1:nrow(data)){
cat(paste0("\\newpage\n # Page ", i ," \n"))
plot(data$value[i])
cat("\n\n")
cat(paste0("\\newpage\n ## page with smaller plot \n\n"))
cat("Here is some text on this page for the smaller plot.")
cat("\n\n")
data2 = data.frame(x = 7, y = 900)
library(ggplot2)
myplot = ggplot(data2, aes(x = x, y = y ))+geom_point()
subchunkify(myplot , 4,4 )
# print(myplot) -> IS there a way to just reduce the height and width with print()?
cat("\n\n")
}
```

Using your subchunkify() function for the graphics::plot call outputs those plots to the intended pages. Replacing plot(data$value[i]) in your second chunk with
subchunkify(plot(data$value[i]), 5, 5)
outputs the 5 pages with plots as intended (where height & width are set to 5/can be edited to conditionally set dimensions for a specific plot).

Related

fig.width and fig.height in R Markdown flex_dashboard

I'm having trouble getting fig.width and fig.height to work in R Markdown with flex_dashboard output, if I generate tabs using R code with results="asis".
In the following example, for the "A" page (with tabs entered manually), fig.width and fig.height work as I would expect. But on the "B" page (with the tabs generated using R code and results="asis"), fig.width and fig.height don't work as I would expect.
---
title: "Report"
output:
flexdashboard::flex_dashboard:
vertical_layout: scroll
---
```{r, include=FALSE}
library(ggplot2)
library(flexdashboard)
d <- data.frame(x = rep(c("g1", "g2"), each = 50),
y = rnorm(100))
```
# A
## A tabs {.tabset}
### g1
```{r, echo=FALSE, fig.width = 4, fig.height = 12}
qplot(y, data = d[d$x == "g1", ])
```
### g2
```{r, echo=FALSE, fig.width = 4, fig.height = 12}
qplot(y, data = d[d$x == "g2", ])
```
# B
## B tabs {.tabset}
```{r, echo=FALSE, fig.width = 4, fig.height = 12, results = "asis"}
for (g in c("g1", "g2")) {
cat(paste0("\n\n### ", g, "\n"))
print(qplot(y, data = d[d$x == g, ]))
}
```
The screenshot below is from the "A" page. Because of the fig.height=12, the image is tall and the window can be scrolled vertically, which is what I want.
The screenshot below is from the "B" page. Here the image is proportionally tall, but it doesn't occupy the full height of the browser window, which is not what I want.
I want to generate tabs using R code and results="asis" (as on the B page), but I want fig.height to make the image large (as on the A page). How can I do this?
I'm using rmarkdown version 2.14, flexdashboard version 0.5.2, and ggplot2 version3.3.6, in R version 4.2.1.
Thanks.
You can write a function create_tab that will create the code for each tab given a plot object qp, fig.height, fig.width and a tab name. And then in the for loop, you can pass the plot object in the create_tab function.
---
title: "Report"
output:
flexdashboard::flex_dashboard:
vertical_layout: scroll
---
```{r, include=FALSE}
library(ggplot2)
library(flexdashboard)
d <- data.frame(x = rep(c("g1", "g2"), each = 50),
y = rnorm(100))
create_tab <- function(g, fig_height=4, fig_width=12, tab_name) {
g_deparsed <- paste0(deparse(
function() {g}
), collapse = '')
tab_chunk <- paste0(
"\n\n### ", tab_name, "\n",
"```{r tab_chunk_", tab_name,
", fig.height=", fig_height, ", fig.width=", fig_width, ", echo=FALSE}",
"\n(",
g_deparsed
, ")()",
"\n```"
)
cat(knitr::knit(text = knitr::knit_expand(text = tab_chunk), quiet = TRUE))
}
```
# A
## A tabs {.tabset}
### g1
```{r, echo=FALSE, fig.width = 4, fig.height = 12}
qplot(y, data = d[d$x == "g1", ])
```
### g2
```{r, echo=FALSE, fig.width = 4, fig.height = 12}
qplot(y, data = d[d$x == "g2", ])
```
# B
## B tabs {.tabset}
```{r, echo=FALSE, results = "asis"}
for (g in c("g1", "g2")) {
qp <- qplot(y, data = d[d$x == g, ])
create_tab(qp, tab_name = g)
}
```
Acknowledgement
I have taken the code for the create_tab function from this blog post and did a very slight modification for creating flexdashborad tabs. Please read that post to understand in detail how the code in that function works.

Subplots in bookdown repeating the caption

The section of the bookdown manual on generating figures demonstrates a case where include_graphics() can be given a vector of paths of length > 1, producing a number of subplots with a single caption:
However, when I try this in my fork of thesisdown, in the PDF output I get the figure caption (and, judging by the spacing, the entire figure environment) repeated for each subplot. Here is a reproducible example:
---
output: bookdown::pdf_document2
toc: false
---
```{r, echo = FALSE}
for(i in 1:3){
jpeg(filename = paste0("temp_", i, ".jpg"), width = 600, height = 250)
plot(cars)
title(main = i)
dev.off()
}
```
```{r fig.cap = "Caption", out.width="100%", fig.ncol = 1, echo = FALSE}
knitr::include_graphics(paste0("temp_", 1:3, ".jpg"))
```
I was hoping more for the five images stacked, with a single caption at the bottom. This also appears to be breaking the figure cross-referencing, as each plot has its own figure number and cross-references to the chunk render as ??.
Getting subfigures requires a few additional settings to be set in the chunk header.
fig.subcap is a list of the captions for subfigures
fig.ncol: the number of columns of subfigures
out.width: the output width of the figures. You will normally set this 100% divided by the number of sub columns.
Subfigures are built using the subfig package. You can either include this within your LaTeX bookdown template, or alternative you can added it to the YAML as follows:
Here is an example:
---
output: bookdown::pdf_document2
toc: false
header-includes:
- \usepackage{subfig}
---
```{r, echo = FALSE}
for(i in 1:3){
jpeg(filename = paste0("temp_", i, ".jpg"), width = 600, height = 250)
plot(cars)
title(main = i)
dev.off()
}
```
```{r fig.cap = "Caption", out.width="100%", fig.ncol = 1, echo = FALSE, fig.subcap= c("First", "Second", "Third")}
knitr::include_graphics(paste0("temp_", 1:3, ".jpg"))
```

Pixiedust table in loop in r markdown not rendering

Relevant to the problem, I have a dataset with factors of states ("Massachusetts", "California", etc) and 2 fields of values. I would like to create a graph for each state with a table below it showing the associated fields and the difference between those fields.
I found that using a loop seems to require a results = 'asis' option and a cat(" \n") at the end of the loop to print the images. That works OK. However, the only way I can seem to get a table is if I use xtable or kable. I would like to use pixiedust to color and otherwise beautify the table.
Here is a minimal example:
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(pixiedust)
library(ggplot2)
library(knitr)
library(xtable)
df <- data.frame(state = c("MA", "CA"), last_year = c(105, 90), this_year = c(110, 85))
```
# Here is the loop
```{r loops, results = 'asis', echo = FALSE}
for (i in 1:nrow(df)){
state_dat <- df[i,]
p1 <- ggplot(state_dat, aes(last_year, this_year)) +
geom_point()
print(p1)
cat(" \n")
tab <- data.frame(last_year = state_dat$last_year, this_year = state_dat$this_year, yoy_percent = 100*(state_dat$this_year - state_dat$last_year)/state_dat$last_year)
dust(tab) %>%
sprinkle(rows = 1, bg = "orchid")
cat(" \n")
print(kable(tab, row.names = FALSE, align = "c"))
cat(" \n")
print(xtable(tab, auto = TRUE),type = "html", comment = FALSE, include.rownames = F)
cat(" \n")
}
```
I also tried assigning the result of the dust commands to an object and printing that:
pixie <- dust(tab) %>%
sprinkle(rows = 1, bg = "orchid")
print(pixie)
cat(" \n")
to no avail.
Can pixiedust tables be produced as html in a chunk with option asis? Is there another workaround to produce a table and a graph in a loop?
Yes, this can be done. To get there, you have to turn off the asis printing in the print.dust method. This can be done with:
dust(tab) %>%
sprinkle(rows = 1, bg = "orchid") %>%
print(asis = FALSE) %>%
cat()
In time, I hope to come up with a better solution.

Suppress alt text for GIFs in rmarkdown HTML output

I'm generating GIFs using the gganimate package within an RMarkdown file. When using output = github_document in the front matter, the GIF appears as expected in the output (github-document-output). However, when using output = html_document, the GIF generates with alt text, which defaults to the chunk name (html-document-output).
Is there a way to suppress this automatic caption? I've tried setting my own caption using the fig.cap chunk option, but that was unsuccessful.
RMarkdown code
---
output:
html_document: default
github_document: default
---
```{r}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "output/test-fig-",
cache.path = "output/test-cache-"
)
```
```{r cache = FALSE}
library(knitr)
library(animation)
ani.options(autobrowse = FALSE, interval = 1)
opts_knit$set(animation.fun = function(x, options, format = "gif") {
x = c(knitr:::sans_ext(x), knitr:::file_ext(x))
fig.num = options$fig.num
format = sub("^[.]", "", format)
fig.fname = paste0(sub(paste0(fig.num, "$"), "*", x[1]),
".", x[2])
mov.fname = paste0(sub(paste0(fig.num, "$"), "", x[1]), ".",
format)
# order correctly
figs <- Sys.glob(fig.fname)
figs <- figs[order(as.numeric(stringr::str_match(figs, paste0("(\\d+)\\.", x[2]))[, 2]))]
animation::im.convert(figs, output = mov.fname)
sprintf("![%s](%s)", options$label, paste0(opts_knit$get("base.url"), mov.fname))
})
opts_chunk$set(cache = TRUE, message = FALSE, warning = FALSE, fig.show = "animate")
```
```{r pkgs, cache = FALSE}
library(gapminder)
library(ggplot2)
theme_set(theme_bw())
```
```{r setup}
p <- ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, color = continent, frame = year)) +
geom_point() +
scale_x_log10()
```
```{r dependson = "setup"}
library(gganimate)
gg_animate(p)
```
The problem here is that you include the resulting animation with markdown syntax. This introduces some iiritations I guess.
Taking a look at hook_plot_html we can simulate the default output for standard plots:
sprintf(paste0('<div class="figure %s">',
'<img src="%s">',
'<p class="caption">%s</p>',
'</div>'), options$fig.align, mov.fname, options$fig.cap)

Loops in Rmarkdown: How to make an in-text figure reference? Figure captions?

{r setup, include=FALSE, message=FALSE, results="hide"}
knitr::opts_chunk$set(echo = TRUE)
library(knitr)
library(kfigr)
library(dplyr)
library(png)
library(grid)
library(pander)
library(ggplot2)
Question
Loops in rmarkdown: in-text figure reference? figure captions?
Goal
Use a for loop to create sections with text, in-text results, and multiple figure references with associated figure captions in the figure list. The figure references/numbering should be seemless with figures numbered before and after these new sections.
Note: The figures referenced in the for loop are generated earlier in the text, saved as pngs, and then re-loaded. This might seem clunky for the purpose of this example, but the actual figs are maps and are slow to generate (I plan to comment out the loop that generates the figures once I have them how I want).
{r echo = FALSE, warnings=FALSE, message=FALSE, results="hide"}
Data: Each year we have a different number of strata, hence the need for a loop.
df <- rbind(
data.frame(strata = rep("A", 10), x = rnorm(10, mean= 10), y = rnorm(10, mean = 15),z = rnorm(10, mean = 20)),
data.frame(strata = rep("B", 10), x = rnorm(10, mean= 5), y = rnorm(10, mean = 10), z = rnorm(10, mean = 15)),
data.frame(strata = rep("C", 10), x = rnorm(10, mean= 15), y = rnorm(10, mean = 20), z = rnorm(10, mean = 10)))
first_plot: the figure that should appear in the list before for loop creates the sections by strata
first_plot <- ggplot(df, aes(x, fill=strata)) + geom_histogram()
last_plot: the figure that should appear in the list after the for loop creates the sections by strata
last_plot <- ggplot(df, aes(x = strata, y = z)) + geom_boxplot()
Figure generation (this is the part that will be commented out later in my version once I have the maps how I want)
strat <- unique(df$strata)
for (i in seq_along(strat)) {
sub <- df %>% filter(strata %in% strat[i])
fig1 <- ggplot(sub, aes(x = x, y = y)) + geom_point()
ggsave(fig1, file=paste0("fig1_", strat[i], ".png"))
fig2 <- ggplot(sub, aes(x = x, y = z)) + geom_point()
ggsave(fig2, file=paste0("fig2_", strat[i], ".png"))
}
Load the png's
df_figs <- list.files(pattern = "\\.png$")
for (i in df_figs){
name <- gsub("-",".",i)
name <- gsub(".png","",name)
i <- paste(".\\",i,sep="")
assign(name,readPNG(i))
}
Introduction section
Some introductory text in the report and a figure r figr('first_plot',TRUE, type='Figure').
```{r echo = FALSE, warnings=FALSE, message=FALSE, results = "asis"}
# Summary of results and image file names that will be references in text
results <- df %>%
group_by(strata) %>%
dplyr::summarise_each(funs(mean)) %>%
mutate(fig1 = paste0("fig1_", strata),
fig2 = paste0("fig2_", strata))
#Text template (each strata will have its own section)
template <- "# The %s stratum
The mean of *x* in %s stratum was %1.1f. Relationships between *x* and *y* and *x* and *z* can be found in `r figr('%s', TRUE, type='Figure')` and `r figr('%s', TRUE, type='Figure')`.
"
#Create markdown sections in for loop
for(i in seq(nrow(results))) {
current <- results[i, ]
cat(sprintf(template,
current$strata, current$strata,
current$x,
current$fig1, current$fig2))
}
#Also doesn't work:
template <- "# The %s stratum
The mean in %s stratum was %1.0f. Results can be found in "
template2 <- " and "
template3 <- ".
"
`figr('%s', TRUE, type='Figure')` and `figr('%s', TRUE, type='Figure')`."
#For loop
for(i in seq(nrow(results))) {
current <- results[i, ]
cat(sprintf(template,
current$strata, current$strata,
current$mean,
current$fig_1, current$fig_2))
print(paste0("`r figr(",paste0("'", current$fig1,"'"), TRUE, type='Figure'))
cat(sprintf(template2))
print(paste0("`r figr(",paste0("'", current$fig2,"'"), "TRUE, type='Figure'),`"))
cat(sprintf(template3))
}
```
Conclusion section
Some discussion text in the report and figure r figr('last_plot',TRUE, type='Figure').
Figures
*NOTE:* I don't know how to automate the looped portion of the list of figures here, so I've done it by hand.
```{r 'first_plot', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="The caption for the first figure."}
suppressMessages(print(first_plot))
```
```{r 'fig1_A', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="Caption text for fig1_A."}
grid.raster(fig1_A)
```
```{r 'fig2_A', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="Caption text for fig2_A."}
grid.raster(fig2_A)
```
```{r 'fig1_B', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="Caption text for fig1_B."}
grid.raster(fig1_B)
```
```{r 'fig2_B', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="Caption text for fig2_B."}
grid.raster(fig2_B)
```
```{r 'fig1_C', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="Caption text for fig1_C."}
grid.raster(fig1_C)
```
```{r 'fig2_C', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="Caption text for fig2_C."}
grid.raster(fig2_C)
```
```{r 'last_plot', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6, fig.cap="The caption for the last figure."}
suppressMessages(print(last_plot))
```
SOLUTIONS
Use knit_expand()
Use captioner instead of kfigr
This numbers your figures (or tables) in text and at the end of your report.
This script shows you how to create markdown paragraphs in for loops that have in-text references to figures.
It also shows you how to create custom figure captions in for loops while retaining the number order.
If you show how to do #4 and #5 using brew I will give you all the SO points.
Libraries
library(knitr)
library(dplyr)
library(png)
library(grid)
library(pander)
library(ggplot2)
library(devtools)
library(captioner)
Create a fig_nums() function using the captioner package
(https://github.com/adletaw/captioner/blob/master/vignettes/using_captioner.Rmd)
fig_nums <- captioner(prefix = "Figure")
Data
Each year we have a different number of strata, hence the need for a loop.
df <- rbind(
data.frame(strata = rep("A", 10), x = rnorm(10, mean= 10), y = rnorm(10, mean = 15), z = rnorm(10, mean = 20)),
data.frame(strata = rep("B", 10), x = rnorm(10, mean= 5), y = rnorm(10, mean = 10), z = rnorm(10, mean = 15)),
data.frame(strata = rep("C", 10), x = rnorm(10, mean= 15), y = rnorm(10, mean = 20), z = rnorm(10, mean = 10)))
first_plot: the figure that should appear in the list before for loop creates the sections by strata
first_plot <- ggplot(df, aes(x, fill=strata)) + geom_histogram()
fig_nums("first_plot", display = FALSE)
last_plot: the figure that should appear in the list after the for loop creates the sections by strata
last_plot <- ggplot(df, aes(x = strata, y = z)) + geom_boxplot()
Figure generation
Comment this section out once you have figs how you want. This step will not feel convoluted, unnatural, suboptimal, unnecessary, or like a very bad idea if you do a lot of mapping in R.
strat <- unique(df$strata)
for (i in seq_along(strat)) {
sub <- df %>% filter(strata %in% strat[i])
fig1 <- ggplot(sub, aes(x = x, y = y)) + geom_point()
ggsave(fig1, file=paste0("fig1_", strat[i], ".png"))
fig2 <- ggplot(sub, aes(x = x, y = z)) + geom_point()
ggsave(fig2, file=paste0("fig2_", strat[i], ".png"))
}
Load the png's
df_figs <- list.files(pattern = "\\.png$")
for (i in df_figs){
name <- gsub("-",".",i)
name <- gsub(".png","",name)
i <- paste(".\\",i,sep="")
assign(name,readPNG(i))
}
Introduction
Some introductory text in the report and a figure r fig_nums("first_plot", display="cite").
Results and image file names that will be referenced in text:
```{r echo = FALSE, warnings=FALSE, message=FALSE, results = "asis"}
results <- df %>%
group_by(strata) %>%
dplyr::summarise_each(funs(mean)) %>%
mutate(fig1 = paste0("fig1_", strata),
fig2 = paste0("fig2_", strata))
```
```{r run-numeric-md, warning=FALSE, include=FALSE}
#The text for the markdown sections in for loop... the knit_expand() is the work-horse here.
out = NULL
for (i in as.character(unique(results$strata))) {
out = c(out, knit_expand(text=c('#### The *{{i}}* strata',
'\n',
'The mean of *x* is ',
'{{paste(sprintf("%1.1f", results$x[results$strata==i]))}}', '({{fig_nums(results$fig1[results$strata==i],display="cite")}}).',
'\n'
)))
}
```
Creates section for each strata
`r paste(knit(text = out), collapse = '\n')`
Conclusion
Some discussion text in the report and figure r fig_nums("last_plot",display="cite").
List of Figures
`r fig_nums("first_plot",caption="Here is the caption for the first figure.")`
```{r 'first_plot', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6}
suppressMessages(print(first_plot))
```
```{r figcaps, include=FALSE}
caps = NULL
for (i in as.character(unique(results$strata))) {
caps = c(caps, knit_expand(
text=c({{fig_nums(results$fig1[results$strata==i], caption="Caption text for strata *{{i}}* goes here.")}},
'``` {r {{results$fig1[results$strata==i]}}, echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6}',
{{paste0('grid.raster(',results$fig1[results$strata==i],')')}},
'```',
'\n')))
}
#DON'T FORGET TO UNLIST!
src <- unlist(caps)
```
`r paste(knit(text = src),sep='\n')`
`r fig_nums("last_plot", caption="The caption for the last figure.")`
```{r 'last_plot', echo=FALSE, warning=FALSE, fig.width=6.5, fig.height=6}
suppressMessages(print(last_plot))
```

Resources