Pixiedust table in loop in r markdown not rendering - r

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.

Related

How to parameterize the inline code, text, together with R code chunk in Rmarkdown

In my Rmarkdown report, most of sections have the same text, inline code and R code chunk. Is it possible to parameterize them? For example the below image, is it possible to use something like for loop to produce them instead of repeating similar code 3 times?
In main RMD file,
library(tidyverse)
dat <- tibble(
id = 1:3,
fruit = c("apple", "orange", "banana"),
sold = c(10, 20, 30)
)
res <- lapply(dat$id, function(x) {
knitr::knit_child(
'template.Rmd', envir = environment(), quiet = TRUE
)
})
cat(unlist(res), sep = '\n')
In template.RMD,
current_dat <- filter(dat, id == x)
# Section: `r current_dat$fruit`
current_dat %>%
ggplot(aes(x = fruit, y = sold)) + geom_col()
IMHO, the simplest wat to achieve this is to use results = 'asis' and cat() below is a minimal RMarkdown file.
---
title: "Minimal example"
---
```{R results = "asis"}
for (i in 1:3) {
x <- runif(10)
cat("# section", floor(i), "\n")
plot(x)
# line break
cat("\n\n")
}
```

rmarkdown resize plot inside of code chunk

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).

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))
```

R Generate slides in slidify presentation in a loop

I want to create N slides to report descriptive statistics for N subsets of (big) data using slidify package . In an earlier discussion Create parametric R markdown documentation? the combination of brew and knitr were advised to achieve this.
I'm wondering whether slidify has its own workaround for such a task? I guess iterating through data to populate slides is even more logical than for plain text...
A minimal example (from the question above, I need slides instead of paragraphs)
```{r loopResults, echo=FALSE, results='asis'}
results = list(result1 = data.frame(x=rnorm(3), y=rnorm(3)), result2=data.frame(x=rnorm(3), y=rnorm(3)))
for(res in names(results)) {
cat(paste("<h3>Results for: ", res, "</h3>>"))
plot(results[[res]]$x, results[[res]]$y)
}
For those interested, this is what I have by far (for-loop + two column layout (as here)))
---
title: Loop test
widgets: [bootstrap, quiz]
---
## Simplest Example##
```{r echo = F, fig.width = 12, fig.height = 7, results='asis'}
library(data.table)
data <- as.data.table(list(days = 1:100, revenue = runif(10, 1, 100), profit = runif(10, 1, 10), department = factor(sample(letters[1:3]))))
for(j in levels(data$department)) {
dataj <- data[data$department == j,]
cat("\n\n--- &twocol\n")
cat(paste("\n\n## Department: ", j, "##\n") )
cat("\n*** left\n")
cat(paste("\nMean profit = ", round(mean(dataj$profit)), "\n"))
cat(paste("\nMean revenue = ", round(min(dataj$revenue))), "\n")
cat("\n*** right\n\n")
z<-ggplot(data = dataj ) +
geom_density(aes(x = profit), alpha = 0.3) +
geom_density(aes(x = revenue), alpha = 0.3)
print(z)
}
```

R kableExtra latex: Add linebreaks to rotated header row

I'm trying to produce table to a Latex-pdf document. I'm using kableExtra and knitr in R.
I have a table that has long column names. When I rotate the header row by 90 degrees linebreaks won't work. Does anyone have an idea how I could achieve both rotated row and linebreaks?
My example is the same as in Hao's Best Practice for newline in Latex table, but I added piped row_spec to the end of the code.
\documentclass[10pt,a4paper]{article}
\usepackage[table]{xcolor}
\begin{document}
\begin{table}
<<global_options, echo=FALSE>>=
library(kableExtra)
library("dplyr")
dt_lb <- data.frame(
Item = c("Hello\nWorld", "This\nis a cat"),
Value = c(10, 100)
)
dt_lb %>%
mutate_all(linebreak) %>%
kable("latex", booktabs = T, escape = F,
col.names = linebreak(c("Item\n(Name)", "Value\n(Number)"))) %>%
row_spec(0, angle = 90, align='l', monospace=T)
#
\end{table}
\end{document}
What I get is this, but the [l] tags hint that there's something else wrong with the tags as well:
On StackExchange TEX I found a question about rotation and linebreaks, this is what I'm trying achieve: https://tex.stackexchange.com/questions/14730/big-table-with-rotated-column-labels-using-booktabs
You could use tableHTML:
library(tableHTML)
Replace the new line character "\n" with the HTML tag <br>:
headers <- c("Item\n(Name)", "Value\n(Number)") %>%
stringr::str_replace_all(pattern = "\\n", replacement = "<br>")
Create a tableHTML object and rotate the headers using add_css_header():
dt_lb %>%
tableHTML(rownames = FALSE,
headers = headers,
escape = FALSE,
widths = c(100, 100),
theme = 'scientific') %>%
add_css_header(css = list(c('transform', 'height', 'text-align'),
c('rotate(-45deg)', '70px', 'center')),
headers = 1:2)
The result looks like this:
Note: you can add more css or default themes. Check out the vignettes for more details:
If you want to create a pdf document, you could use RMarkdown:
---
title: "tableHTML2pdf"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
\
```{r}
library(tableHTML)
dt_lb <- data.frame(
Item = c("Hello\nWorld", "This\nis a cat"),
Value = c(10, 100)
)
headers <- c("Item\n(Name)", "Value\n(Number)") %>%
stringr::str_replace_all(pattern = "\\n", replacement = "<br>")
dt_lb %>%
tableHTML(rownames = FALSE,
headers = headers,
escape = FALSE,
widths = c(100, 100),
theme = 'scientific') %>%
add_css_header(css = list(c('transform', 'height', 'text-align'),
c('rotate(-45deg)', '70px', 'center')),
headers = 1:2)
```
\
It will then create an HTML file. This file can then be converted to PDF using wkhtmltopdf.
I think you probably rendered your rmarkdown document into HTML... In the rmarkdown yaml header, does it say html_document right now? If so, you can try to change it to pdf_document.....
I rendered a pdf_document with exactly the same code and I think I got what you were looking for...
---
title: "Table Sample"
output: pdf_document
---
``` {r, include = FALSE}
library(tidyverse)
library(kableExtra)
```
```{r}
dt_lb <- data.frame(
Item = c("Hello\nWorld", "This\nis a cat"),
Value = c(10, 100)
)
dt_lb %>%
mutate_all(linebreak) %>%
kable("latex", booktabs = T, escape = F,
col.names = linebreak(c("Item\n(Name)", "Value\n(Number)"), align = "c")) %>%
row_spec(0, angle = 90)
```
Latex package makecell was missing.
Hao pointed out that Page 3 of this manual lists some of the necessary Latex packages: haozhu233.github.io/kableExtra/awesome_table_in_pdf.pdf

Resources