knitr/rmarkdown/Latex:如何使用dcolumn自定义合理的xtable列,同时抑制其他dcolumn格式

时间:2022-03-03 18:21:28

I have a table of values where each cell has a number, a space, and then another number in parentheses. I'm using xtable to render this table in the document. I'd like the numbers to be justified on the left parenthesis (or on the space). I've used the latex dcolumn package to create a command to justify on the left parenthesis. However, that changes other aspects of how the table is formatted and I'd like to prevent that from happening.

我有一个值表,每个单元格都有一个数字,一个空格,然后是另一个数字。我正在使用xtable在文档中呈现这个表。我希望这些数字在左括号(或者空格)上对齐。我使用了latex dcolumn包来创建一个命令,以便在左括号中进行对齐。但是,这改变了表的格式的其他方面,我想防止这种情况发生。

I know just enough latex to be dangerous and am not sure of the next step. Below is a reproducible example showing what the table looks like now and explaining how I'd actually like it to look. I'd like to figure out how to get the formatting I want programmatically, within the rmarkdown document, so that I don't have to hack the latex afterward. Also, I'm not wedded to this particular method of justifying the table values, so please feel free to suggest another approach if I'm on the wrong track.

我知道只有足够多的乳胶是危险的,我不确定下一步该怎么做。下面是一个可复制的示例,展示了这个表现在的样子,并解释了我希望它看起来如何。我想弄清楚如何在rmarkdown文档中以编程的方式获取我想要的格式,这样之后我就不必修改乳胶了。此外,我并不拘泥于证明表值的这种特殊方法,因此,如果我走错了方向,请随意提出另一种方法。

Since this question focuses on using latex in the context of r, knitr and rmarkdown, I thought it would be better to ask it here, but please let me know if I should move it to the Tex Stack Exchange site instead.

由于这个问题的重点是在r、knitr和rmarkdown的上下文中使用latex,所以我认为最好在这里提出这个问题,但是请让我知道是否应该将它移到Tex栈交换站点。

header.tex file containing the dcolumn command:

\usepackage{dcolumn}
\newcolumntype{Q}{D{(}{(}{-1}}

rmarkdown document:

---
title: "Test"
date: "July 19, 2016"
output: 
  pdf_document:
    includes:
      in_header: header.tex
    keep_tex: yes
    number_sections: yes
fontsize: 11pt
geometry: margin=1in
graphics: yes
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message=FALSE, warning=FALSE, fig.align="center")
```

```{r}
library(xtable)

# Data frame to create table
tab1 = structure(list(Term = structure(1:5, .Label = c("Fall 2007", 
"Spring 2008", "Fall 2008", "Spring 2009", "Fall 2009", "Spring 2010", 
"Fall 2010", "Spring 2011", "Fall 2011", "Spring 2012", "Fall 2012", 
"Spring 2013", "Fall 2013", "Spring 2014", "Fall 2014", "Spring 2015", 
"Fall 2015", "Spring 2016", "Fall 2016"), class = c("ordered", 
"factor")), `BIO 10` = c("89 (2)", "96 (2)", "77 (1)", "103 (3)", 
"81 (1)"), `BIO 20` = c("194 (5)", "175 (3)", "176 (8)", "168 (3)", 
"170 (4)"), `BIO 30` = c("153 (2)", "154 (14)", "188 (7)", "192 (9)", 
"183 (8)"), `BIO 40` = c("284 (23)", "296 (5)", "267 (17)", "296 (16)", 
"279 (7)"), `BIO 50` = c("88 (1)", "107 (5)", "98 (1)", "109 (7)", 
"93 (5)")), .Names = c("Term", "BIO 10", "BIO 20", "BIO 30", 
"BIO 40", "BIO 50"), row.names = c(NA, 5L), class = "data.frame")
```

```{r results="asis"}
print.xtable(
  xtable(tab1, 
         label="tab:tab1",
         caption = "Default Table"), 
  size="small",
  include.rownames=FALSE, comment=FALSE, caption.placement="top"
)
```

```{r results="asis"}
print.xtable(
  xtable(tab1, 
         label="tab:tab2",
         caption = "Columns aligned at left parenthesis",
         align=c("llQQQQQ")), 
  size="small",
  include.rownames=FALSE, comment=FALSE, caption.placement="top"
)
```

Below is the output of the rmarkdown document. Table 1 is the default table created by xtable. Table 2 uses the dcolumn command in the my_header.tex file. In Table 2, the left-hand number in each cell is right-aligned, which is what I want. However, docolumn has changed the formatting in other ways that I don't want:

下面是rmarkdown文档的输出。表1是xtable创建的默认表。表2在my_header中使用dcolumn命令。特克斯文件。在表2中,每个单元格中的左手号是右对齐的,这就是我想要的。但是,docolumn改变了格式设置,这是我不希望看到的:

  1. Column headers should look like the column headers in Table 1, meaning there should be no italics and there should be a space between BIO and the following number.
  2. 列标题应该类似于表1中的列标题,这意味着不应该有斜体,BIO和下面的数字之间应该有空格。
  3. Column widths should be more like the widths in Table 1.
  4. 列宽应该更像表1中的宽。
  5. In the data columns, there should be a space between the first number and the number in parentheses. For example, "89(2)" should be "89 (2)".
  6. 在数据列中,第一个数字和括号中的数字之间应该有空格。例如,“89(2)”应该是“89(2)”。
  7. If possible, it would be even better to have both numbers separately right-aligned. This means that there could be either one or two spaces between the numbers, depending on whether the number in parentheses has, respectively two digits or one digit.
  8. 如果可能的话,两个数字分别右对齐会更好。这意味着数字之间可以有一个或两个空格,这取决于括号中的数字是两个数字还是一个数字。

knitr/rmarkdown/Latex:如何使用dcolumn自定义合理的xtable列,同时抑制其他dcolumn格式

2 个解决方案

#1


2  

I updated my answer in that sense, that you dont need dcolumn anymore. It is a bit of a mix between using R's regex functionalities and adding primitive LaTeX commands such as{\hskip 0.5em}. The thing is, that you can add these primitives in (as far as I know) any LaTeX environment in order to format your paragraphs and such.

我更新了我的答案,你不再需要dcolumn了。在使用R的regex功能和添加原始的LaTeX命令(比如{\hskip 0.5em})之间有点混合。问题是,您可以在(据我所知)任何乳胶环境中添加这些原语,以便格式化您的段落等。

So using apply we reformat the content of the table cells depending on whether the number in parenthesis has 1 or 2 digits and then add a proper horizontal spacing.

因此,使用apply,我们根据括号中的数字是否为1或2,重新格式化表单元格的内容,然后添加适当的水平间距。

By using sanitize.text.function = identity inside of print.xtable we make sure that these LaTeX commands do not get deleted when the data.frame is processed by xtable.

通过使用sanitize.text。函数=打印内部的标识。我们要确保在xtable处理data.frame时不会删除这些LaTeX命令。

---
title: "Test"
output: 
  pdf_document:
    keep_tex: true
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message=FALSE, warning=FALSE, fig.align="center")
```

```{r}
library(xtable)
namesVec <- c("Term", "BIO 10", "BIO 20", "BIO 30", 
"BIO 40", "BIO 50")
# Data frame to create table
tab1 = structure(list(Term = structure(1:5, .Label = c("Fall 2007", 
"Spring 2008", "Fall 2008", "Spring 2009", "Fall 2009", "Spring 2010", 
"Fall 2010", "Spring 2011", "Fall 2011", "Spring 2012", "Fall 2012", 
"Spring 2013", "Fall 2013", "Spring 2014", "Fall 2014", "Spring 2015", 
"Fall 2015", "Spring 2016", "Fall 2016"), class = c("ordered", 
"factor")), `BIO 10` = c("89 (2)", "96 (2)", "77 (1)", "103 (3)", 
"81 (1)"), `BIO 20` = c("194 (5)", "175 (3)", "176 (8)", "168 (3)", 
"170 (4)"), `BIO 30` = c("153 (2)", "154 (14)", "188 (7)", "192 (9)", 
"183 (8)"), `BIO 40` = c("284 (23)", "296 (5)", "267 (17)", "296 (16)", 
"279 (7)"), `BIO 50` = c("88 (1)", "107 (5)", "98 (1)", "109 (7)", 
"93 (5)")), .Names = paste("\\textnormal{", namesVec, "}"), row.names = c(NA, 5L), class = "data.frame")

tab1 <-apply(tab1, 2, function(x) { 
  tmp <- nchar(gsub(".*\\( ?([0-9]+).*","\\1", x))
  skip <-ifelse(tmp == 1, "{\\\\hskip 1em}(", "{\\\\hskip 0.5em}(")
  ifelse(tmp == 1, gsub(x, pattern = " \\(", replacement = paste("{\\\\hskip 1em}(")),
                   gsub(x, pattern = " \\(", replacement = paste("{\\\\hskip 0.5em}(")))
})
```


```{r results="asis"}
print.xtable(
  xtable(tab1, 
         label="tab:tab2",
         caption = "Columns aligned at left parenthesis",
         align=c("llrrrrr")), 
  size="small",
  include.rownames=FALSE, comment=FALSE, caption.placement="top"
, sanitize.text.function = identity)
```

knitr/rmarkdown/Latex:如何使用dcolumn自定义合理的xtable列,同时抑制其他dcolumn格式

#2


1  

This is a similar approach to Martin;s now edited away answer. Its probably easier (at least for a non-latex speaker like me) to align the numbers, and numbers in parenthesis separately, so split these into separate columns. You can then use \multicolumn to group the columns, and define the headers (see possible to create latex multicolumns in xtable?)

这和马丁的回答是相似的。它可能更容易(至少对于像我这样的非乳胶扬声器来说)对齐数字和括号中的数字,因此将它们分割成不同的列。然后,您可以使用\ multiolumn来对列进行分组,并定义标题(在xtable中可以看到创建乳胶的多色层吗?)

```{r results="asis", echo=FALSE}    

tab1 = structure(list(Term = structure(1:5, .Label = c("Fall 2007", 
"Spring 2008", "Fall 2008", "Spring 2009", "Fall 2009", "Spring 2010", 
"Fall 2010", "Spring 2011", "Fall 2011", "Spring 2012", "Fall 2012", 
"Spring 2013", "Fall 2013", "Spring 2014", "Fall 2014", "Spring 2015", 
"Fall 2015", "Spring 2016", "Fall 2016"), class = c("ordered", 
"factor")), `BIO 10` = c("89 (2)", "96 (2)", "77 (1)", "103 (3)", 
"81 (1)"), `BIO 20` = c("194 (5)", "175 (3)", "176 (8)", "168 (3)", 
"170 (4)"), `BIO 30` = c("153 (2)", "154 (14)", "188 (7)", "192 (9)", 
"183 (8)"), `BIO 40` = c("284 (23)", "296 (5)", "267 (17)", "296 (16)", 
"279 (7)"), `BIO 50` = c("88 (1)", "107 (5)", "98 (1)", "109 (7)", 
"93 (5)")), .Names = c("Term", "BIO 10", "BIO 20", "BIO 30", 
"BIO 40", "BIO 50"), row.names = c(NA, 5L), class = "data.frame")

tab2 <- cbind(tab1[1], do.call(cbind.data.frame, lapply(tab1[-1], function(x) 
  do.call(rbind, strsplit(as.character(x), " ")))))

addtorow <- list(list(0), paste(names(tab1)[1], paste0('& \\multicolumn{2}{l}{', names(tab1)[-1], '}', collapse=''), '\\\\'))

library(xtable)

print.xtable(
  xtable(tab2,
         align=c("l","l", rep(c("r@{\\hskip 0in}", "r"),5))),
  include.rownames=FALSE, ,
  add.to.row=addtorow, include.colnames=FALSE)

```

knitr/rmarkdown/Latex:如何使用dcolumn自定义合理的xtable列,同时抑制其他dcolumn格式

#1


2  

I updated my answer in that sense, that you dont need dcolumn anymore. It is a bit of a mix between using R's regex functionalities and adding primitive LaTeX commands such as{\hskip 0.5em}. The thing is, that you can add these primitives in (as far as I know) any LaTeX environment in order to format your paragraphs and such.

我更新了我的答案,你不再需要dcolumn了。在使用R的regex功能和添加原始的LaTeX命令(比如{\hskip 0.5em})之间有点混合。问题是,您可以在(据我所知)任何乳胶环境中添加这些原语,以便格式化您的段落等。

So using apply we reformat the content of the table cells depending on whether the number in parenthesis has 1 or 2 digits and then add a proper horizontal spacing.

因此,使用apply,我们根据括号中的数字是否为1或2,重新格式化表单元格的内容,然后添加适当的水平间距。

By using sanitize.text.function = identity inside of print.xtable we make sure that these LaTeX commands do not get deleted when the data.frame is processed by xtable.

通过使用sanitize.text。函数=打印内部的标识。我们要确保在xtable处理data.frame时不会删除这些LaTeX命令。

---
title: "Test"
output: 
  pdf_document:
    keep_tex: true
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message=FALSE, warning=FALSE, fig.align="center")
```

```{r}
library(xtable)
namesVec <- c("Term", "BIO 10", "BIO 20", "BIO 30", 
"BIO 40", "BIO 50")
# Data frame to create table
tab1 = structure(list(Term = structure(1:5, .Label = c("Fall 2007", 
"Spring 2008", "Fall 2008", "Spring 2009", "Fall 2009", "Spring 2010", 
"Fall 2010", "Spring 2011", "Fall 2011", "Spring 2012", "Fall 2012", 
"Spring 2013", "Fall 2013", "Spring 2014", "Fall 2014", "Spring 2015", 
"Fall 2015", "Spring 2016", "Fall 2016"), class = c("ordered", 
"factor")), `BIO 10` = c("89 (2)", "96 (2)", "77 (1)", "103 (3)", 
"81 (1)"), `BIO 20` = c("194 (5)", "175 (3)", "176 (8)", "168 (3)", 
"170 (4)"), `BIO 30` = c("153 (2)", "154 (14)", "188 (7)", "192 (9)", 
"183 (8)"), `BIO 40` = c("284 (23)", "296 (5)", "267 (17)", "296 (16)", 
"279 (7)"), `BIO 50` = c("88 (1)", "107 (5)", "98 (1)", "109 (7)", 
"93 (5)")), .Names = paste("\\textnormal{", namesVec, "}"), row.names = c(NA, 5L), class = "data.frame")

tab1 <-apply(tab1, 2, function(x) { 
  tmp <- nchar(gsub(".*\\( ?([0-9]+).*","\\1", x))
  skip <-ifelse(tmp == 1, "{\\\\hskip 1em}(", "{\\\\hskip 0.5em}(")
  ifelse(tmp == 1, gsub(x, pattern = " \\(", replacement = paste("{\\\\hskip 1em}(")),
                   gsub(x, pattern = " \\(", replacement = paste("{\\\\hskip 0.5em}(")))
})
```


```{r results="asis"}
print.xtable(
  xtable(tab1, 
         label="tab:tab2",
         caption = "Columns aligned at left parenthesis",
         align=c("llrrrrr")), 
  size="small",
  include.rownames=FALSE, comment=FALSE, caption.placement="top"
, sanitize.text.function = identity)
```

knitr/rmarkdown/Latex:如何使用dcolumn自定义合理的xtable列,同时抑制其他dcolumn格式

#2


1  

This is a similar approach to Martin;s now edited away answer. Its probably easier (at least for a non-latex speaker like me) to align the numbers, and numbers in parenthesis separately, so split these into separate columns. You can then use \multicolumn to group the columns, and define the headers (see possible to create latex multicolumns in xtable?)

这和马丁的回答是相似的。它可能更容易(至少对于像我这样的非乳胶扬声器来说)对齐数字和括号中的数字,因此将它们分割成不同的列。然后,您可以使用\ multiolumn来对列进行分组,并定义标题(在xtable中可以看到创建乳胶的多色层吗?)

```{r results="asis", echo=FALSE}    

tab1 = structure(list(Term = structure(1:5, .Label = c("Fall 2007", 
"Spring 2008", "Fall 2008", "Spring 2009", "Fall 2009", "Spring 2010", 
"Fall 2010", "Spring 2011", "Fall 2011", "Spring 2012", "Fall 2012", 
"Spring 2013", "Fall 2013", "Spring 2014", "Fall 2014", "Spring 2015", 
"Fall 2015", "Spring 2016", "Fall 2016"), class = c("ordered", 
"factor")), `BIO 10` = c("89 (2)", "96 (2)", "77 (1)", "103 (3)", 
"81 (1)"), `BIO 20` = c("194 (5)", "175 (3)", "176 (8)", "168 (3)", 
"170 (4)"), `BIO 30` = c("153 (2)", "154 (14)", "188 (7)", "192 (9)", 
"183 (8)"), `BIO 40` = c("284 (23)", "296 (5)", "267 (17)", "296 (16)", 
"279 (7)"), `BIO 50` = c("88 (1)", "107 (5)", "98 (1)", "109 (7)", 
"93 (5)")), .Names = c("Term", "BIO 10", "BIO 20", "BIO 30", 
"BIO 40", "BIO 50"), row.names = c(NA, 5L), class = "data.frame")

tab2 <- cbind(tab1[1], do.call(cbind.data.frame, lapply(tab1[-1], function(x) 
  do.call(rbind, strsplit(as.character(x), " ")))))

addtorow <- list(list(0), paste(names(tab1)[1], paste0('& \\multicolumn{2}{l}{', names(tab1)[-1], '}', collapse=''), '\\\\'))

library(xtable)

print.xtable(
  xtable(tab2,
         align=c("l","l", rep(c("r@{\\hskip 0in}", "r"),5))),
  include.rownames=FALSE, ,
  add.to.row=addtorow, include.colnames=FALSE)

```

knitr/rmarkdown/Latex:如何使用dcolumn自定义合理的xtable列,同时抑制其他dcolumn格式