Variable plot height within single chunk - r

The following knitr thingy produces multiple plots via lapply. Their number and content therefore varies depending on the preceding R code.
Is there a way to set the plot height individually for each plot using a variable (like the height of the highest bar in a given bar chart)?
---
title: "Variable plot height"
output: word_document
---
Plots:
```{r, echo=FALSE, fig.height = 2}
library(ggplot2)
library(tidyr)
data(mtcars)
mtcars$car = row.names(mtcars)
cars = gather(mtcars[1:5, ], variable, value,
-c(car, mpg, disp, hp, qsec))
lapply(unique(cars$car), function(x) {
ggplot(cars[cars$car == x, ], aes(variable, value)) +
geom_bar(stat = "identity")
})
```

One way would be to create each image and include it into the document as an external image. You can employ the power of "asis". Here's a small example.
---
title: "Untitled"
author: "Neznani partizan"
date: "04. december 2015"
output: html_document
---
```{r, echo=FALSE, fig.height = 2}
library(ggplot2)
library(tidyr)
data(mtcars)
mtcars$car = row.names(mtcars)
cars = gather(mtcars[1:5, ], variable, value,
-c(car, mpg, disp, hp, qsec))
suppressMessages(invisible(lapply(unique(cars$car), function(x) {
ggplot(cars[cars$car == x, ], aes(variable, value)) +
geom_bar(stat = "identity")
ggsave(sprintf("%s.png", x))
})))
```
```{r results = "asis", echo = FALSE}
cat(sprintf("<img src='%s' alt='' style='width:350px;height:228px;'> <br />",
list.files(pattern = ".png", full.name = TRUE)))
```
Image sizes can be adjusted on-the-fly using appropriate arguments in ggsave and/or in printing HTML code.

The fig.width and fig.height chunk options can take in multiple values. In your example, there are five plots, so by setting a numeric vector of length five for the widths and heights, and saving the list of ggplot objects, you can have one chunk produce five graphics of different sizes in the final document. An example .Rmd file is below.
---
title: "Variable plot height"
output: word_document
---
Plots:
```{r, echo=FALSE}
library(ggplot2)
library(tidyr)
data(mtcars)
mtcars$car = row.names(mtcars)
cars = gather(mtcars[1:5, ], variable, value, -c(car, mpg, disp, hp, qsec))
plots <-
lapply(unique(cars$car), function(x) {
ggplot(cars[cars$car == x, ], aes(variable, value)) +
geom_bar(stat = "identity")
})
widths <- c(3, 4, 5, 3, 6)
heights <- c(3, 3, 3, 4, 3)
```
```{r show_plots, fig.width = widths, fig.height = heights}
plots
```

Related

How to combine outputs of different functions into a nicely report?

I have like:
. multiple plots, each created by a distinct function.
#Plot 1
sis_name <- babynames %>%
filter(name == "Kate", sex == "F") %>%
select("year", "name", "prop")
plot1 <- ggplot(data = sis_name) +
geom_line(mapping = aes(x = year, y = prop)) +
labs(title = "The Popularity of baby girls' name Kate", x =
"Year", y = "Proportion")
#Plot 2
plot2 <- ggplot(data = mydata) +
geom_point(mapping=aes(x=X, y=Y), color="blue") +
labs(title="Y vs X")
. some "text" outputs, created by glue::glue() and paste() functions.
conf_interval <- function(mydata) {
model <- lm(Y~X, data = mydata)
B_conf <- confint(model, #confidence interval for B
model$coefficients[2],
level = 0.95
glue::glue("Confidence interval for slop is {B_conf}")
}
What if I want to create a FUNCTION that calls out all the outputs (plot 1, plot 2, and the confidence interval) and combine them all into ONE nicely formatted report
(i.e. a sequence of plot and glue() commands from all the functions called sequentially)?
The requirement is to call out the report with a "function".
Any suggestions on which functions that I should look at?
You can save the example below as a file called report.Rmd:
---
title: "My Title"
author: "Me"
date: "21/08/2021"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
suppressPackageStartupMessages(
library(tidyverse)
)
library(glue)
```
# Title
```{r}
ggplot(mpg, aes(displ, hwy)) +
geom_point()
```
Other variables include `r glue_collapse(colnames(mpg), sep = ", ", last = " and ")`.
Subsequently, you can run the following:
library(rmarkdown)
render("report.Rmd", html_document())
To produce the report.

code chunk how to loop with knit_expand plots results asis with headings?

I was reading similar questions (here, here, and here and also here) but haven't been able to get this working, possibly due to a mix of grobs and rastergrobs in the list within the loop. Essentially, I have a nested for loop to print objects in a list, and want headers for the objects using markdown. I'm going with the knit_expand approach with child templates. If I set results='asis' the headers work but the plots don't, and if I remove results='asis' the plots work but not the headers. Any solutions? Here is a reproducible example that is close to what I'm doing though the sublists are contrived here and not working exactly correct but the idea is the same (a mix of headers that need to be asis and plot objects):
Master template (Master.Rmd)
---
title: "Example"
output:
html_document:
toc: true
toc_float: true
toc_depth: 2
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
library(knitr)
library(datasets)
library(ggplot2)
library(data.table)
library(png)
library(grid)
library(gridExtra)
library(RCurl)
data(iris)
plotsList = vector(mode = "list", length = length(levels(iris$Species)))
names(plotsList) = levels(iris$Species)
irisDT = as.data.table(iris)
dataInList = split(irisDT, by="Species")
for(i in seq(plotsList)){
plotsList[[i]]$example_plot[1] <- list(ggplot(data=dataInList[[i]], aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() + xlab("Sepal Length") + ylab("Sepal Width") +
ggtitle(paste0("Sepal Length-Width for ", names(plotsList[i]))))
plotsList[[i]]$example_plot[2] <- list(ggplot(data=dataInList[[i]], aes(x = Petal.Length, y = Petal.Width)) +
geom_point() + xlab("Petal Length") + ylab("Petal Width") +
ggtitle(paste0("Petal Length-Width for ", names(plotsList[i]))))
plotsList[[i]]$png_example <- rasterGrob(readPNG(getURLContent(("https://i.imgur.com/mfuTUPD.png"))))
}
```
# Big Heading
some text
## Other headings
other text
# Loop output Description {.tabset .tabset-fade}
text about loop output
```{r run_loop, echo=FALSE}
# for the headings to work, results need to be asis, but then the plots don't work :(
out = NULL
out2 = NULL
for(i in seq(plotsList)){
cat("\n")
cat("## ", {{names(plotsList[i])}}, "\n")
out = c(out, knit_expand('one_level.Rmd'))
for(j in seq(plotsList[[i]][['example_plot']])){
out2 = c(out2, knit_expand('two_level.Rmd'))
}
}
```
`r paste(knit(text = out), collapse = '\n')`
`r paste(knit(text = out2), collapse = '\n')`
```
and the child template (Child.Rmd):
```{r echo=FALSE}
grid.arrange(plotsList[[i]][['example_plot']][[j]])
```
```{r echo=FALSE, results='asis'}
grid.arrange(plotsList[[i]][['png_example']])
```
I'm sure there are other ways to do it, but I eventually figured out how to add the headings markdown directly to the knit expand output from the master.Rmd, like this:
`out = c(out, knit_expand(text = paste0("\n## ", {{names(combined.plots[i])}}, "\n ")))`

Printing headers, plots, and tables in a loop in rmarkdown

I'm trying to use RMarkdown/R Notebooks to produce an automated report based on a single dataset broken out by department. Intuitively, I want to use a for loop that filters the dataset to a single department, provides a title, displays a few graphs and tables specific to that deparment, and then produces a page break and starts with the next department.
Here's a reprex of what I have so far. Problems with the resulting code:
There are blank spaces throughout, pretty sure related to the use of dev.off() and plot.new(). If I remove these, only the first plot prints and the titles print all together in a bunch at the beginning. If I include the dev.off and plot.new calls, then I get blank graphics placeholders.
There are NA items throughout. I'm not sure why. They don't appear when running the code in RStudio, only in the resulting output.
I can't get headers to work and act like H1, H2 etc.
headers. They are rendered as code output.
Am I approaching this wrong? Should I not use a for loop, but do something else instead? If I take everything out of the loop and do it manually, it works great.
```
---
title: "Demo Notebook"
output:
word_document: default
---
```{r echo=FALSE, message=FALSE, warning=FALSE, paged.print=TRUE, results= "asis"}
library(tidyverse)
library(knitr)
spp <- unique(iris$Species)
for (i in seq_along(spp)) {
print(paste0("# Species: ", spp[i]))
d <- iris %>%
filter(Species == spp[i])
# one kind of plot
p <- ggplot(d, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_smooth() +
ggtitle(spp[i])
print(p)
dev.off()
plot.new()
# another plot
q <- plot(d$Sepal.Length)
print(q)
dev.off()
# a table
print(kable(head(d)))
}
```
That seems to be more complicated than anticipated. I managed with this solution, using flextable and, for some reason, needed two types of page breaks to actually get one in the word document:
---
title: "Demo Notebook"
output:
word_document: default
---
```{r setup, include=FALSE}
library(tidyverse)
library(flextable)
```
```{r, echo=FALSE, message=FALSE, warning=FALSE, results='asis', fig.height=3, out.width="50%"}
spp <- unique(iris$Species)
for (x in seq_along(spp)) {
print(paste0("# Species: ", spp[x]))
d <- iris %>% filter(Species == spp[x])
cat("\n\n")
# one kind of plot
p1 <- ggplot(d, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_smooth() +
ggtitle(spp[x])
plot(p1)
cat("\n\n")
# another plot
plot(d$Sepal.Length)
cat("\n")
# a table
ft <- d[1:10, ] %>%
flextable() %>%
align(part = "all") %>% # left align
set_caption(caption = "Table 1: Example") %>%
font(fontname = "Calibri (Body)", part = "all") %>%
fontsize(size = 10, part = "body") %>%
theme_booktabs() %>% # default theme
autofit()
cat("\n")
cat(paste("\n```{=openxml}",
format(ft, type = "docx"),
"```\n", sep = "\n"))
cat("\n\n\\pagebreak\n")
cat('<div style="page-break-before: always;" />')
}
```

Using long figures in markdown

I'm displaying long figures in a markdown report.
These are long because they use ggplot2::facet_wrap so their height depend on the data, which is not constant.
I can set the figure.height parameter of the chunk but then it's fixed and my report looks bad. Is their a way around this ?
Example :
---
title: "title"
author: "author"
date: '`r Sys.Date()`'
output: html_document
---
```{r, figure.height=40}
library(dplyr)
library(ggplot2)
iris %>%
mutate_at("Sepal.Length",cut, 5) %>%
mutate_at("Sepal.Width",cut,2) %>%
group_by_at(c(1,2,5)) %>%
summarize_at("Petal.Length",mean) %>%
ggplot(aes(Species, Petal.Length)) +
geom_col() +
facet_wrap(Sepal.Length ~ Sepal.Width,ncol=2)
```
I had a similar issue and was not able to get Peter's solution to work. From what I'm able to gather, eval.after does not work with fig.height.
But thanks to Peter's example, I was able to find a work-around:
---
author: "author"
date: '`r Sys.Date()`'
output: html_document
---
```{r setup}
library(dplyr)
library(ggplot2)
library(knitr)
FACET_HEIGHT <- 3.4
```
In chunk 1: First, create the ggplot.
Then, use `ggplot_build` to create a new variable called `adaptive_figure_height`.
Finally, use knitr::opts_chunk$set to update the chunk option `fig.height` to better suit your ggplot.
```{r}
g <-
iris %>%
mutate_at("Sepal.Length",cut, 5) %>%
mutate_at("Sepal.Width",cut,2) %>%
group_by_at(c(1,2,5)) %>%
summarize_at("Petal.Length",mean) %>%
ggplot(aes(Species, Petal.Length)) +
geom_col() +
facet_wrap(Sepal.Length ~ Sepal.Width, ncol = 2)
adaptive_fig_height <- FACET_HEIGHT * max(ggplot_build(g)$layout$layout$ROW)
opts_chunk$set( fig.height = adaptive_fig_height )
```
In chunk 2: Plot the ggplot.
If needed, you can revert `fig.height` back to a default value.
```{r }
g
opts_chunk$set( fig.height = 7 )
```
Repeat the setup in chunk 1 and 2 if you have multiple long plots with differing heights.
To go along with the n * single_height idea: you can use the chunk option eval.after so that the fig.width and fig.height options will be evaluated after the rest of the chunk is evaluated and then use the ggplot_build to pull apart a ggplot object and determine the number of rows and columns used in the facets.
For example:
---
author: "author"
date: '`r Sys.Date()`'
output: html_document
---
```{r setup}
library(dplyr)
library(ggplot2)
library(knitr)
FACET_HEIGHT = 3.4
FACET_WIDTH = 5
opts_chunk$set(out.width = "80%",
out.height = "80%",
eval.after = c("fig.height", "fig.width"))
```
For the example we'll have one basic plot to which we will set different facets.
```{r}
g <-
iris %>%
mutate_at("Sepal.Length",cut, 5) %>%
mutate_at("Sepal.Width",cut,2) %>%
group_by_at(c(1,2,5)) %>%
summarize_at("Petal.Length",mean) %>%
ggplot(aes(Species, Petal.Length)) +
geom_col()
```
A graphic with two columns
```{r fig1, fig.height = FACET_HEIGHT * max(ggplot_build(g1)$layout$layout$ROW), fig.width = FACET_WIDTH * max(ggplot_build(g1)$layout$layout$COL)}
g1 <- g + facet_wrap(Sepal.Length ~ Sepal.Width, ncol = 2)
g1
```
A graphic with two rows
```{r fig2, fig.height = FACET_HEIGHT * max(ggplot_build(g2)$layout$layout$ROW), fig.width = FACET_WIDTH * max(ggplot_build(g2)$layout$layout$COL)}
g2 <- g + facet_wrap(Sepal.Length ~ Sepal.Width, nrow = 2)
g2
```
A screenshot of the resulting html is:
Some fine tuning of the image width and height will be needed, but this should be a good starting point.

A Kableextra table and a ggplot plot on same row in Rmarkdown (PDF - not Flexdashboard)

I have been experimenting with R Markdown to create some PDF reports. I am having difficulty in getting the layout right. Basically, I need to have a KableExtra created table (dataframe) and a ggplot plot on the same row. I have explored some grid packages, but couldn't get it to work.
Here is my code:
---
title: "Untitled"
author: ""
date: "14 June 2018"
output: pdf_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(knitr)
library(reshape2)
library(dplyr)
library(kableExtra)
```
## R Markdown
```{r chart, echo=FALSE}
Years <- c("2016","2016","2016","2016",
"2017","2017","2017","2017")
Quarters <- c("Q1","Q2","Q3","Q4",
"Q1","Q2","Q3","Q4")
Series1 <- c("100","200","300","400","500","600","700","800")
Series1 <- as.numeric(Series1)
df <- data.frame(Years,Quarters, Series1)
library(ggplot2)
ggplot(df) +
geom_point(aes(x = Quarters, y = Series1)) +
facet_wrap( ~ Years, strip.position = "bottom",scales = "free_x") +
theme(panel.spacing = unit(0,"lines"), strip.background =
element_blank(),
strip.placement = "outside")
```
```{r table, echo=FALSE}
Table <- dcast(df, Years ~ Quarters, fun.aggregate = sum, value.var =
"Series1")
Table <- Table %>%
kable(format = "latex", caption = "Balances", booktabs = TRUE) %>%
kable_styling(latex_options = c("striped","hold_position","condensed"),
font_size = 10)
Table
```
If you are not strongly depending on kable() I can provide this gridExtra solution. When using tableGrob(kable(.)) the latex code won't be executed somehow, maybe somebody else comes up with how to execute latex code within a tableGrob().
```{r chart, echo=FALSE, message=FALSE}
df <- data.frame(Years=rep(2016:2017, each=4),
Quarters=rep(paste0("Q", 1:4), 2),
Series1=seq(100, 800, 100))
library(ggplot2)
p1 <- ggplot(df) +
geom_point(aes(x=Quarters, y=Series1)) +
facet_wrap( ~ Years, strip.position="bottom", scales="free_x") +
theme(panel.spacing=unit(0, "lines"),
strip.background=element_blank(),
strip.placement="outside",
aspect.ratio=1) # set aspect ratio
Table <- dcast(df, Years ~ Quarters, fun.aggregate=sum, value.var="Series1")
library(gridExtra)
t1 <- tableGrob(Table, theme=ttheme_minimal(), rows=NULL) # transform into a tableGrob
grid.arrange(p1, t1, nrow=1)
```
Produces:

Resources