How to sequentially output partial (base r) plots in rmarkdown/knitr? - r

Suppose I have the following rmarkdown code:
---
title: "Untitled"
author: "Author"
date: "04/12/2019"
output: ioslides_presentation
---
## Slide title
```{r echo=FALSE}
plot(1:10, axes = FALSE, ty = "n")
axis(1)
## Next
axis(2)
## Next
points(1:10, 1:10)
```
At each of the ## Next points I would like to output the current state of the plot, so that I can sequentially reveal parts of the plot. My ultimate goal is to create subsequent slides in an ioslides presentation with the sequential plots in them, all from the code above (with the caveat that ideally I would not want later lines of code to be able affect earlier ones, as could occur above).
I would like it to have the same effect as how I'm currently solving it:
---
title: "Untitled"
author: "Author"
date: "04/12/2019"
output: ioslides_presentation
---
```{r setup, include=FALSE}
## Set up environment for running the code
env <- new.env()
## Source code to run, as a list of quotes
full_src <- list(
quote({
plot(1:10, axes = FALSE, ty = "n")
axis(1)
}),
quote({
axis(2)
}),
quote({
points(1:10, 1:10)
})
)
```
## Slide title
```{r echo=FALSE}
# Evaluate first set of lines
eval(full_src[[1]], envir = env)
# Reset environment so later code can't affect earlier code
rm(list = ls(envir = env), envir = env)
```
## Slide title
```{r echo=FALSE}
# Evaluate first and second set of lines
invisible(sapply(1:2, function(i) eval(full_src[[i]], envir = env)))
# Reset environment so later code can't affect earlier code
rm(list = ls(envir = env), envir = env)
```
## Slide title
```{r echo=FALSE}
# Evaluate all lines
invisible(sapply(1:3, function(i) eval(full_src[[i]], envir = env)))
```
The slides that are output look like this:
But as you can see, that is clunky and not very general. Important features of a solution would be that the lines of code for the plot is as close together as possible, and only written once; ideally, I could just do this with a chunk option/hook.
The best kind of answer would retain the ability to echo the new lines of code added to the graph (e.g., the ones between the ## Next markers) yet yield the whole plot up to that point.
What's the best way to achieve this?
Edit: Also, I want to be able to group changes to a plot, and not just output every to a new plot, so fig.keep won't work.

The chunk option fig.keep can take a numeric vector to index the plots, so you can choose the last three plots via fig.keep = 2:4 (or remove the first plot via fig.keep = -1).
To group plots on different slides, you can dynamically add slide titles. You can either use cat('\n\n## Title\n\n') with the chunk option results = 'asis', or with knitr::asis_output().
Below is a full example:
---
title: "Untitled"
author: "Author"
date: "04/12/2019"
output: ioslides_presentation
---
```{r, include=FALSE}
new_slide = function(title = '\n\n## Next\n\n') {
knitr::asis_output(title)
}
```
## Slide title
```{r echo=FALSE, fig.keep=2:4}
plot(1:10, axes = FALSE, ty = "n")
axis(1)
new_slide()
axis(2)
new_slide()
points(1:10, 1:10)
```
Note that there is a bug in the current version of knitr, which I just fixed on Github. For now you need to use the Github version of knitr:
if (packageVersion('knitr') <= '1.26')
remotes::install_github('yihui/knitr')

Related

R Markdown html: main and axis labels of plots as text suitable for search-function

I want to generate a R markdown html document with plots and it should be possible to jump to a certain plot by search-function (in my example there are 3 plots and I want to jump in the html-doc to the plot, where the main is "rivers").
I think, the problem is, that main and axis labels of a plot are grafical elements, like the plot itself, and not text. So the search-function doesn't work.
Of course it would be possible to add manually text before each plot, but as all my plots are generated with a for-loop, I don_t know how to do it.
is there a possibilty to include text-output in this kind of for-loop or are there other ideas, how the main or axis labels of a plot can be suitable for search-function?
thanks in advance!
---
title: "search function test"
author: "Michel Grün"
date: "last edited `r format(Sys.Date(),'%d.%m.%Y')`"
output:
html_document:
df_print: paged
---
knitr::opts_chunk$set(echo = TRUE,warning = FALSE)
df<-data.frame(x=seq(1,20),
trees=rnorm(20,4,3),
mountains=rnorm(20,6,3),
rivers=rnorm(20,4,4))
for(i in 2:length(colnames(df))){
plot(df$x,df[,i],
main=colnames(df)[i],
xlab=colnames(df)[1],
ylab=colnames(df)[i])
}
solved in another issue: https://stackoverflow.com/a/57034752/16578253
in this issue, the question is slightly different, but a solution shown there was also the solution for my problem. The idea is to create headings + outputs within a loop. As result, in the output dokument every header is followed by a plot and the header is of course suitable for search-function. It's important to use the argument results='asis' in the chunk konfiguration to allow that cat() is interpreted as Markdown syntax. Furthermore the
cat()ing tshould be surrounded by some newlines to make sure it's interpreted properly.
You can combine a svg device with a knitr hook:
---
title: "search function test"
author: "Michel Grün"
date: "last edited `r format(Sys.Date(),'%d.%m.%Y')`"
output:
html_document:
df_print: paged
---
```{r setup}
library(ggplot2)
library(knitr)
# see https://github.com/yihui/knitr/issues/754
local({
hook_plot <- knit_hooks$get("plot")
knit_hooks$set(plot = function(x, options) {
x <- paste(x, collapse = ".")
if (!grepl("\\.svg", x)) {
return(hook_plot(x, options))
}
# read the content of the svg image and write it out without <?xml ... ?>
paste(readLines(x)[-1], collapse = "\n")
})
})
opts_chunk$set(echo = TRUE, warning = FALSE, dev = "svglite")
df <- data.frame(
x = seq(1, 20),
trees = rnorm(20, 4, 3),
mountains = rnorm(20, 6, 3),
rivers = rnorm(20, 4, 4)
)
```
```{r}
for (i in 2:length(colnames(df))) {
plot(df$x, df[, i],
main =paste0(colnames(df)[i], " äöα😋"),
xlab = colnames(df)[1],
ylab = colnames(df)[i]
)
}
```

rmarkdown powerpoint: two kable on one slide

In this example each kable is produced on one slide, even though the slide is large enough for 2.
---
title: "Untitled"
author: ""
date: "2/2/2022"
output: powerpoint_presentation
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE, fig.height=5, fig.width=10)
```
## Slide with R Output
```{r}
knitr::kable(head(summary(cars),2))
```
```{r}
knitr::kable(head(summary(cars),2))
```
gives the output:
How do I make both kable on one slide one after the other.
You can use the gridExtra package https://cran.r-project.org/web/packages/gridExtra/vignettes/tableGrob.html
The documentation provides additional details about controlling formatting. I provided one example for illustration.
library(tidyverse)
library(gridExtra)
t1 <-
head(summary(cars),2) %>%
tableGrob(theme = ttheme_minimal(), rows = NULL)
tt3 <- ttheme_minimal(
core=list(bg_params = list(fill = blues9[1],
col=NA),
fg_params=list(fontface=3)),
colhead=list(fg_params=list(col="navyblue", fontface=4L)))
t2 <-
head(summary(cars),2) %>%
tableGrob(theme = tt3, rows = NULL)
grid.arrange(t1 , t2)

Is there a way to add a scroll bar to dfSummary output in R markdown file?

I've already tried using the print function:
print(dfSummary(df), method = "render")
And also all the solutions here but they don't seem to work with html_document as the output file type of the R Markdown.
Olá, Inês
The answer is in provided link. You just need to add max.tbl.height argument and specify height in pixels:
print(dfSummary(df),
max.tbl.height = 250,
method = "render")
Here goes an reproducible example (see # comments for tips & tricks):
---
title: "Title"
author: "Author"
date: "26/05/2020"
output:
html_document:
toc: TRUE
toc_float: TRUE
---
```{r setup, include = FALSE}
library(knitr)
library(summarytools)
knitr::opts_chunk$set(results = "asis")
```
### Adding a scrollbar to dfSummary
```{r summarytools-css, echo = FALSE}
# with summarytools’ CSS we can cancel Bootstrap’s CSS (both are included by default)
# without it odd layout are expected, especially with dfSummary()
st_css(bootstrap = FALSE)
```
```{r dfSummary, echo = FALSE}
print(dfSummary(tobacco,
style = "grid", # set style to “grid”
valid.col = FALSE, # drop Valid column if redundant
graph.magnif = 0.82), # zoom factor (max = 1) for bar plots and histograms
headings = FALSE,
footnote = NA,
# use maximum table (specified) height (in pixels) and wrap the results in a scrollable window
max.tbl.height = 300,
method = "render")
```
If you have interest, check out Dominic's vignette - "Recommendations for Using summarytools With Rmarkdown".
May I suggest using the package "DT", if you absolutely need dfSummary I can try again, but would need the minimal amount of code for replicate your example. DT has search functionality along with letting the user control how many rows they see when viewing the data.
---
title: "DF summary"
author: "stackoverflow"
date: "5/24/2020"
output: html_document
---
```{r}
library(DT)
datatable(head(mtcars, 30), options = list(
order = list(list(2, 'asc'), list(4, 'desc'))
))
```

wrong order of mixed textual and graphical output in the final output document

Within a for loop I decide to create some graphics and related tables.
The tables are created with writeLines() and print().
For creating the graphics I use plot(), boxplot(), mtext(), text(), axis().
So one graphic is created in many steps. Each graphic for it self is complete and nice.
My problem is:
when I knit the markdown document in Rstudio the graphics and tables are not in the correct place.
The first graphic is shown at the place, where the second should be or a little before, after cor.test.default() tells me a warning. Definitively it is shown during the next pass of the for loop.
Conclusion of the Problem
Creating textual output in adition to graphics is mixed up under special circumstances
For reproducing the problem I create some data
All the computations are collected in the "workingChunk"
For demonstrating the problem I use the chunk "loops" at the end.
So read the last chunk first
In the second loop the FIRST graphic is placed in the output
during the SECOND passage of the loop, after the function cor.test() comes up with the warning.
As well the SECOND graphic is placed in the output
during the THIRD passage of the loop, after the function cor.test() comes up with the warning.
and so on.
I found a workarround for this problem but it is not really handy:
When I replace the for loop with single chunk-calls, then the output is in the correct order.
So I'm sure that the reason for the problem is the interaction of the for loop and the function cor.test()
Here is the Example-code (about 140 lines):
---
title: "Test graphic & textual output"
output:
pdf_document: default
word_document:
html_document:
df_print: paged
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r creatingData, echo=TRUE}
### {r creatingData, echo=TRUE}
# creating some data
a.df <- data.frame(height=c(1:19),
width=c(21:39)*10,
depht=c(41:59)*20,
group=c(1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3))
a.df$groupF <- as.factor(a.df$group)
Vars <- names(a.df)[c(1:3)]
```
```{r workingChunk, echo=TRUE, eval=FALSE}
cat("\n===================================================================\n",
"following the calculations for:\n",
"YVar:",YVar, "\n Group:", "group", "\n")
# Daten für Auswertung extrahieren
selvar <- c("group","groupF", YVar)
# Datensätze mit Fehlwerten in der Zielvariablen ausschließen!
a.sub <- a.df[ complete.cases(a.df[ , selvar]), selvar]
# print(str(a.sub))
## Tabelle für gruppierte Zielgrößen
mT <- table(a.sub[ , c("groupF", YVar)])
print(ftable(addmargins(mT))) ## absolute Häufigkeiten
writeLines("\n")
if (runCorTest) {
## calculating Spearmans correlation
myCorTest <- try(cor.test(a.sub[["group"]], a.sub[[YVar]],
method = "spearman", alternative = "two.sided" ))
print(myCorTest)
writeLines("\n")
}
## preparing the grafic
GL.x1 <- levels(a.sub[["groupF"]]) ## grouplabels
# Calculating the range of Y
my.ylim <- c(min(a.sub[[YVar]], na.rm = TRUE), max(a.sub[[YVar]], na.rm = TRUE))
at.x <- c(1:length(GL.x1)) ## Labelpositionen anlegen
G.data <- vector("list", length(GL.x1)) ## Vektoren für die Daten der Gruppen anlegen
# Daten der Gruppen herausziehen
G.data <- split(a.sub[[YVar]], a.sub["groupF"])
# print(str(G.data))
## drawing emtpy plot
cat("\n\n>>> Here should be placed the Grafik for:",YVar, "<<<\n")
plot( x = NA, y = NA, type = "n",
main = YVar,
xlim = c( 1, length( GL.x1)) + c( -0.6, 0.6),
ylim = my.ylim,
xlab = NA, ylab = NA,
axes = FALSE, frame.plot = TRUE)
# X-axis
axis( 1, las = 1, labels = FALSE)
mtext(GL.x1, at = at.x, cex=0.8, side = 1, line = .3)
# Y-axis
axis( 2, las = 1)
## drawing the data
for (i in 1:length(G.data)){
boxplot(G.data[i], # col = "white",
at = at.x[i], range = 0, add = TRUE,
boxwex = 0.6, yaxs = "i", axes = FALSE)
}
```
```{r, loops, echo=FALSE}
cat("\n===================================================================",
"\n===================================================================\n",
"calling the workingChunk within a for loop without executing cor.test()",
"\n works fine!!",
"\n===================================================================",
"\n===================================================================\n")
runCorTest <- FALSE
for ( YVar in Vars) {
<<workingChunk>>
}
cat("\n===================================================================",
"\n===================================================================\n",
"calling the workingChunk within a for loop with executing cor.test() ",
"\n mixes up the textual output and the graphics!!",
"\n===================================================================",
"\n===================================================================\n")
runCorTest <- TRUE
for ( YVar in Vars) {
<<workingChunk>>
}
cat("\n===================================================================",
"\n===================================================================\n",
"calling the workingChunk with executing cor.test() ",
"\n workarround without a for loop works fine!!",
"\n===================================================================",
"\n===================================================================\n")
runCorTest <- TRUE
YVar <- Vars[1]
<<workingChunk>>
YVar <- Vars[2]
<<workingChunk>>
YVar <- Vars[3]
<<workingChunk>>
```
Not a real answer but a much shorter test case and a workaround:
---
title: "Test graphic & textual output"
output:
html_document:
df_print: paged
word_document: default
pdf_document:
keep_tex: yes
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r}
foo <- function(letter, warn) {
cat("Doing", letter, "\n")
print(letter)
if (warn) warning(letter)
cat("Graphic for", letter, "goes here", "\n")
plot(1, main = letter)
}
```
# with loop and with warning
```{r}
for (letter in letters[1:3])
foo(letter, TRUE)
```
# with loop and without warning
```{r}
for (letter in letters[1:3])
foo(letter, FALSE)
```
# without loop and with warning
```{r}
foo("a", TRUE)
foo("b", TRUE)
foo("c", TRUE)
```
# with loop and with suppressed warning
```{r warning=FALSE}
for (letter in letters[1:3])
foo(letter, TRUE)
```
Besides removing all of the data processing, I have also switched from a named chunk to a function, which I find more idiomatic in R. That does not change the strange ordering, though.
If the warning message is not important in your case, you can use the workaround shown in the end: Suppressing the warning with the chunk option warning=FALSE restores the ordering.

Replace variables with their corresponding values in source code chunk

I'd like to show parameter values and not params$... in R Markdown output. For example, the first code chunk below displays params$file in the output, but I'd like to replace that with samples.txt. I tried adding a second chunk with message, but that outputs a white code chunk and I'd like a gray background like all other R code blocks.
---
output: html_document
params:
file: samples.txt
---
```{r read, message=FALSE, collapse=TRUE, comment=""}
x <- read_tsv(params$file)
x
```
This just needs a gray background
```{r print, echo=2, collapse=TRUE, comment=""}
message('x <- read_tsv("', params$file, '")')
x
```
You could modify the source hook. A solution tailored to your need follows immediately. For a more general approach, that replaces all elements in params, scroll down.
---
output:
pdf_document: default
html_document: default
params:
file: samples.txt
---
```{r, include=FALSE}
library(knitr)
library(stringr)
default_source_hook <- knit_hooks$get('source')
knit_hooks$set(source = function(x, options) {
x <- str_replace_all(x, pattern = 'params\\$file', paste0("'",params$file,"'"))
default_source_hook(x, options)
})
```
```{r print, echo=T, comment="", eval = T}
print(params$file)
```
First we save the default hook that would be used depending on the output file type (render_html or render_latex etc.). Then we change the source hook: we replace all occurrences of params$file with its value and then throw the source code back into the default hook we saved before.
In this case this results in:
This magic works, because we only modify the source code that will be printed, not the one being evaluated!
Update: A more general Approach
I played a bit with your example and created a more general hook. It should replace all elements of the form params$... in your code chunks. It even checks for the type of value and adds quotes if it is a character value.
Check the following MRE:
---
output:
pdf_document: default
html_document: default
params:
file: samples.csv
age: 28
awesome: true
34: badname
_x: badname
---
```{r, include=FALSE}
library(knitr)
library(gsubfn)
default_source_hook <- knit_hooks$get('source')
knit_hooks$set(source = function(x, options) {
x <- gsubfn(x = x, pattern = "params\\$`?([\\w_]+)`?", function(y) {
y <- get(y, params)
ifelse(is.character(y), paste0("'", y, "'"), y)
})
default_source_hook(x, options)
})
```
```{r print, echo=T, comment="", eval = T}
file <- params$file
age <- params$age
awsm <- params$awesome
# dont name your variables like that! works though...
badexmpls <- c(params$`34`, params$`_x`)
```
We make use of gsubfn(). This function allows us to use a function for the replacement attribute (not possible in common gsub). This function takes on the elements found, but, thanks to regex only the part after the $. So in this chunk, y equals file, age and awesome.

Resources