【发布时间】:2012-12-28 23:21:09
【问题描述】:
我想用\phantom{...} 命令替换格式化数字字符串中的前导空格,其中... 的长度与前导空格的长度相同。我能做的是:
x <- c(1, 1., 0.230, 10.1, 1000, 10000.12)
y <- format(round(x, 2), nsmall=2, big.mark="\\\\,", big.interval=3L)
gsub(" ", "\\\\phantom{ }", y)
但是,我希望有一个适当长度的\phantom{}(例如\phantom{ },然后是几个\phantom{ }。
更新
基于 Arun 的解决方案,我构建了这个函数,用于格式化 R 中的数字以在 LaTeX 表格中对齐:
tabAlign <- function(x, nsmall=0L, digits=NULL,
flag.before="\\\\phantom{", flag.after="}", embrace="$",
big.mark="\\\\,", big.interval=3L, ...)
{
x <- if(!is.null(digits)) round(x, digits=digits) else x
x <- format(x, nsmall=nsmall, big.mark=big.mark, big.interval=big.interval, ...)
x <- sub("^([ ]+)", paste0(flag.before, "\\1", flag.after), x)
paste0(embrace, x, embrace)
}
现在,让我们用它做一些有用的事情。 tabAlign(x) 给:
[1] "$\\phantom{ }1.00$" "$\\phantom{ }1.00$"
[3] "$\\phantom{ }0.23$" "$\\phantom{ }10.10$"
[5] "$\\phantom{ }1\\,000.00$" "$10\\,000.12$"
将其复制并粘贴到 LaTeX 文件显示对齐不正确。原因是big.mark。这会保留nchar(big.mark)=3 空格(在R 字符串中)。但是,在 LaTeX 中,这占用的空间要少得多,因此数字不再完全对齐。理想情况下,sub() 命令因此必须考虑nchar(big.mark)(对于任何给定的big.mark)。
更新 2
这是另一个更新,现在考虑了来自 DWin 的提示。
tabAlign <- function(x, nsmall=0L, digits=NULL,
flag="\\\\phantom{\\1}", embrace="$",
big.mark="\\\\,", big.interval=3L, ...)
{
## round (if digits is not NULL)
x <- if(!is.null(digits)) round(x, digits=digits) else x
## determine those with/without big.mark (idea from prettyNum())
y <- format(x, nsmall=nsmall, trim=TRUE)
y.sp <- strsplit(y, ".", fixed=TRUE)
B <- sapply(y.sp, `[`, 1L)
ind.w.big.mark <- grep(paste0("[0-9]{", big.interval+1L, ",}"), B)
ind.wo.big.mark <- setdiff(1:length(y), ind.w.big.mark)
## format the numbers
x <- format(x, nsmall=nsmall, big.mark=big.mark, big.interval=big.interval, ...)
## substitute spaces
z <- character(l <- length(x))
n <- nchar(big.mark)
for(i in seq_len(l)){
z[i] <- if(i %in% ind.wo.big.mark) sub("^([ ]+)", paste0(flag, big.mark), x[i])
else sub("^([ ]+)", flag, x[i])
}
## embrace
paste0(embrace, z, embrace)
}
唯一缺少的部分是不是替换if() 部分中\phantom 中的所有空格,而是替换空格的数量-n,其中n <- nchar(big.mark)。如何在sub() 中指定?
更新 3
这是一个解决方案(但不太优雅......见下文):
tabAlign <- function(x, nsmall=0L, digits=NULL,
flag="\\\\phantom{\\1}", embrace="$",
big.mark="\\\\,", big.mark2="\\,", big.interval=3L, ...)
{
## round (if digits is not NULL)
x <- if(!is.null(digits)) round(x, digits=digits) else x
## determine those with/without big.mark (idea from prettyNum())
y <- format(x, trim=TRUE)
y.sp <- strsplit(y, ".", fixed=TRUE)
B <- sapply(y.sp, `[`, 1L)
w.big.mark <- grep(paste0("[0-9]{", big.interval+1L, ",}"), B)
wo.big.mark <- setdiff(1:length(y), w.big.mark)
## format the numbers
x. <- if(length(wo.big.mark) > 0 && length(w.big.mark) > 0) {
## format but trim
y <- format(x, trim=TRUE, nsmall=nsmall, big.mark=big.mark, big.interval=big.interval, ...)
## paste big.mark to all numbers without big.mark
y[wo.big.mark] <- paste0(big.mark2, y[wo.big.mark])
format(y, justify="right")
} else { # either all numbers have big.mark or not
format(x, nsmall=nsmall, big.mark=big.mark, big.interval=big.interval, ...)
}
z <- sub("^([ ]+)", flag, x.)
## embrace
paste0(embrace, z, embrace)
}
x <- c(1, 1., 0.230, 10.1, 1000, 10000.12)
tabAlign(x)
tabAlign(x[1:4])
tabAlign(x[5:6])
如果我们只能指定big.mark(而不是big.mark2)会更好。
【问题讨论】:
-
这就是为什么会有
if()... 对于x的那些带有大标记的元素,我们替换所有的空格。对于x的那些没有大标记的元素,至少有三个空格(由于有些元素有大标记,所以我们将big.mark粘贴到这些字符串中) -
我展示的代码是目前我所知道的最好的,但它没有做正确的事情(在 R 和 LaTeX 中都没有)。问题是对于那些没有大标记的 x 元素,
\phantom{ }引入了太多空间(即n空间太多,其中n是big.mark中的字符数)。 -
我现在(更新 3)做得更优雅。但是,如果我们可以省略参数
big.mark2,那就太好了。