kable/kableExtra Add superscript to group labels in group_rows - r

I have a table that I am creating for a pdf presentation using kable and kableExtra. I am wanting to group the rows and I need to use superscripts in the row group labels. I have tried several different things. Here is an example of some of the methods I have tried so far.
library(kable)
library(kableExtra)
foo <- data.frame(a = 1:10, b = 11:20, c = 21:30)
kable(foo, format = "latex", booktabs = T, row.names = FALSE, linesep = "", escape = FALSE) %>%
kable_styling(latex_options = c("striped")) %>%
group_rows("Group1<sup>a</sup>", 1, 2) %>%
group_rows(paste0("Group2", footnote_marker_alphabet(1), sep = ""), 3, 4) %>%
group_rows(expression("Group3"^a), 5, 6) %>%
group_rows("Group4\\textsuperscript{a}", 7, 8)
I have run out of ideas and haven't been able to find any additional suggest in my search.

You need escape=FALSE in your group_rows() calls to allow the latex commands to be interpreted. You also seem to need to double each backslash (I don't quite understand why). After that, there are a few different options that work:
kable(foo, format = "latex", booktabs = T, row.names = FALSE, linesep = "", escape = FALSE) %>%
kable_styling(latex_options = c("striped")) %>%
group_rows("$\\\\text{Group1}^a$", 1, 2, escape = FALSE) %>%
group_rows(paste0("Group2\\\\", footnote_marker_alphabet(1), sep = ""), 3, 4, escape = FALSE) %>%
# I don't think expression() is helpful, doesn't seem to get converted
# to latex
group_rows(expression("Group3"^a), 5, 6) %>%
group_rows("Group4\\\\textsuperscript{a}", 7, 8, escape = FALSE)

Related

Wrapping column names in KableExtra with str_wrap

I have a table that looks like this:
wide.df <- cbind.data.frame(
Letter = c("A", "B", "C", "D", "E", "F"),
`Component 1 - Class Grades`= c(30,25,15,10,10,10),
`Component 2 - External Grades` = c(10, 10, 10, 15, 25, 30)
)
However, when I output it into kableExtra it obviously comes out looking crappy because of the length of the names.
wide.df %>%
knitr::kable(caption = "Example Kable", row.names = F) %>%
row_spec(0, bold = T, color = "white", background = "darkred")%>%
add_header_above(c("." = 1, "Count of Grades" = (ncol(wide.df)-1)), background = "darkred", color = "white") %>%
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed"),
fixed_thead = TRUE)
I'd like these column labels to wrap after the component number, e.g.
Component 1 -
Class Grades
Usually I used strwrap in a function for this kind of wrapping for graph titles:
wrapper <- function(x, ...)
{
paste(strwrap(x, ...), collapse = "\n")
}
However I can't figure out a way to do this to rename columns in my dataframe to include linebreaks. When I do this it returns nonsense.
I tried to pivot the data to long format and do the renaming, as seen below:
long.df <-
wide.df %>%
pivot_longer(-Letter, names_to = "Component", values_to = "Perc") %>%
mutate(Component = wrapper(Component, 15))
This, however, just gives me incredible long repeats of the component name in that column.
Any help on an easy way to do this? Either by renaming the columns with appropriate breaks in them, or pivoting to long format to rename then pivoting back to wide?
Also, as a bonus - does anyone know how to make my add_header above color in the header over the "Letter" column without including the dummy "." there? When that header value is blank there is no color
Here's a way of solving the problems:
library(dplyr)
library(kableExtra)
wide.df <- cbind.data.frame(
Letter = c("A", "B", "C", "D", "E", "F"),
`Component 1 - Class Grades` = c(30, 25, 15, 10, 10, 10),
`Component 2 - External Grades` = c(10, 10, 10, 15, 25, 30)
)
wrapper <- function(x, ...)
{
sapply(x, function(y)
paste(strwrap(y, ...), collapse = "<br>"))
}
wide.df %>%
rename_with(.fn = wrapper, width = 15) |>
knitr::kable(caption = "Example Kable",
row.names = F,
escape = FALSE) %>%
row_spec(0,
bold = T,
color = "white",
background = "darkred") %>%
add_header_above(
c(
"<span></span>" = 1,
"Count of Grades" = (ncol(wide.df) - 1)
),
background = "darkred",
color = "white",
escape = FALSE
) %>%
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed"),
fixed_thead = TRUE
)
This works by:
Using rename_with to rename columns according to the wrapper function
passing the html <br> instead of \n, as the kable parts seemed to ignore the newline
(rewriting the wrapper function slightly to handle/return vectors)
adding escape = FALSE to knitr::kable to ensure newline is kept in
For Bonus question: adding "<span></span>" as a blank column name and escape = FALSE to add_header_above

Allow duplicated names in binded tables

UPDATE for why I changed my votes.
This code has the table displayed but R doesn't knit pdf.
all_jt %>%
kbl(longtable = T, booktabs = T,
caption = "table") %>%
remove_column(7) %>%
add_header_above(c(" " = 2, "Year 1" = 4, "Year 2" = 4)) %>%
kable_styling(latex_options = c("repeat_header"))
Quitting from lines 13-37 (test_table.Rmd)
Error in remove_column(., 7) :
Removing columns was not implemented for latex kables yet
switching to select(-7) as in here Remove_Column from a kable table which will be output as latex/pdf doesn't work because R doesn't like duplicated column names.
I have two ANOVA tables, jt_1 and jt_2 below, that I want to merge and keep 1 column for the model term only. As I remove the duplicated column, R added .1 to the tail of columns' 7, 8, 9 and 10 names.
library(emmeans)
library(stringr)
warp.lm <- lm(breaks ~ wool * tension, data = warpbreaks)
jt_1 <- print(joint_tests(warp.lm), export = T) %>% as.data.frame()
jt_2 <- jt_1
all_jt <- cbind(jt_1, jt_2) %>%
setNames(gsub("summary.", "", colnames(.)))
all_jt[,-6]%>% #to remove the duplicated column for model term
data.frame(check.names = F) %>%
kbl(longtable = T, booktabs = T,
caption = "table") %>%
add_header_above(c(" " = 2, "Year 1" = 4, "Year 2" = 4)) %>%
kable_styling(latex_options = c("repeat_header"))
Here is a brief idea of what I need.
Many thanks in advance.
You can use remove_column function from kableExtra to remove a column instead of all_jt[,-6] which makes the column name unique.
library(knitr)
library(kableExtra)
all_jt %>%
kbl(longtable = T, booktabs = T,
caption = "table") %>%
remove_column(7) %>%
add_header_above(c(" " = 2, "Year 1" = 4, "Year 2" = 4)) %>%
kable_styling(latex_options = c("repeat_header"))
R does not like duplicate column names in data.frames. If you step through your last code block line by line you will notice that all_jt[, -6] makes column names unique by adding the ".1" suffix.
The/a solution is to provide column names to kbl directly, e.g.
all_jt[,-6] %>%
kbl(longtable = T, booktabs = T,
col.names = gsub("\\.\\d", "", names(.)),
caption = "table") %>%
add_header_above(c(" " = 2, "Year 1" = 4, "Year 2" = 4)) %>%
kable_styling(latex_options = c("repeat_header"))
This produces

kableExtra addfootnote general spanning multiple lines with PDF (LaTeX) output

Problem
Code
# Toy Data
ID <- c(paste("G0", as.character(1:9), sep = ""),"G10","G11","Mean")
V1 <- c(10.06,11.06,12.06,13.06,14.06,15.06,16.06,17.07,18.07,19.07,6.88,13.86)
V2 <- c(0.21,0.03,0.09,0.03,0.09,0.03,0.09,0.03,0.09,0.21,0.31,NA)
tbl <- data.frame(ID, V1, V1, V2, V1, V2, V1, V2, V2)
colnames(tbl) <- c('ID','Get. \\%','Get. \\%','K','Get. \\%','K','Get. \\%','K','P')
# Specify kable NA value and load kableExtra
options(knitr.kable.NA = '--')
require(kableExtra)
# Generate table for PDF output (LaTeX)
kbl(tbl, format = 'latex', align = 'l', booktabs = T, escape = F, digits = 2,
linesep = "", caption = "This is a table caption.") %>%
add_header_above(c(" ", "AB", "BP" = 2, "CK" = 2, "JAM" = 2, ""), bold = T) %>%
column_spec(1, width = '1.15cm') %>%
row_spec(11, hline_after = T) %>%
row_spec(12, bold = T) %>%
kable_styling(position = "center", latex_options = "hold_position") %>%
footnote(general_title = "Note.", footnote_as_chunk = T,
general = "Relatively long footnote that I would like to span
a couple of lines. Relatively long footnote that I
would like to span a couple of lines.")
Output
Comments
Issue 1: The output displays 'makecell[1]' in the footnote, which I obviously do not want included. Adding the argument escape = T did not resolve this problem as I expected it might have.
N.B. By setting footnote_as_chunk = F, this issue was resolved, but with the unwanted effect of introducing a line break before the caption starts. This is demonstrated by Peter's answer below.
Issue 2 The footnote does not want to be constrained to the length of the table. I suppose one might be able to manually add line breaks in the footnote string, but this seems like tedious work-around, and I'm hoping there is a method for achieving this more efficiently. The documentation shows (see Table 4, p. 25) an example of how one might circumvent this problem, but the code is absent.
EDIT: This issue (#2) was resolved by setting threeparttable = T when calling kbl.
Compiling with pdflatex or xelatex does not seem to make any difference. Any insight would be much appreciated.
Try this:
library(kableExtra)
library(magrittr)
kbl(tbl,
format = 'latex',
longtable = TRUE,
align = 'l',
booktabs = T,
escape = F,
digits = 2,
linesep = "",
caption = "This is a table caption.") %>%
add_header_above(c(" ", "AB", "BP" = 2, "CK" = 2, "JAM" = 2, ""), bold = T) %>%
column_spec(1, width = '1.15cm') %>%
row_spec(11, hline_after = T) %>%
row_spec(12, bold = T) %>%
kable_styling(position = "center", latex_options = "hold_position", full_width = FALSE) %>%
footnote(general_title = "Note.",
footnote_as_chunk = TRUE,
threeparttable = TRUE,
general = "Relatively long footnote that I would like to span a couple of lines. Relatively long footnote that I would like to span a couple of lines.")
With footnote_as_chunk = TRUE using the "general" footnote option "Note." and the "Footnote...." text start on the same line. As in this example, image below.

kable indent rows with specific hspace for all columns

The following code add indentations to the 2nd & 4th rows for first column only.
library(kableExtra)
knitr::kable(head(mtcars[ ,1:4]), "latex") %>%
add_indent(positions = c(2, 4))
Wondering how to add indentations of specific hspace to the 2nd & 4th rows for all columns. Something like this
library(kableExtra)
knitr::kable(head(mtcars[ ,1:4]), "latex") %>%
add_indent(positions = c(2, 4), hspace = "2em", allCols = TRUE)
add_indent is indeed only made for the first column.
A workaround would be to add the indentation manually in your data frame:
head(mtcars) %>%
mutate_all(., funs(ifelse(row_number() %in% c(2, 4),
paste0("\\hspace{1em}", .), .))) %>%
kable(format = "latex", booktabs = T, escape = F)
gives:
We can write a version of the add_indent function that adds this option. Doing it this way ensures that kable options such as digits are applied consistently in each row.
add_indent = function(kable_input, positions, allCols = FALSE) {
out = kableExtra::add_indent(kable_input, positions)
if (allCols){
table_info <- magic_mirror(kable_input)
for (i in positions + table_info$position_offset) {
rowtext <- table_info$contents[i]
table_info$contents[i] <- gsub(' &', paste(' &', kableExtra:::latex_indent_unit('')), rowtext)
out <- gsub(rowtext, table_info$contents[i], out, fixed = T)
}
out <- structure(out, format = "latex", class = "knitr_kable")
attr(out, "kable_meta") <- table_info
}
return(out)
}
kable(head(mtcars[ ,1:4]), "latex", align = 'l') %>%
add_indent(positions = c(2, 4), allCols = T)

LaTex table in knitr with complex structure (rotating multirow text, removing column separators)

I need to create a latex table in RStudio for pdf output with the following structure:
This table was created for html output with the following code:
mat <- data.frame(a = c("column header","column header"),
rowx=c("row1","row2"),b = c("a","b"),
c = c("x","y"))
kable(mat, align = "c",col.names = c("","","v1","v2")) %>%
kable_styling(bootstrap_options = "striped", full_width = F,
position = "left",font_size = 12) %>%
column_spec(1, bold = T,width="2em",extra_css="transform: rotate(-90deg);") %>%
collapse_rows(columns = 1, valign = "middle") %>%
add_header_above(c(" " = 2, "row header" = 2))
I need to create a similar structure with LaTeX tables.
His is how far I got:
mat <- data.frame(a = c("column header","column header"),
rowx=c("row1","row2"),b = c("a","b"),c = c("x","y"))
kable(mat, align = "c",col.names = c("","","v1","v2")) %>%
kable_styling(bootstrap_options = "striped", full_width = F, position = "left",font_size = 12) %>%
collapse_rows(columns = 1, latex_hline = "none") %>%
add_header_above(c(" " = 2, "rows" = 2))
So I still need at least 2 more things:
rotate the label in the very first column
remove the spurious leftmost column separator in the second row.
Can this be achieved with kableExtra commands and parameters?
Here's a shot with huxtable (my package):
as_hux(mat, add_colnames = TRUE) %>%
insert_row(c("", "", "rows", "")) %>%
merge_cells(3:4, 1) %>%
merge_cells(1, 3:4) %>%
merge_cells(1:2, 1:2) %>%
set_rotation(3, 1, 90) %>%
set_bottom_border(0.4) %>%
set_bold(1:2, everywhere, TRUE) %>%
set_wrap(3, 1, TRUE) %>%
set_bottom_padding(4, -1, 48) %>%
set_bottom_padding(3, -1, 30) %>%
set_row_height(c("1em", "1em", "1.5em", "1.5em")) %>%
quick_pdf()
I have to admit, this took a lot of tweaking. TeX tables are hard to understand....

Resources