When using rmarkdown to render pdf document, we can use three options for printing data.frame: default, kable and tibble (see here)
With the default option, it is possible to limit the number of rows printed, with the option: max.print
For tibble, we can use: dplyr.print_max
I can't find a way to limit the number of rows for kable. Is it possible?
kable renders the full data frame passed to it as a table in the output document. AFAIK there's no argument that will limit the number of rows. However, you can preselect the number of rows in the output table (for example, kable(head(dat)) or kable(dat[1:5, ])). If you want to avoid having to select the rows each time, you could write a helper function to limit the number of rows printed. For example:
---
output: pdf_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
library(knitr)
```
```{r}
my_kable = function(x, max.rows=6, ...) {
kable(x[1:max.rows, ], ...)
}
```
```{r}
my_kable(mtcars, caption="My first caption")
```
```{r}
iris$Sepal.Length = 1000 * iris$Sepal.Length
my_kable(iris, 3, caption="My second caption", format.args=list(big.mark=","))
```
A really simple solution is to wrap kable around head:
kable(head(mtcars, n = 5))
You can create a custom method for printing data frames like this.
Adapted from Yihui Xie's explanation here
---
title: "Untitled"
output: html_document
---
```{r include=FALSE}
knit_print.data.frame <- function(x, ...) {
head(x, 5) |>
knitr::kable() |>
paste(collapse = "\n") |>
knitr::asis_output()
}
registerS3method(
genname = "knit_print",
class = "data.frame",
method = knit_print.data.frame,
envir = asNamespace("knitr")
)
```
```{r}
iris
tibble::as_tibble(iris)
```
Related
I'd like to create a pdf report for each cylinder ('cyl') between the 3 possibilities (4, 6 and 8) in a separate table. But, I need to create one table for each cylinder in a loop and for this I try to do:
library(knitr)
library (rmarkdown)
data(mtcars)
id.cyl <-unique(mtcars$cyl)
for(i in 1:id.cyl){
mtcars.sub<-
mtcars[mtcars$cyl==id.mtcars[i],]
kagle(mtcars.sub)
}
render("my_loop_report.pdf",
pdf_document())
This code doesn't work and I need some suggestions to solve it. Please any help with it?
Create an .rmd file that generates the report, and pass the cylinder as a value.
Here is the .rmd file (say, cylinder.rmd)
---
title: "cylinder_report"
author: "author_name"
date: "2023-01-25"
output: pdf_document
params:
cylinder: 0
---
``{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
df = mtcars
``
## Cylinder `r params$cylinder`
``{r output, echo=FALSE}
knitr::kable(df[df$cyl == params$cylinder,])
``
Separately, you can then loop through the cylinder values, each time passing the cylinder value
cyls = unique(mtcars$cyl)
for(cyl in cyls) {
rmarkdown::render(
input = "cylinder.Rmd",
output_file = paste0("cylinder_report_", cyl),
params = list(cylinder = cyl)
)
}
This will produce cylinder_report_4.pdf, cylinder_report_6.pdf, and cylinder_report_8.pdf.
(Note, in this toy example, you don't even have to pass the parameter; in fact, loading mtcars into df and subsetting it within the .rmd is not necessary. When you use `rmarkdown::render(), objects in the environment are available to the .rmd file, so you could also subset the data in the loop outside the rmd file, like this,
cyls = unique(mtcars$cyl)
for(cyl in cyls) {
df = mtcars[mtcars$cyl == cyl,]
rmarkdown::render(
input = "cylinder.Rmd",
output_file = paste0("cylinder_report_", cyl)
)
}
and the .rmd could be simplifed to this (note, no parameter, no creation of df, no subsetting, etc:
---
title: "cylinder_report"
author: "author_name"
date: "2023-01-25"
output: pdf_document
---
``{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
``
## Cylinder `r cyl`
``{r output, echo=FALSE}
knitr::kable(df)
``
)
Is there a way to generate tables in a loop and set tab.id that can be later cross-referenced? Generating tables in a loop is trivial, but I don't know how to set ids and captions afterwards. Please, see the code below. Here I'm iterating over a list of R data.frames. In the last chunk I put the vector of ids (its lenght is 2) into chunk options - tab.id and tab.cap. This results in generation of two tables (good), but how to get the id of currently processed data.frame in the chunk?
---
output: officedown::rdocx_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r}
library(flextable)
dfs <- list(
df1 = head(iris),
df2 = head(mtcars)
)
df_ids <- names(dfs)
```
```{r results='asis', tab.cap=paste("Result for item", df_ids), tab.id=paste0("tab_", df_ids),
tab.cap.style = "Table Caption"}
flextable(dfs[[???]]) # can current id be accessed somehow?
```
Another option is to use for loop to generate the tables. But how to set tab.id and tab.cap afterwards? Even a workaround solution can be fine for me.
You can probably set these values in a loop by using knitr::opts_chunk$set(). I would probably prefer set_caption in your case.
To print flextables in a loop inside an R Markdown document, use flextable_to_rmd(). See Looping in R Mardown documents.
---
output: officedown::rdocx_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r}
library(flextable)
library(officer)
library(officedown)
library(magrittr)
dfs <- list(
df1 = head(iris),
df2 = head(mtcars)
)
df_ids <- names(dfs)
```
```{r results='asis'}
for(i in df_ids){
z <- flextable(dfs[[i]]) %>%
set_caption(caption = paste("Result for item", i),
style = "Table Caption",
autonum = run_autonum(seq_id = "tab", bkm = i))
flextable_to_rmd(z)
crossref_id <- sprintf("\\#ref(tab:%s)", i)
cat("\n\n\nA ref to table ", crossref_id, "\n\n\n\n")
}
```
I often want to print out the dataframes contained in a list as paged tables in my rmarkdown documents. Calling each dataframe individually renders the desired ouptut if the right df_print option is selected. However, the point of having a list is that the number of dataframes varies depending on the parameters passed to the rmarkdown document; so that's no real solution.
Based on Vincent Guyader's answer to this question and on this example of rmarkdown::paged_table, I've tried to do the following without success.
Is there a way to achieve this at all? I'd be happy to use any package that supports pagination remotely resembling the df_print option.
---
title: "Printing paged tables from a list of dataframes in Rmarkdown"
output:
html_document:
df_print: paged
---
```{r}
library(DT)
library(rmarkdown)
library(purrr)
library(knitr)
df_list <- list("cars" = mtcars, "flowers" = iris)
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, results='asis')
```
### Desired output but impossible to generalise
```{r}
df_list[["cars"]]
```
```{r}
df_list[["flowers"]]
```
### datatable shows as blanks on the page
```{r}
map(df_list, ~DT::datatable(.x) %>%
htmltools::tagList() %>%
print())
```
### rmarkdown outputs dataframe contents as one very long string
```{r}
map(df_list, rmarkdown::paged_table)
```
The issue is that the JS dependencies needed to render the Datatable are not included in the HTML output. A workaround which I borrowed from here is to add a code chunk
```{r init-step, include=FALSE}
DT::datatable(mtcars)
```
outside of the loop or map statement which ensures that the JS dependencies are included. Also, I would recommend to switch to purrr::walk as using map has the effect that the tables are plotted twice.
---
title: "Printing paged tables from a list of dataframes in Rmarkdown"
output:
html_document:
df_print: paged
---
```{r}
library(DT)
library(rmarkdown)
library(purrr)
library(knitr)
df_list <- list("cars" = mtcars, "flowers" = iris)
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, results='asis')
```
### Desired output but impossible to generalise
```{r}
df_list[["cars"]]
```
```{r}
df_list[["flowers"]]
```
### datatable shows as blanks on the page
```{r init-step, include=FALSE}
DT::datatable(mtcars)
```
```{r}
walk(df_list, ~DT::datatable(.x) %>%
htmltools::tagList() %>%
print())
```
When using results='asis' argument, the renderer (here DT) has to be initialized once before being applied on a asis list.
This seems to be a general problem, see here with leaflet, and here with Highcharter.
The answer to this general question has been given here.
In this case:
---
title: "Printing paged tables from a list of dataframes in Rmarkdown"
output:
html_document:
df_print: paged
---
```{r,}
library(DT)
library(rmarkdown)
library(purrr)
library(knitr)
df_list <- list("cars" = mtcars, "flowers" = iris)
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE, results='asis')
# initialize the renderer
data.frame() %>%
DT::datatable() %>%
knitr::knit_print() %>%
attr('knit_meta') %>%
knitr::knit_meta_add() %>%
invisible()
```
```{r , results='asis'}
#Remove already printed element and print the rest
df_list[[1]] <- NULL
map(df_list, ~DT::datatable(.x) %>%
htmltools::tagList() %>%
print())
```
In R markdown, I want to output a sortable table while the r chunk is set to results='asis'
DT::datatable yields the desired kind of output that I'm looking for. However that doesn't seem work with results='asis' set. I'm not sure if there's any kind of escape to this setting.
```{r setup, include=FALSE}
library(knitr)
my_data = mtcars
df_list = list()
df_list[[1]] = my_data[1:5,]
df_list[[2]] = my_data[6:10,]
### tabs {.tabset}
```{r, results='asis', echo = FALSE}
for (i in 1:length(df_list)){
cat("#### tab", i)
print(kable(df_list[[i]]))
cat('\n\n')
}
(*In the code above I had to omit the closing of r chunks "```" because I couldn't figure out how to properly display that in here)
As you can see, the second r chunk has results='asis' set. I'm setting it because I want to dynamically generate the tabs within the for loop. I'm printing the tables using print(kable...) but they are not sortable. Is there a way to output sortable tables once I have the file knitted? Thanks!
The DT renderer has to be initialized, as shown here and discussed more generally here:
---
title: "DT tabs"
output: html_document
---
```{r setup, include=FALSE}
library(knitr)
library(dplyr)
library(DT)
my_data = mtcars
df_list = list()
df_list[[1]] = my_data[1:5,]
df_list[[2]] = my_data[6:10,]
data.frame() %>%
DT::datatable() %>%
knitr::knit_print() %>%
attr('knit_meta') %>%
knitr::knit_meta_add() %>%
invisible()
```
### tabs {.tabset}
```{r, results='asis', echo = FALSE}
for (i in 1:length(df_list)){
cat("#### tab", i,'\n')
print(htmltools::tagList(DT::datatable(df_list[[i]])))
cat('\n\n')
}
I'm trying to create multiple tables using kable from a csv file, but the requirement is that I need to also put an image in the table as per the below image. The entire image needs to be in the left column. Is this possible?
The dataframe looks like this:
df<-data.frame(Amount= c('$25', '$45', '$75'),
Rate = c('1%', '1%', '3%'),
Location = c('Germany', 'Switzerland', 'England'),
ImageName= c('GE.png', 'BE.png', 'CE.png'),
Status = c('Sold','Unsold','Sold')
)
So far my R code is
---
output:
word_document:
reference_docx: ReferenceDoc.docx
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
df<-read.csv('Productdata.csv')
```
```{r loops, echo=F, results='asis'}
library(knitr)
for (i in 1:nrow(df))
{
print(kable(df[i,]))
}
```
Im really not sure how I can enter an image like that in my RMarkdown for WORD.
Here's an approach using kable, along with kableExtra functions to take care of some of the formatting. So far, I've only been able to render this properly to html. I'll update if I can make this work in Word. In the code below, I used some images I happened to have lying around. Just run the same sprintf function on your original ImageName column to get the appropriate rmarkdown tagging for your images.
---
output:
html_document:
df_print: paged
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, warning=FALSE, message=FALSE)
library(knitr)
library(kableExtra)
library(tidyverse)
#df<-read.csv('Productdata.csv')
df<-data.frame(Amount= c('$25', '$45', '$75'),
Rate = c('1%', '1%', '3%'),
Location = c('Germany', 'Switzerland', 'England'),
ImageName= c('GE.png', 'BE.png', 'CE.png'),
Status = c('Sold','Unsold','Sold')
)
# Change to names of my local images
df$ImageName =c("mal2.jpg",
"serenity2.jpg",
"blue_sun2.jpg")
# Add appropriate rmarkdown tagging
df$ImageName = sprintf("![](%s)", df$ImageName)
```
```{r loops, echo=F, results="asis"}
for (i in 1:nrow(df)) {
# Select desired row
d = df[i, ]
# Change name of ImageName column to Status value
names(d)[grep("ImageName", names(d))] = as.character(d[["Status"]])
# Convert data to "long" format
d = d %>%
select(-Status) %>%
gather(Product, value, Amount:Location) %>%
rename(` ` = value)
# Render table using kableExtra for formatting
print(kable(d, format="html") %>%
kable_styling(full_width=FALSE) %>%
collapse_rows(columns=1, valign="top"))
}
```
And here's what the html output file looks like:
I think this may bump up against the limits of kable, but here is a very not elegant way to do something similar with htmlTable. Notice that the images are relative to the directory you are running the Rmd from, or you can use links to URLs.
---
output:
html_document:
df_print: paged
word_document:
reference_docx: ReferenceDoc.docx
---
```{r setup, echo=FALSE, message=FALSE, warning=FALSE, results='asis'}
library(knitr)
library(dplyr)
library(magrittr)
library(htmlTable)
x <- 0
df <- data.frame( Status = c('Sold','Unsold','Sold'),
Image = c('images/fwhm1.jpg', 'images/ridges-shade.jpg', 'images/sep16-2018.jpg'),
Amount= c('$25', '$45', '$75'),
Rate = c('1%', '1%', '3%'),
Location = c('Germany', 'Switzerland', 'England')
)
df$Image <- sprintf('![](%s){width=150px}', df$Image)
for (i in 1:nrow(df)) {
x <- t(df[i,])
new.df <- data.frame(matrix(ncol=2,nrow=2))
new.df[1,1] <- paste(x[1],x[2],sep="<br>")
new.df[1,2] <- paste0(rownames(x)[3:5], ": ", x[3:5], collapse="<br>")
colnames(new.df) <- NULL
rownames(new.df) <- NULL
print( htmlTable(new.df,
css.cell="padding-left: .5em; padding-right: .5em; align: left; align: left; vertical-align: top;",
rnames=FALSE) )
}
```
Here is what it looks like:
I also opened the HTML file that was rendered in Word, and was able to shrink the images and add borders -- the table structure was retained. It looked like this: