Given a data.frame A, how can I use subscripted rows and columns names? Eventually I want produce a table through kable() in rmarkdown (output: word document).
A <- data.frame(round(replicate(3, runif(2)),2))
rownames(A) <- c("Hola123", "Hola234")
A
X1 X2 X3
Hola123 0.47 0.55 0.66
Hola234 0.89 0.45 0.20
How could I make all numbers from row and column names subscripted when creating a table through kable(A)?
I have tried:
rownames(A) <- c(expression(Hola["123"]), expression(Hola["234"]))
names(A) <- c(expression(X["1"]), expression(X["2"]), expression(X["3"]))
But it does not appears subscripted when creating the table through kable() in the .rmd file.
To add subscripts in a rmarkdown document, you can embed your text between two tilde: text~sub~.
When using function kable, any text in the table is recognized as markdown syntax. So that your rmarkdown code should be:
```{r}
A <- data.frame(round(replicate(3, runif(2)),2))
rownames(A) <- c("Hola~123~", "Hola~234~")
names(A) <- c("X~1~", "X~2~", "X~3~")
knitr::kable(A)
```
Just one note about bamphe's response is that the correct code is misspelled. It should be \\textsubscript{}. It is missing the second "t".
And completing the answer, you might choose to use the arguments row.names and col.names inside kable, in this way:
A <- data.frame(round(replicate(3, runif(2)),2))
rownames(A) <- c("Hola\\textsubscript{123}", "Hola\\textsubscript{234}")
knitr::kable(A,
row.names = T,
col.names = c("X\\textsubscript{1}", "X\\textsubscript{2}", "X\\textsubscript{3}"),
escape = F)
I, too, was looking for a method that would allow for subscript and superscript in both html and pdf formats in markdown tables with kable. After a bit of searching, I finally found the text reference method explained here by #yihui-xie : bookdownguide
(ref:foo) H~2~O where foo is the reference and H~2~O the text.
My code example shows how the text reference can be used. Make sure to follow the cardinal rules:
The reference needs to be unique throughout the document
The reference should not have a white space following the "to be inserted stuff"
The reference needs to be in its own paragraph and have an empty line both above and below it
Note that only the referenced "foo" and "fo" will give the subscripts while the ~[]~ method will only work in html but not pdf.
(ref:foo) CO~2~/CO~2~
(ref:fo) CO~2~
```{r chunk-to-show-the-text-reference-method, echo = FALSE }
library(dplyr)
library(knitr)
library(kableExtra)
# Make lists
dtmin_name <- c("ref/ref","refrigerant/CO2","(ref:foo)",paste0("ground/","(ref:fo)"),"ground/water","air/refrigerant","water/refrigerant","water/CO2")
temp_diff <- c( 2.3, 1.4, 0.8, 6.8, 14, 6, 4, 3.46)
# Make dataframe and column names
dtmin_df <- data.frame(dtmin_name,temp_diff, stringsAsFactors = FALSE)
colnames <- data.frame("Interface Type ", "dT~min~ Interval [K]", stringsAsFactors = FALSE)
colnames(dtmin_df) <- colnames
# Make Table
kable(dtmin_df, caption = "Typical dT~min~ Temperature Intervals", booktabs = TRUE, format.args = list(big.mark = ",")) %>%
kable_styling(bootstrap_options = c("striped", "hover"),latex_options = c("striped","scale_down"))```
Related
I'm creating a table in a rmarkdown/html document and it's inadvertently changing cell values to an ordered list when in the format of for example (123). An example is below. Any help would be most welcomed.
---
output: html_document
---
df <-
data.frame(
col1 = c("a", "b"),
col2 = c("(123)", "(61)"),
stringsAsFactors = FALSE
)
knitr::kable(
df,
format = "html",
align = c('lr')
)
The values are recognized as markdown and pandoc thinks that (123) and (61) are list items. Try
df <- data.frame(col1 = c("a", "b"),
col2 = c("(123)", "(61)"),
stringsAsFactors = F)
df$col2 <- gsub("\\(", "(", df$col2)
df$col2 <- gsub("\\)", ")", df$col2)
to replace the parenthesis by their HTML entity code.
Note: stringsAsFactors = F is the new default in R > 4.0
Another approach is to disable the fancy_lists extension. This will apply globally to the document, so you might prefer #MartinSchmelzer's solution.
Just put this in your YAML:
output:
html_document:
md_extensions: "-fancy_lists"
After you've done this, you'll only get ordered lists with numbers (no letters or Roman numerals), and they have to be in the format 1., not (1) or 1), etc.
The following content of a .Rmd file:
---
title: "Untitled"
output:
html_document: default
---
```{r cars}
mtcars$am <- sprintf("(%s)", as.character(mtcars$am))
knitr::kable(mtcars, format = "html")
```
Will show ordered lists <ol><li></li></ol> in the am column, instead of the numbers in brackets (as produced with the sprintf) after rendering to html.
Is this intended? How can I work around this and have numbers in brackets show as they are in the html output?
The output of knitr::kable seems to be fine, showing:
<td style="text-align:left;"> (1) </td>
Details:
Using knitr 1.20
RStudio Server 1.1.453
note that removing format = "html" does not resolve the issue as in the real-life context I would like to do advanced formatting with css e.g. based on the classes of the produced tables
A quick workaround solution based on Michael Harper's accepted answer may be a method like so:
replacechars <- function(x) UseMethod("replacechars")
replacechars.default <- function(x) x
replacechars.character <- function(x) {
x <- gsub("(", "(", x, fixed = TRUE)
x <- gsub(")", ")", x, fixed = TRUE)
x
}
replacechars.factor <- function(x) {
levels(x) <- replacechars(levels(x))
x
}
replacechars.data.frame <- function(x) {
dfnames <- names(x)
x <- data.frame(lapply(x, replacechars), stringsAsFactors = FALSE)
names(x) <- dfnames
x
}
Example use:
mtcars <- datasets::mtcars
# Create a character with issues
mtcars$am <- sprintf("(%s)", as.character(mtcars$am))
# Create a factor with issues
mtcars$hp <- as.factor(mtcars$hp)
levels(mtcars$hp) <- sprintf("(%s)", levels(mtcars$hp))
replacechars(mtcars)
If you don't want to remove the format="html" argument, you could try using the HTML character entities for the parentheses (&lpar and &rpar) and then add the argument escape = FALSE:
```{r cars}
mtcars$am <- sprintf("(%s)", as.character(mtcars$am))
knitr::kable(mtcars, format = "html", escape = FALSE)
```
Still not entirely sure of what is causing the error though. It seems that the specific combination of parentheses is being processed strangely by knitr.
An alternative solution is to escape the parentheses, e.g.,
mtcars$am <- sprintf("\\(%s)", as.character(mtcars$am))
Then you won't need escape = FALSE.
See https://pandoc.org/MANUAL.html#backslash-escapes in Pandoc's Manual.
library(knitr)
library(kableExtra)
df <- data.frame("r1" = c(1,2,3,4), "r2"=c(4,5,6,6), "r3"=c(7,8,9,8), "r4"=c(11,12,13,89))
kable(df, format = "latex", booktabs = T, linesep = c('','','\\hline'))
actually this code should get a horizontal line at the second last line
But, i am not getting it. Is this a bug in kable or anything else?
I am trying to get a line above the last line for total. I am using Knitr Kable for this and knitting to pdf. Please Help
As far as I know, this is not how linesep works for kable. Instead you could use xtable:
library(xtable)
df <- data.frame("r1" = c(1,2,3,4), "r2"=c(4,5,6,6), "r3"=c(7,8,9,8), "r4"=c(11,12,13,89))
print(xtable(df), hline.after = c(0,3))
Why it does not work
This is the internal code in kable that produces the linesep:
linesep = if (nrow(x) > 1) {
c(rep(linesep, length.out = nrow(x) - 2), linesep[[1L]], '')
} else rep('', nrow(x))
linesep = ifelse(linesep == "", linesep, paste0('\n', linesep))
In line 2 you can see that your linesep argument is going to be repeated nrow(x)-2 times. So if you pass linesep = c("", "", "\\hline") to kable and you only have 4 rows, then this vector will be repeated 2 times. But since the vectors length is greater than 2, it only uses the first 2 elements which are empty. At the end of the snippet you have an empty character vector with 4 elements and therefore no horizontal ruler appears.
I am writing a conference abstract booklet using R/Sweave. I have already made the program booklet for printing that contains the id, author, title only.
Now I want to modify it to include the abstract (not for print). But abstracts are lengthy. My thought is to take the cell with the abstract info, and have it display below the row with the author info - expanded across the full width of the page.
ID--author--------title--------------------------------
abstract-----------------------------------------------
So every other row has only one column spanning the width of the entire table.
Is there a way to add multicolmn{x} to every other row?
If a solution can't be figured out, advice for how to print full abstracts in a nice way would be welcome. (Something other than "just use landscape" or "adjust column widths")
Also, it doesn't have to be PDF. I could switch to markdown/html - and make it look closer to real conference program schedules that have full abstracts on them. Again, one I figure out how to print a table where every other row has only one column that is the width of the entire table.
If you want to try - Here is a complete MWE for what I have working now. Note that it uses the R package lipsum which has to be installed via devtools/github.
\documentclass{article}
\usepackage{booktabs, multicol, array}
\usepackage[margin=0.75in]{geometry}
%%%%%%%%%%% Let tables to span entire page
\newcolumntype{L}[1]{>{\raggedright\let\newline\\\arraybackslash\hspace{0pt}}m{#1}}
<<echo=FALSE, warning=FALSE, message=FALSE>>=
# devtools::install_github("coolbutuseless/lipsum")
library(lipsum)
library(xtable)
knitr::opts_chunk$set(echo = FALSE, warning=FALSE, message=FALSE)
options(xtable.comment = FALSE)
tblalign <- "lL{0.5cm}|L{4cm}L{6cm}L{8cm}"
# fake data setup
dat <- data.frame(ID = c(1:3), author = substr(lipsum[1:3], 1, 40),
title = substr(lipsum[4:6], 1, 100),
abstract = lipsum[7:9])
names(dat)=c("\\multicolumn{1}{c}{\\textbf{\\large{ID}}}",
"\\multicolumn{1}{c}{\\textbf{\\large{Author List}}}",
"\\multicolumn{1}{c}{\\textbf{\\large{Title}}}",
"\\multicolumn{1}{c}{\\textbf{\\large{Abstract}}}")
#
\begin{document}
<<results='asis'>>=
print(
xtable(x = dat
, align = tblalign)
, table.placement = "H"
, sanitize.colnames.function=function(x){x}
, include.rownames = FALSE
, include.colnames = TRUE
, size = "small"
, floating = FALSE
, hline.after = c(0,1:nrow(dat))
)
#
\end{document}
Split data from abstract manually
out <- dat[,-4]
ab.list <- dat$abstract
then add.to.row
, add.to.row = list(pos = as.list(1:nrow(out)),
command = paste0("\\multicolumn{3}{L{15cm}}{\\textbf{Abstract: }", ab.list, "} \\\\"))
One approach using my package huxtable. I couldn't install lipsum for some reason, so just hacked it. This is in a .Rmd file with output pdf_document.
```{r, results = 'asis'}
lipsum <- rep(do.call(paste, list(rep('blah ', 100), collapse = '')), 10)
dat <- data.frame(ID = c(1:3), author = substr(lipsum[1:3], 1, 40),
title = substr(lipsum[4:6], 1, 100),
abstract = lipsum[7:9], stringsAsFactors = FALSE)
library(huxtable)
# shape data
datmat <- matrix(NA_character_, nrow(dat) * 2, 3)
datmat[seq(1, nrow(datmat), 2), ] <- as.matrix(dat[, c('ID', 'author', 'title')])
datmat[seq(2, nrow(datmat), 2), 1] <- dat$abstract
# print as PDF
ht <- as_huxtable(datmat)
colspan(ht)[seq(2, nrow(ht), 2), 1] <- 3
wrap(ht) <- TRUE
col_width(ht) <- c(.2, .2, .6)
number_format(ht) <- 0
ht
```
Is there a way to format a single cell in a table in rmarkdown? I am using kable to generate a table as follows:
library(knitr)
kable(data.frame(c('a','b','c'),c(1,2,3)))
I wish to bold "c" in the last row and also add a horizontal line at the end of the table. Any pointers?
This also works: the first argument is row number, so you can bold rows programmatically
or columns using column_spec.
library(kableExtra)
library(tidyverse)
kable(data.frame(letter =c('a','b','c'),number =c(1,2,3)))%>%
kable_styling()%>%
row_spec(3,bold=T,hline_after = T)
Just generalising the question to include other font faces. Pandoc offers other ways to easily reformat text, and as explained in the RMarkdown Cheatsheet:
italics can be achieved using *italics*
bold can be achieved using **bold**
strikethrough can be achieved using ~~strikethrough~~
This output will support the various output methods, including PDF, html and word:
Here is a basic example:
---
output: pdf_document: default
---
```{r}
knitr::kable(data.frame(char = c('*a*','**b**','~~c~~'),
num = c(1,2,3)))
```
Applying formatting with a function
To make it easier to select cells to reformat, I have put together the following function format_cells.
format_cells <- function(df, rows ,cols, value = c("italics", "bold", "strikethrough")){
# select the correct markup
# one * for italics, two ** for bold
map <- setNames(c("*", "**", "~~"), c("italics", "bold", "strikethrough"))
markup <- map[value]
for (r in rows){
for(c in cols){
# Make sure values are not factors
df[[c]] <- as.character( df[[c]])
# Update formatting
df[r, c] <- paste0(markup, df[r, c], markup)
}
}
return(df)
}
It allows the user to select the cell row and columns which need to be reformatted and select the styling to apply. Multiple cells can be selected at the same time if several values in a row/column need to be reformatted. Here is an example:
library(tidyverse)
df <- data.frame(char = c('a','b','c'),
num = c(1,2,3))
df %>%
format_cells(1, 1, "italics") %>%
format_cells(2, 2, "bold") %>%
format_cells(3, 1:2, "strikethrough") %>%
knitr::kable()
Further Reading: the kableExtra package has been written to offer a lot of extra controls of styling of tables. However, the advice is different for the different types of output, and therefore there are different approaches for HTML and LaTeX
Highlighting cells, rows or columns with pander is pretty straightforward:
> df <- data.frame(c('a','b','c'),c(1,2,3))
> emphasize.strong.cells(which(df == 3, arr.ind = TRUE))
> pander(df)
-------------------------------
c..a....b....c.. c.1..2..3.
------------------ ------------
a 1
b 2
c **3**
-------------------------------
But adding horizontal line to the table is out of the scope of markdown table specifications.