table with long text, bullet points and specific table width - r

I want a table to have bullet points in one column and to have a specific table width (in order to be placed on one page when rendered to PDF).
How can I achieve this in rmarkdown using one of the many packages out there?
What I have tried and have so far:
---
output: pdf_document
---
```{r, include = FALSE}
df <- data.frame(col1 = "Some really long text here. I mean some reeeeeaaly loooong text. So long, it should be wrapped. Really.",
col2 = "* bullet point 1\n * bullet point 2", col3 = "Yes, there is still another column.")
```
# Attempt 1: kableExtra
```{r, echo = FALSE, warning = FALSE}
library(kableExtra)
df1 <- df
df1$col2 <- linebreak(df1$col2)
knitr::kable(df1, escape = FALSE) %>% column_spec(1, width = "15em")
```
# Attempt 2: pander
```{r, echo = FALSE}
pander::pander(df, keep.line.breaks = TRUE, style = 'grid', justify = 'left')
```
This renders to:
As you can see both options have caveats. The kableExtra version does have a specific table width that fits on one page but does not show bullet points nicely. Whereas the pander solution renders the bullet points nicely but spans multiple pages, because I don't know how to specifiy the table width in pander.
Is there a solution that can do both?
Related questions are for example here and there.

An alternative solution based on kableExtra (optional: with footnotes)
This approach allows to add a caption, to manually add footnotes within the table, and to fix column widths.
To create the bullet point list:
"thicker" \cdots from LaTex are employed as bullets (alternatives see here)
linebreaks are forced with \n (indentation is therefore not as nice as with the pander approach from #daroczig).
MWE
library(stringi); library(kableExtra); library(dplyr)
string_short <- "Descriptor"
string_long <- substr(stri_rand_lipsum(1), 1, 50)
# add footnotes manually within table
string_bulletlist <- "$\\boldsymbol{\\cdot}$ bullet point 1: foo$^{a}$ \n $\\boldsymbol{\\cdot}$ bullet point 2: bar$^{b}$"
df <- data.frame(col1 = c(string_short, string_short),
col2 = c(string_bulletlist, string_bulletlist),
col3 = c(string_long, string_long)
)
col_names <- c("Descriptor", "Description with list", "Some comment")
# header: bold column names
colnames(df) <- paste0("\\textbf{", col_names,"}")
# add footnote with kableExtra commands
names(df)[1] <- paste0(names(df)[1], footnote_marker_symbol(1))
df %>%
mutate_all(linebreak) %>% # required for linebreaks to work
kable(
"latex",
escape = F,
booktabs=TRUE,
align = "l",
caption = 'kableTable with bullet list and footnote') %>%
# kable_styling(full_width = F) %>% # makes no difference here
footnote(general = "General comment of the table.",
alphabet = c("Footnote A;", "Footnote B;"),
symbol = c("Footnote Symbol 1")) %>%
column_spec(1, width = "5em") %>% # fix width column 1
column_spec(2, width = "10em") %>% # fix width column 2
column_spec(3, width = "15em") # fix width column 3
To [improve line spacing[(https://stackoverflow.com/questions/53794142/increase-line-row-spacing-with-kableextra), one can add the following before & after code chunk in the Rmd:
\renewcommand{\arraystretch}{1.5} <!-- increase line spacing for the table -->
RMD CHUNK HERE
\renewcommand{\arraystretch}{1} <!-- reset row hight/line spacing -->
Comment:
I also tried the pander approach from #daroczig and made the following experiences:
Pro: nice line spacing plus a "real bullet list" (compared to the $\boldsymbol{\cdot}$-workaround in the kableExtra approach).
Con: a large blank space is included after the bullet list
In addition, when using the pander approach in an Rmd file using the huskydown thesis template the footnotes greatly messed up the table alignment.

Use the split.table parameter of pandoc.table (that is being called by pander in the background) or disable the table splitting in general via panderOptions's table.split.table, eg
pander::pander(df, keep.line.breaks = TRUE, style = 'grid', justify = 'left', split.table = Inf)
or
library(pander)
panderOptions('table.style', 'grid')
panderOptions('table.alignment.default', 'left')
panderOptions('table.split.table', Inf)
panderOptions('keep.line.breaks', TRUE)
pander(df)

Related

How to handle the overlay of kable output with page number?

I use kbl() function, in a rnw file that I compile in a pdf with knitr, to make a table starting from a dataframe. I use kableExtra option scale_down to adapt the table size to page size. It works fine but the table overlay with the page number of my pdf output. I'd like to know what's the best way to handle this type of problem. Thanks in advance for the help.
You could add some Latex code that puts the tabular environment into a box and resizes the content. Below is an example using \resizebox
Here's tab, a LaTex table with Gaussian random numbers generated by kable:
```{r, message = F, warning=F}
library(knitr)
library(tidyverse)
library(kableExtra)
ncol <- 10
nrow <- 30
df <- map_dfc(1:ncol, ~ data.frame(round(rnorm(nrow), 2))) %>%
set_names(letters[1:ncol])
tab <- kbl(df, format = "latex")
```
And here's how to resize it by text height/width using resizebox (see this post on tex.stackexchange for details on how the scaling works)
```{r, results='asis'}
cat(
"\\begin{table}
\\centering
\\resizebox*{\\textwidth}{\\dimexpr\\textheight-2\\baselineskip\\relax}{%",
tab,
"\n}
\\end{table}"
)
```
Result for the 30x10 table
Let's double rows and columns (ncol <- 20; nrow <- 60)
Result for 60x20 table.
I have three suggestions. The first one is maybe set your table to the next page using \newpage in your Rmarkdown code which is latex code. The second option is maybe set latex_options="HOLD_position" in your Kable_styling function. This will set the table at a certain place which maybe could help your problem. The third option is using 'longtable = TRUE` which means that the table continues on the next page. Here is the code for option one and two with:
```{r}
library(knitr)
library(tidyverse)
library(kableExtra)
df <- data.frame(x = 1:10, y = 11:20)
kable(df) %>%
kable_styling(latex_options = "HOLD_position")
```
\newpage
```{r}
kable(df) %>%
kable_styling(latex_options = "HOLD_position")
```
Which looks like this:
Here is the code for option three:
```{r}
library(tidyverse)
library(knitr)
library(kableExtra)
df <- data.frame(x = 1:50, y = 11:60)
kable(df, "latex", longtable = T, booktabs = T) %>%
kable_styling(latex_options = c("repeat_header"), font_size = 7)
```
Looks like this:

changing text colour in kable, in Rmarkdown, in latex with % label leads to unwanted "\" proceeding % label when knit

I'm trying to knit a table in a large Rmarkdown document. The table represent disease levels on a farm and thus some are expressed as percentages percentages. When the disease levels are above target I would like to highlight by changing the color of the font to red. This works fine when text does not have a % label, but when I escape the % label the % label in the PDF document is always proceeded by an unwanted "". I'm a vet not a data scientist I have spent many hours on this but can't find an answer.
The original document is extremely complex pulling data from many sources to generate the current disease levels and targets using shiny input to determine various options so not possible to use code from the original document. But I have produced a very minimal reproducible example below
require(kableExtra)
require(scales)
library(knitr)
require(tidyverse)
knitr::opts_chunk$set(echo = TRUE)
MDF = data.frame(a = label_percent()(.07),b = label_percent()(.05))
MDF$a = as.character(MDF$a)
MDF$b = as.character(MDF$b)
MDF[1,] = apply(MDF[1,],2,function(f) gsub("%", "\\\\%", f))
MDF = MDF %>% mutate(a = cell_spec(a, color = ifelse(a > 6,"red","black")))
kable(MDF, "latex", escape = F, booktabs = T)
As far as I get it the issue is that the percent sign in the conditionally formatted cell gets escaped twice, once via the gsub and once via kable itself so that you end up with an additional \textbackslash() inserted in you latex code. Hence, one option to solve your issue would be to manually escape only the percent signs in the column to which you don't apply the conditional formatting:
---
title: "Untitled"
output: pdf_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r}
require(kableExtra)
require(scales)
library(knitr)
require(tidyverse)
MDF = data.frame(a = .07, b = .05) %>%
mutate(across(c(a, b), percent)) %>%
# Escape percent sign in column b only
mutate(across(c(b), ~ gsub("%", "\\\\%", .x))) %>%
mutate(a = cell_spec(a, color = ifelse(a > 6,"red","black")))
kable(MDF, "latex", escape = F, booktabs = T)
```

Space after every five rows in kable output (with booktabs option) in R Markdown document

I am using knitr::kable() to render tables as part of an R Markdown document (that itself is part of a bookdown project). In particular, the booktabs option (through setting the booktabs argument to equal TRUE) renders the table in a nice-looking way. However, I'd like for there not to be a space after every five rows.
Here, for example, is the code and how the table in the bookdown demo appears when rendered as a PDF:
knitr::kable(
head(iris, 20), caption = 'Here is a nice table!',
booktabs = TRUE
)
I'd like for the space that aappears after every five rows to not be included, but I cannot seem to find a setting in knitr::kable() that does this.
The reason why the row height is not always equal is that by default, kable inserts a \addlinespace every 5 rows when booktabs is specified as TRUE, as is shown here:
linesep = if (booktabs) c('', '', '', '', '\\addlinespace') else '\\hline'
To alter this, add linesep = "" as an argument to kable().
knitr::kable(
head(iris, 20), caption = 'Here is a nice table!',
booktabs = TRUE,
linesep = ""
)
See Get rid of \addlinespace in kable for more details.
It is also worth saying that you can play around with this option if you want to change the style. For example linesep = c("", "", "", "\\hline") would add a horizontal line every four spaces.
Based on the example above I was interested in controlling the separation. That works nicely with the following helper function. This makes it possible to control the locations of the line separation.
linesep<-function(x,y=character()){
if(!length(x))
return(y)
linesep(x[-length(x)], c(rep('',x[length(x)]-1),'\\addlinespace',y))
}
knitr::kable(
head(iris, 20), caption = 'Here is a nice table!',
booktabs = TRUE,
linesep = linesep(c(3,2,1,1,3,5,4,1))
)
Bart's answer failed to work for me on my remote server when using prettyNum() on the table and passing the output to kableExtra::kable_styling(), where it just added an column full of NAs and no linespaces. It seems to work fine on my local machine, so I can't reproduce the error.
Anyway, this alternative solution works for me (but only when passing the table to kableExtra for some reason):
linesep <- function(table, groups) {
sep_indx <- rep("", nrow(table))
sep_indx[cumsum(groups)] <- '\\addlinespace'
return(sep_indx)
}
knitr::kable(
head(iris, 20),
caption = 'Here is a nice table!',
booktabs = TRUE,
linesep = linesep(head(iris, 20), c(3, 2, 1, 1, 3, 5, 4, 1))
) %>% kableExtra::kable_styling(latex_options = c("scale_down", "HOLD_position"))
example

R Markdown Footnote in xtable

I'm having issues with a footnote not appearing under a table in my R Markdown report. Below is the code I'm using to process which does successfully but with no footnote appearing under the table.
```{r ccxtable7, results="asis", echo=FALSE}
comment <- list(pos = list(0), command = NULL)
comment$pos[[1]] <- c(nrow(indPctChgCC))
comment$command <- c(paste("\\hline\n",
"{\\footnotesize Note: * signifies number of
properties used was 35 or less.}\n", sep = ""))
print(xtable(valPctCon(indPctChgCC, indfreqCC, 35), align = "crrrrr",
label = "tab:indCC", add.to.row = comment, hline.after = c(-1,0),
caption = "Industrial Certified-Certified Percentage Change per Value Segment"))
```
indPctChCC is a 3x5 matrix of strings. Could someone help me understand why the footnote is not appearing under the table with this current code?
add.to.row (and also hline.after) are arguments of the print function, not xtable().
This should get you where you want:
print(xtable(tab, align = "crrr",
label = "tab:indCC",
caption = "Industrial Certified-Certified Percentage Change per Value Segment"),
add.to.row = comment,
hline.after = c(-1,0))

Separate columns for text and code/output in Markdown

I am writing a small exercise book with Markdown. I would like to have the final output with the plots on a column and the document text on the other. Similar problems are addressed here and here. Unfortunately, they mainly desire one output per column. I would like to produce the output on a column and the text on the other. Really interesting is Docco, but it apprently shows the code output with the text.
A possible solution would be the RPres markdown horizontal rule: using ***, it creates two easy to use columns. But I do find documentation on its implementation in Markdown documents.
Here an image showing my results so far and an example of my code:
```{r setoption, cache=TRUE, warning=FALSE, message=FALSE, fig.width=12}
knitr::opts_chunk$set(cache=TRUE, warning=FALSE, message=FALSE, fig.width=4, echo = FALSE)
```
```{r, loadlibraries}
library(knitr)
library(lattice)
```
### Exercise 1 - 22/4/'16
Is the data set shown in the following figure symmetric or skewed? How many modes does this data set have?
```{r 1.1}
e1 <- rep(seq(1, 6, 1), c(6, 4, 2, 2, 4, 6))
barchart(table(e1), horizontal = FALSE, xlab = "", ylab = "Frequency")
```
**Solution:**
The data set is symmetric. Furthermore, it has two modes.
### Exercise 2 - 22/4/'16
Describe the shape of the dataset shown in the following figure.
```{r 2.1}
e2 <- rep(seq(1, 9, 1), c(6, 5, 4, 3, 2, 1, 1, 1, 1))
barchart(table(e2), ylab = "Frequency", horizontal = FALSE)
```
**Solution:**
The dataset is right skewed, also said right skewed, with one mode.
As you're asking for columns, my answer will be: table.
Using pipe_tables, figure and text can be alinged next to each other. However, this comes at a price:
The cells of pipe tables cannot contain block elements like paragraphs and lists, and cannot span multiple lines.
If this limitation is acceptable, pipe_tables provide a quite straightforward solution:
```{r img, fig.show = "hide", echo = FALSE}
library(knitr)
hist(rnorm(1000))
```
Figure|Explanation
-------------------------------|-------------------------
`r include_graphics(paste0(opts_chunk$get("fig.path"), "img-1.png"))`|Histogram of 1000 draws from a standard normal density.
Although the column headers cannot be omitted, you can leave them blank if desired.
Note that I initially suppress the plot (fig.show = "hide") and use include_graphics to include it afterwards. Otherwise, there would be a newline after the plot which disrupts the table.
(In knitr 1.12.3, include_graphics doesn't seem to work properly with inline code chunks. However, the current development version 1.12.25 works well.)
Extension
I hacked together an extension that allows to use a single chunk to generate and show the plots and some more features:
```{r setup, echo = FALSE}
library(knitr)
FigureNextToText <- function(number, # number of plot in chunk
text,
alt = "", # alternative text for image
label = opts_current$get("label"), # set explicitly when using inline!
ext = ".png",
headerL = " ", headerR = " ", # empty string confuses pandoc if only right header is set
widthL = 30, widthR = 30,
...) {
path <- fig_chunk(label = label, ext = ext, number = number, ...)
template <- "%s|%s
%s|%s
![%s](%s)|%s\r\n\r\n"
output <- sprintf(
template,
headerL, headerR,
paste0(rep("-", widthL), collapse = ""), paste0(rep("-", widthR), collapse = ""),
alt, path, text
)
return(asis_output(output))
}
```
```{r img, fig.show = "hide", echo = FALSE, results = "asis"}
library(knitr)
hist(rnorm(1000))
hist(runif(n = 1000, min = 0, max = 10))
FigureNextToText(1, text = "Histogram of draws from standard normal density.", widthL = 50, widthR = 10)
FigureNextToText(2, text = "Histogram of draws from uniform distribution.", headerR = "Explanation", alt = "Histogram 2.")
```
Some text.
`r FigureNextToText(2, text = "The same plot, this time inline.", label = "img", headerR = "Explanation", alt = "Histogram 2.")`
Some more text.
I know that the setup looks a little bit scary, but once FigureNextToText is defined, it can be called quite simply, e.g.:
FigureNextToText(2, text = "Histogram of draws from uniform distribution.", headerR = "Explanation", alt = "Histogram 2.")
Finding the correct values for widthL and widthR is somewhat cumbersome. This is because their effect depends on the number of characters in the cell, i.e. the filename of the image in the MD file and the alt text, too.

Resources