Better Tables with knitr::kable()
Note
This is one of a few posts on new features from a recent four-day knitr triage sprint; see that post for the full details of the sprint.
kable() is deliberately minimal—it makes a simple table and gets out of your way, and if you want fancy tables, you reach for many other packages such as kableExtra, gt, lt, and flextable, etc. But “minimal” shouldn’t mean “stuck,” and in this cycle, kable() picked up a handful of refinements. A few are about accessibility, a few are about getting numbers and captions to look right, and a few just remove old annoyances.
Accessible HTML tables
An HTML table is much more useful to screen-reader users when its header cells are marked as headers with a scope. kable() now adds scope="col" to column headers automatically—no option to set, you just get it:
knitr::kable(head(mtcars), format = "html")
# column headers now render as
If you also want the row-name column treated as row headers (<th scope="row">), opt in with a global option—it’s off by default because <th> cells can render differently from <td>:
options(knitr.table.html.row.header = TRUE)
(#1747)
Numbers that typeset correctly in LaTeX
LaTeX has strong opinions about numbers, and left to its own devices it renders minus signs, infinities, and scientific notation in text mode, where they look wrong. kable() can now typeset numeric columns in math mode instead, which fixes the minus signs and wraps decimal/thousands separator commas in braces to preserve spacing:
knitr::kable(df, format = "latex", numeric.math = TRUE)
# or globally:
options(knitr.table.numeric.math = TRUE)
(#1709)
Caption on top or bottom
For a floating LaTeX table, convention differs by field about whether the caption sits above or below. kable() defaults to the top; the new caption.pos argument lets you move it:
knitr::kable(df, format = "latex", caption = "...", caption.pos = "bottom")
# or globally:
options(knitr.table.caption.pos = "bottom")
(#1189)
Line breaks in simple tables
format = "simple" produces Pandoc’s simple tables, which are compact and readable in the Markdown source—but they can’t represent a line break inside a cell, so any \n used to wreck the layout. kable() now detects that case and emits a Pandoc multiline table instead, which preserves alignment:
x <- data.frame(item = "a", note = "first line\nsecond line")
knitr::kable(x, format = "simple")
(#2021)
Keep your whitespace, control your NAs
Two small quality-of-life additions:
- By default
kable()trims leading/trailing whitespace in cells. If you were using spaces to align numbers under a monospace font in the source, setoptions(knitr.kable.keep.whitespace = TRUE)to preserve them (#2066). - The new
naargument controls howNAvalues display in a single call—the per-call equivalent of the globalknitr.kable.NAoption, and it wins when both are set:
(#1813)knitr::kable(x, na = "") # hide NAs knitr::kable(x, na = "--") # show them as dashes