eipi10 eipi10 - 4 months ago 74
LaTeX Question

knitr/rmarkdown/Latex: How to custom justify xtable columns using dcolumn, while suppressing other dcolumn formatting

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.

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.

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.

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:


  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. Column widths should be more like the widths in Table 1.

  3. 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)".

  4. 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.



enter image description here

Answer

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.

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.

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.

---
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)
```

enter image description here

Comments