I have a table that I'd like to output in PDF format from an rmarkdown
document. However, with my limited Latex skills, I can't figure out how to get spanning columns, cell borders, and font faces the way I want them using xtable
with various Latex
additions.
我有一个表格,我想从rmarkdown文档中以PDF格式输出。然而,由于我有限的乳胶技能,我无法弄清楚如何使用xtable和各种Latex添加来获得跨越列,单元格边框和字体面。
I was able to get pretty much what I wanted using the FlexTable
function from the ReporteRs
package, but it looks like FlexTable
can only be used with rmarkdown
to produce html output, but not PDF output.
我能够使用ReporteRs包中的FlexTable函数得到我想要的东西,但看起来FlexTable只能用于rmarkdown来生成html输出,而不是PDF输出。
So, I'm looking for help with formatting my table using xtable
, or any other R package or (possibly custom) R function that can be used for programmatically creating reasonably complex tables for PDF output. Also, if there's some way to coax FlexTable
to work with PDF output, that would be great too.
所以,我正在寻找帮助,使用xtable格式化我的表,或任何其他R包或(可能是自定义)R函数,可用于以编程方式为PDF输出创建合理复杂的表。此外,如果有某种方法可以诱导FlexTable使用PDF输出,那也会很棒。
Below I create a table using FlexTable
so you can see what I'm aiming for. Following that, I provide a sample rmarkdown
document showing where I've gotten so far in my (somewhat lame) efforts to create a similar table using xtable
.
下面我使用FlexTable创建一个表,这样你就可以看到我的目标。接下来,我提供了一个示例rmarkdown文档,显示了到目前为止我在使用xtable创建类似表的过程中(有点蹩脚)所做的工作。
ReporteRs::FlexTable
version
First, let's create the data that will go into the table:
首先,让我们创建将进入表格的数据:
library(ReporteRs)
x = structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(
Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual",
"Predicted")), class = "table")
x=cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
x[, -c(1,2)] = sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
x = cbind(Actual=rownames(x), x)
Now for the FlexTable creation and formatting:
现在为FlexTable创建和格式化:
# Set up general table properties and formatting
cell_p = cellProperties(padding.right=3, padding.left=3)
par_p = parProperties(text.align="right")
# Create table
ft = FlexTable(x, header.columns=FALSE, body.cell.props=cell_p, body.par.props=par_p)
# Add three header rows
ft = addHeaderRow(ft, text.properties=textBold(), c("","Predicted"),
colspan=c(1,8), par.properties=parCenter())
ft = addHeaderRow(ft, text.properties=textBold(),
value=c("", "Count", "Overall\nPercent", "Row\nPercent", "Column\nPercent"),
colspan=c(1,rep(2,4)), par.properties=parCenter())
ft = addHeaderRow(ft, text.properties=textItalic(), par.properties=parCenter(),
value=colnames(x))
# Format specific cells
ft[1:2, 1, to="header", side="left"] = borderProperties(color="white")
ft[1:2, 1, to="header", side="top"] = borderProperties(color="white")
ft[3, 1, to="header"] = textProperties(font.style="normal", font.weight="bold")
ft[ , 1] = textProperties(font.style="italic")
ft[ , 2:3] = cellProperties(padding.right=7, padding.left=7)
ft[ , 1] = cellProperties(padding.right=10, padding.left=10)
# Display ft
ft
And here's what the final table looks like (this is a PNG screenshot of the table displayed in a browser window):
这是最终表的样子(这是浏览器窗口中显示的表格的PNG截图):
Now for my attempt at doing the same thing with xtable
.
现在我尝试用xtable做同样的事情。
xtable
version
Here's the rmarkdown
document and the header.tex
file:
这是rmarkdown文档和header.tex文件:
---
title: "Untitled"
author: "eipi10"
date: "11/19/2016"
output:
pdf_document:
fig_caption: yes
includes:
in_header: header.tex
---
```{r setup, include=FALSE}
library(knitr)
opts_chunk$set(echo = FALSE, message=FALSE)
```
```{r}
# Fake confusion matrix to work with
x = structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(
Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual",
"Predicted")), class = "table")
x=cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
x[, -c(1,2)] = sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
x = cbind(Actual=rownames(x), x)
```
```{r use_xtable, results="asis"}
# Output the confusion matrix created above as a latex table
library(xtable)
options(xtable.comment=FALSE)
# This is a modified version of a function created in the following SO answer:
# http://*.com/a/38978541/496488
make_addtorow <- function(row.name, terms, colSpan, width) {
# Custom row function
paste0(row.name,
paste0('& \\multicolumn{', colSpan, '}{C{', width, 'cm}}{',
terms,
'}',
collapse=''),
'\\\\')
}
addtorow <- list()
addtorow$pos <- list(-1,-1,-1,-1)
addtorow$command <- c(
"\\hline",
make_addtorow("", c("Predicted"), 8, 12),
"\\hline",
make_addtorow("", c("Count", "Percent", "Row Percent", "Column Percent"), 2, 3)
)
xtbl = xtable(x, caption="Created with xtable")
align(xtbl) <- c("|L{0cm}|", "L{1.2cm}|", rep("R{1cm}|",8))
print(xtbl,
include.rownames=FALSE,
tabular.environment="tabularx",
width="0.92\\textwidth",
add.to.row = addtorow)
```
File header.tex
that is used for knitting the rmarkdown document above:
文件header.tex用于编写上面的rmarkdown文档:
% xtable manual: https://cran.r-project.org/web/packages/xtable/vignettes/xtableGallery.pdf
\usepackage{array}
\usepackage{tabularx}
\newcolumntype{L}[1]{>{\raggedright\let\newline\\
\arraybackslash\hspace{0pt}}m{#1}}
\newcolumntype{C}[1]{>{\centering\let\newline\\
\arraybackslash\hspace{0pt}}m{#1}}
\newcolumntype{R}[1]{>{\raggedleft\let\newline\\
\arraybackslash\hspace{0pt}}m{#1}}
\newcolumntype{P}[1]{>{\raggedright\tabularxbackslash}p{#1}}
% Caption on top
% http://tex.stackexchange.com/a/14862/4762
\usepackage{floatrow}
\floatsetup[figure]{capposition=top}
And here's what the table looks like in the PDF output:
这是PDF输出中的表格:
2 个解决方案
#1
6
Quoting this comment:
引用此评论:
I'm looking for a way to do this programmatically from within the rmarkdown document without having to hard-code the formatting, so that it's reproducible and flexible.
我正在寻找一种方法来从rmarkdown文档中以编程方式执行此操作,而无需对格式进行硬编码,因此它具有可重现性和灵活性。
The following solution uses a hard-coded "template", but the template can be filled with any data (provided it has the same 2x8 structure).
以下解决方案使用硬编码的“模板”,但模板可以填充任何数据(前提是它具有相同的2x8结构)。
The generated table looks like this:
生成的表如下所示:
Full code below.
完整代码如下。
Basically, the final table consists of 9 columns, so the basic LaTeX structure is
基本上,最终表由9列组成,因此基本的LaTeX结构是
\begin{tabular}{|c|c|c|c|c|c|c|c|c|}
% rest of table
\end{tabular}
However, it is convenient to fix the width of the cells. This is possible with the custom column type C
(taken from here on TEX.SE), which allows for centered content with fixed width. This, together with the more compact syntax for repeating column types gives:
但是,固定细胞的宽度很方便。这可以使用自定义列类型C(在此处取自TEX.SE),它允许具有固定宽度的居中内容。这与重复列类型的更紧凑语法一起提供:
\begin{tabular}{|c *{8}{|C{1cm}}|}
% rest of table
\end{tabular}
(First column centered with flexible width, then 8 centered columns, each 1cm wide).
(第一列以柔性宽度为中心,然后是8个中心列,每个1cm宽)。
The cells spanning multiple columns are possible using \multicolumn
. These cells should also have a fixed width in order to have the cell captions break into two lines. Note that it is a fallacy to assume that the cells spanning two 1cm-columns should have a width of 2cm because the two spanned cells have additional padding between them. Some measurement revealed that about 2.436cm delivers good results.
使用\ multicolumn可以跨越多列。这些单元格也应该具有固定的宽度,以便将单元格标题分成两行。请注意,假设跨越两个1cm列的单元格应该具有2cm的宽度是错误的,因为两个跨越的单元格之间具有额外的填充。一些测量显示,大约2.436cm可以提供良好的结果。
Remark on the first column: Although \multicolumn{1}{...}{...}
looks useless at first sight, it is useful for changing the column type (including left/right) borders for a single cell. I used it to drop the leftmost vertical line in the first two rows.
备注第一列:虽然\ multicolumn {1} {...} {...}一眼看上去没用,但它对于更改单个单元格的列类型(包括左/右)边框很有用。我用它来删除前两行中最左边的垂直线。
\cline{x-y}
provides horizontal lines that span only the columns x
to y
.
\ cline {x-y}提供仅跨越x到y列的水平线。
Taking these pieces together gives:
把这些碎片放在一起给出:
\begin{tabular}{|c *{8}{|C{1cm}}|} \cline{2-9}
\multicolumn{1}{c|}{} & \multicolumn{8}{c|}{\textbf{Predicted}} \\ \cline{2-9}
\multicolumn{1}{c|}{} & \multicolumn{2}{c|}{\textbf{Count}} & \multicolumn{2}{C{2.436cm}|}{\textbf{Overall Percent}} & \multicolumn{2}{C{2.436cm}|}{\textbf{Row \newline Percent}} & \multicolumn{2}{C{2.436cm}|}{\textbf{Column Percent}} \\ \hline
% rest of table
\end{tabular}
Regarding the data, I dropped the last line of the code that generated to sample data to get:
关于数据,我删除了生成的代码的最后一行,以获取:
> x <- structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual", "Predicted")), class = "table")
> x <- cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
> x[, -c(1,2)] <- sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
> x
Fail Pass Fail Pass Fail Pass Fail Pass
Fail "34" "9" "40.5%" "10.7%" "79.1%" "20.9%" "85.0%" "20.5%"
Pass "6" "35" "7.1%" "41.7%" "14.6%" "85.4%" "15.0%" "79.5%"
To set the column and row names in italics, apply
要以斜体设置列名和行名,请应用
colnames(x) <- sprintf("\\emph{%s}", colnames(x)) # highlight colnames
rownames(x) <- sprintf("\\emph{%s}", rownames(x)) # highlight rownames
Then, the following xtable
code can be used:
然后,可以使用以下xtable代码:
print(xtable(x),
only.contents = TRUE,
comment = FALSE,
sanitize.colnames.function = identity,
sanitize.rownames.function = identity,
hline.after = 0:2)
The argument only.contents
suppresses the enclosing tabular
environment. Assigning the identity function to sanitize.colnames.function
and sanitize.rownames.function
means "don't sanitize". We need this because column and row names contain special LaTeX characters that should not be escaped (\emph
).
参数only.contents抑制了封闭的表格环境。将身份函数分配给sanitize.colnames.function和sanitize.rownames.function意味着“不要清理”。我们需要这个,因为列名和行名包含不应转义的特殊LaTeX字符(\ emph)。
The output should replace the %rest of table
placeholder from above.
输出应该从上面替换表占位符的%rest。
Conceptually, the code uses xtable
to generate only the table body but not the header because it is much easier to write the header manually.
从概念上讲,代码使用xtable仅生成表体而不生成标头,因为手动编写标头要容易得多。
Although the whole table header is "hard-coded", the data can be changed as required.
虽然整个表头是“硬编码的”,但可以根据需要更改数据。
Don't forget to escape all \
with a second \
! Also, the following must be added to the header (header.tex
):
别忘了用一秒钟逃脱所有\!此外,必须将以下内容添加到标头(header.tex):
\usepackage{array}
\newcolumntype{C}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}}m{#1}} % https://tex.stackexchange.com/a/12712/37118
I wrapped all the elements outlined above in a function PrintConfusionMatrix
that can be reused with any 2x8 data frame providing the data and column / row names.
我将上面概述的所有元素包装在PrintConfusionMatrix函数中,该函数可以与提供数据和列/行名称的任何2x8数据帧一起使用。
Full code:
---
output:
pdf_document:
keep_tex: yes
includes:
in_header: header.tex
---
```{r, echo = FALSE}
library(xtable)
# Sample data from question
x <- structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual", "Predicted")), class = "table")
x <- cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
x[, -c(1,2)] <- sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
#x <- cbind(Actual=rownames(x), x) # dropped; better not to add row names to data
PrintConfusionMatrix <- function(data, ...) {
stopifnot(all(dim(x) == c(2, 8)))
colnames(x) <- sprintf("\\emph{%s}", colnames(x)) # highlight colnames
rownames(x) <- sprintf("\\emph{%s}", rownames(x)) # highlight rownames
cat('\\begin{tabular}{|c *{8}{|C{1cm}}|} \\cline{2-9}
\\multicolumn{1}{c|}{} & \\multicolumn{8}{c|}{\\textbf{Predicted}} \\\\ \\cline{2-9}
\\multicolumn{1}{c|}{} & \\multicolumn{2}{c|}{\\textbf{Count}} & \\multicolumn{2}{C{2.436cm}|}{\\textbf{Overall Percent}} & \\multicolumn{2}{C{2.436cm}|}{\\textbf{Row \\newline Percent}} & \\multicolumn{2}{C{2.436cm}|}{\\textbf{Column Percent}} \\\\ \\hline
\\textbf{Actual} ')
print(xtable(x),
only.contents = TRUE,
comment = FALSE,
sanitize.colnames.function = identity,
sanitize.rownames.function = identity,
hline.after = 0:2,
...)
cat("\\end{tabular}")
}
```
```{r, results='asis'}
PrintConfusionMatrix(x)
```
#2
4
Not complete but perhaps something to get you started: use \cline
to limit the range of the \hline
and use \multicolumn
to allow headers to span multiple columns. Tried a few different ways with differing issues with each.
不完整但也许可以帮助您入门:使用\ cline限制\ hline的范围并使用\ multicolumn允许标题跨越多列。尝试了几种不同的方式,每种方式都有不同的问题。
```{r, results="asis"}
# Fake confusion matrix to work with
x = structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(
Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual",
"Predicted")), class = "table")
x=cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
x[, -c(1,2)] = sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
x = cbind(Actual=rownames(x), x)
# output
library(xtable)
# Create function for headers to span multiple columns
spanfun <- function(nms, span=2, align="|c|") {
out = paste0("& \\multicolumn{", span, "}{", align, "}{", nms, "}", collapse=" ")
paste(out, "\\\\")
}
# \\cline limits the range of \hline, so omits first cell
addtorow = list(list( -1, -1, -1, -1),
c("\\cline{2-9} \\multicolumn{1}{c|}{} ",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c|}{} ",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent")) ))
print.xtable(
xtable(x, align=c("|l|","|l|", rep(c("r|"),8))),
include.rownames=FALSE,
add.to.row=addtorow, include.colnames=TRUE)
```
update with a couple of other imperfect attempts
更新与其他几个不完美的尝试
Create header file
创建头文件
txt <- "
\\usepackage{tabularx, array, booktabs,siunitx}
\\newcolumntype{Y}{>{\\raggedleft\\arraybackslash}X}
"
cat(txt, file="so.sty")
Version 2
版本2
spanfun <- function(nms, span=2, align="|c|") {
out = paste0("& \\multicolumn{", span, "}{", align, "}{\\bfseries{", nms, "}}", collapse=" ")
paste(out, "\\\\")
}
addtorow = list(list( -1, -1, -1, -1), c("\\cline{2-9} \\multicolumn{1}{c}{} ",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c}{} ",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent"))
))
# make pass / fail row (3rd row) italic
# but vertical lines are not aligned
# some double lines
# cell alignment all over the shop
print.xtable(
xtable(x, align= c("l", "|l|", rep("S|", 8))),
add.to.row=addtorow,
include.rownames=FALSE,
include.colnames=TRUE,
sanitize.colnames.function=function(x) {paste0('{\\textit{', x ,'}}')})
Version 3
版本3
addtorow = list(list( -1, -1, -1, -1, 0), c("\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent")),
paste(paste0(" \\multicolumn{1}{|c|}{{\\textit{", colnames(x),"}}}", collapse=" & "), "\\\\")
))
# Same issues as preceding example
print(xtable(x, align= c("|l|", "|l|", rep("Y|", 8))),
add.to.row = addtorow,
include.rownames=FALSE,
include.colnames=FALSE,
tabular.environment="tabularx",
width="\\textwidth")
Version 4 -- help from % https://tex.stackexchange.com/questions/140353/align-position-of-decimal-point-within-table-of-numbers-text-and-percentage-val
版本4 - 来自%https://tex.stackexchange.com/questions/140353/align-position-of-decimal-point-within-table-of-numbers-text-and-percentage-val的帮助
Create header file
创建头文件
txt <- "
\\usepackage{booktabs,dcolumn}
\\newcolumntype{Y}{D..{4.3}}
"
cat(txt, file="so.sty")
addtorow = list(list( -1, -1, -1, -1, 0), c("\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent")),
paste0("\\multicolumn{1}{|c|}{{\\textit{", colnames(x)[1],"}}} & ",
paste0(" \\multicolumn{1}{c|}{{\\textit{", colnames(x)[-1],"}}}", collapse=" & "), "\\\\")
))
# Again issues with vertical lines but alignment is better
print(xtable(x, align= c("|l|", "|l|", rep("Y|", 8))),
add.to.row = addtorow,
include.rownames=FALSE,
include.colnames=FALSE)
#1
6
Quoting this comment:
引用此评论:
I'm looking for a way to do this programmatically from within the rmarkdown document without having to hard-code the formatting, so that it's reproducible and flexible.
我正在寻找一种方法来从rmarkdown文档中以编程方式执行此操作,而无需对格式进行硬编码,因此它具有可重现性和灵活性。
The following solution uses a hard-coded "template", but the template can be filled with any data (provided it has the same 2x8 structure).
以下解决方案使用硬编码的“模板”,但模板可以填充任何数据(前提是它具有相同的2x8结构)。
The generated table looks like this:
生成的表如下所示:
Full code below.
完整代码如下。
Basically, the final table consists of 9 columns, so the basic LaTeX structure is
基本上,最终表由9列组成,因此基本的LaTeX结构是
\begin{tabular}{|c|c|c|c|c|c|c|c|c|}
% rest of table
\end{tabular}
However, it is convenient to fix the width of the cells. This is possible with the custom column type C
(taken from here on TEX.SE), which allows for centered content with fixed width. This, together with the more compact syntax for repeating column types gives:
但是,固定细胞的宽度很方便。这可以使用自定义列类型C(在此处取自TEX.SE),它允许具有固定宽度的居中内容。这与重复列类型的更紧凑语法一起提供:
\begin{tabular}{|c *{8}{|C{1cm}}|}
% rest of table
\end{tabular}
(First column centered with flexible width, then 8 centered columns, each 1cm wide).
(第一列以柔性宽度为中心,然后是8个中心列,每个1cm宽)。
The cells spanning multiple columns are possible using \multicolumn
. These cells should also have a fixed width in order to have the cell captions break into two lines. Note that it is a fallacy to assume that the cells spanning two 1cm-columns should have a width of 2cm because the two spanned cells have additional padding between them. Some measurement revealed that about 2.436cm delivers good results.
使用\ multicolumn可以跨越多列。这些单元格也应该具有固定的宽度,以便将单元格标题分成两行。请注意,假设跨越两个1cm列的单元格应该具有2cm的宽度是错误的,因为两个跨越的单元格之间具有额外的填充。一些测量显示,大约2.436cm可以提供良好的结果。
Remark on the first column: Although \multicolumn{1}{...}{...}
looks useless at first sight, it is useful for changing the column type (including left/right) borders for a single cell. I used it to drop the leftmost vertical line in the first two rows.
备注第一列:虽然\ multicolumn {1} {...} {...}一眼看上去没用,但它对于更改单个单元格的列类型(包括左/右)边框很有用。我用它来删除前两行中最左边的垂直线。
\cline{x-y}
provides horizontal lines that span only the columns x
to y
.
\ cline {x-y}提供仅跨越x到y列的水平线。
Taking these pieces together gives:
把这些碎片放在一起给出:
\begin{tabular}{|c *{8}{|C{1cm}}|} \cline{2-9}
\multicolumn{1}{c|}{} & \multicolumn{8}{c|}{\textbf{Predicted}} \\ \cline{2-9}
\multicolumn{1}{c|}{} & \multicolumn{2}{c|}{\textbf{Count}} & \multicolumn{2}{C{2.436cm}|}{\textbf{Overall Percent}} & \multicolumn{2}{C{2.436cm}|}{\textbf{Row \newline Percent}} & \multicolumn{2}{C{2.436cm}|}{\textbf{Column Percent}} \\ \hline
% rest of table
\end{tabular}
Regarding the data, I dropped the last line of the code that generated to sample data to get:
关于数据,我删除了生成的代码的最后一行,以获取:
> x <- structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual", "Predicted")), class = "table")
> x <- cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
> x[, -c(1,2)] <- sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
> x
Fail Pass Fail Pass Fail Pass Fail Pass
Fail "34" "9" "40.5%" "10.7%" "79.1%" "20.9%" "85.0%" "20.5%"
Pass "6" "35" "7.1%" "41.7%" "14.6%" "85.4%" "15.0%" "79.5%"
To set the column and row names in italics, apply
要以斜体设置列名和行名,请应用
colnames(x) <- sprintf("\\emph{%s}", colnames(x)) # highlight colnames
rownames(x) <- sprintf("\\emph{%s}", rownames(x)) # highlight rownames
Then, the following xtable
code can be used:
然后,可以使用以下xtable代码:
print(xtable(x),
only.contents = TRUE,
comment = FALSE,
sanitize.colnames.function = identity,
sanitize.rownames.function = identity,
hline.after = 0:2)
The argument only.contents
suppresses the enclosing tabular
environment. Assigning the identity function to sanitize.colnames.function
and sanitize.rownames.function
means "don't sanitize". We need this because column and row names contain special LaTeX characters that should not be escaped (\emph
).
参数only.contents抑制了封闭的表格环境。将身份函数分配给sanitize.colnames.function和sanitize.rownames.function意味着“不要清理”。我们需要这个,因为列名和行名包含不应转义的特殊LaTeX字符(\ emph)。
The output should replace the %rest of table
placeholder from above.
输出应该从上面替换表占位符的%rest。
Conceptually, the code uses xtable
to generate only the table body but not the header because it is much easier to write the header manually.
从概念上讲,代码使用xtable仅生成表体而不生成标头,因为手动编写标头要容易得多。
Although the whole table header is "hard-coded", the data can be changed as required.
虽然整个表头是“硬编码的”,但可以根据需要更改数据。
Don't forget to escape all \
with a second \
! Also, the following must be added to the header (header.tex
):
别忘了用一秒钟逃脱所有\!此外,必须将以下内容添加到标头(header.tex):
\usepackage{array}
\newcolumntype{C}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}}m{#1}} % https://tex.stackexchange.com/a/12712/37118
I wrapped all the elements outlined above in a function PrintConfusionMatrix
that can be reused with any 2x8 data frame providing the data and column / row names.
我将上面概述的所有元素包装在PrintConfusionMatrix函数中,该函数可以与提供数据和列/行名称的任何2x8数据帧一起使用。
Full code:
---
output:
pdf_document:
keep_tex: yes
includes:
in_header: header.tex
---
```{r, echo = FALSE}
library(xtable)
# Sample data from question
x <- structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual", "Predicted")), class = "table")
x <- cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
x[, -c(1,2)] <- sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
#x <- cbind(Actual=rownames(x), x) # dropped; better not to add row names to data
PrintConfusionMatrix <- function(data, ...) {
stopifnot(all(dim(x) == c(2, 8)))
colnames(x) <- sprintf("\\emph{%s}", colnames(x)) # highlight colnames
rownames(x) <- sprintf("\\emph{%s}", rownames(x)) # highlight rownames
cat('\\begin{tabular}{|c *{8}{|C{1cm}}|} \\cline{2-9}
\\multicolumn{1}{c|}{} & \\multicolumn{8}{c|}{\\textbf{Predicted}} \\\\ \\cline{2-9}
\\multicolumn{1}{c|}{} & \\multicolumn{2}{c|}{\\textbf{Count}} & \\multicolumn{2}{C{2.436cm}|}{\\textbf{Overall Percent}} & \\multicolumn{2}{C{2.436cm}|}{\\textbf{Row \\newline Percent}} & \\multicolumn{2}{C{2.436cm}|}{\\textbf{Column Percent}} \\\\ \\hline
\\textbf{Actual} ')
print(xtable(x),
only.contents = TRUE,
comment = FALSE,
sanitize.colnames.function = identity,
sanitize.rownames.function = identity,
hline.after = 0:2,
...)
cat("\\end{tabular}")
}
```
```{r, results='asis'}
PrintConfusionMatrix(x)
```
#2
4
Not complete but perhaps something to get you started: use \cline
to limit the range of the \hline
and use \multicolumn
to allow headers to span multiple columns. Tried a few different ways with differing issues with each.
不完整但也许可以帮助您入门:使用\ cline限制\ hline的范围并使用\ multicolumn允许标题跨越多列。尝试了几种不同的方式,每种方式都有不同的问题。
```{r, results="asis"}
# Fake confusion matrix to work with
x = structure(c(34L, 6L, 9L, 35L), .Dim = c(2L, 2L), .Dimnames = structure(list(
Actual = c("Fail", "Pass"), Predicted = c("Fail", "Pass")), .Names = c("Actual",
"Predicted")), class = "table")
x=cbind(x, prop.table(x), prop.table(x, 1), prop.table(x,2))
x[, -c(1,2)] = sapply(x[,-c(1,2)], function(i) paste0(sprintf("%1.1f", i*100),"%"))
x = cbind(Actual=rownames(x), x)
# output
library(xtable)
# Create function for headers to span multiple columns
spanfun <- function(nms, span=2, align="|c|") {
out = paste0("& \\multicolumn{", span, "}{", align, "}{", nms, "}", collapse=" ")
paste(out, "\\\\")
}
# \\cline limits the range of \hline, so omits first cell
addtorow = list(list( -1, -1, -1, -1),
c("\\cline{2-9} \\multicolumn{1}{c|}{} ",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c|}{} ",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent")) ))
print.xtable(
xtable(x, align=c("|l|","|l|", rep(c("r|"),8))),
include.rownames=FALSE,
add.to.row=addtorow, include.colnames=TRUE)
```
update with a couple of other imperfect attempts
更新与其他几个不完美的尝试
Create header file
创建头文件
txt <- "
\\usepackage{tabularx, array, booktabs,siunitx}
\\newcolumntype{Y}{>{\\raggedleft\\arraybackslash}X}
"
cat(txt, file="so.sty")
Version 2
版本2
spanfun <- function(nms, span=2, align="|c|") {
out = paste0("& \\multicolumn{", span, "}{", align, "}{\\bfseries{", nms, "}}", collapse=" ")
paste(out, "\\\\")
}
addtorow = list(list( -1, -1, -1, -1), c("\\cline{2-9} \\multicolumn{1}{c}{} ",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c}{} ",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent"))
))
# make pass / fail row (3rd row) italic
# but vertical lines are not aligned
# some double lines
# cell alignment all over the shop
print.xtable(
xtable(x, align= c("l", "|l|", rep("S|", 8))),
add.to.row=addtorow,
include.rownames=FALSE,
include.colnames=TRUE,
sanitize.colnames.function=function(x) {paste0('{\\textit{', x ,'}}')})
Version 3
版本3
addtorow = list(list( -1, -1, -1, -1, 0), c("\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent")),
paste(paste0(" \\multicolumn{1}{|c|}{{\\textit{", colnames(x),"}}}", collapse=" & "), "\\\\")
))
# Same issues as preceding example
print(xtable(x, align= c("|l|", "|l|", rep("Y|", 8))),
add.to.row = addtorow,
include.rownames=FALSE,
include.colnames=FALSE,
tabular.environment="tabularx",
width="\\textwidth")
Version 4 -- help from % https://tex.stackexchange.com/questions/140353/align-position-of-decimal-point-within-table-of-numbers-text-and-percentage-val
版本4 - 来自%https://tex.stackexchange.com/questions/140353/align-position-of-decimal-point-within-table-of-numbers-text-and-percentage-val的帮助
Create header file
创建头文件
txt <- "
\\usepackage{booktabs,dcolumn}
\\newcolumntype{Y}{D..{4.3}}
"
cat(txt, file="so.sty")
addtorow = list(list( -1, -1, -1, -1, 0), c("\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun("Predicted", span=8),
"\\cline{2-9} \\multicolumn{1}{c}{}",
spanfun(c("Count", "Percent", "Row Percent", "Column Percent")),
paste0("\\multicolumn{1}{|c|}{{\\textit{", colnames(x)[1],"}}} & ",
paste0(" \\multicolumn{1}{c|}{{\\textit{", colnames(x)[-1],"}}}", collapse=" & "), "\\\\")
))
# Again issues with vertical lines but alignment is better
print(xtable(x, align= c("|l|", "|l|", rep("Y|", 8))),
add.to.row = addtorow,
include.rownames=FALSE,
include.colnames=FALSE)