如果在带有字符和数字列的data.frame上使用apply over rows,则apply会在内部使用as.matrix将data.frame转换为仅字符。但如果数字列由不同长度的数字组成,则.matrix会添加空格以匹配最高/“最长”的数字。
一个例子:
df <- data.frame(id1=c(rep("a",3)),id2=c(100,90,8), stringsAsFactors = FALSE)
df
## id1 id2
## 1 a 100
## 2 a 90
## 3 a 8
as.matrix(df)
## id1 id2
## [1,] "a" "100"
## [2,] "a" " 90"
## [3,] "a" " 8"
我原以为结果是:
id1 id2
[1,] "a" "100"
[2,] "a" "90"
[3,] "a" "8"
为什么要多余的空间?
在data.frame上使用apply时,它们可能会产生意外的结果:
myfunc <- function(row){
paste(row[1], row[2], sep = "")
}
> apply(df, 1, myfunc)
[1] "a100" "a 90" "a 8"
>
虽然循环给出了预期的结果。
> for (i in 1:nrow(df)){
print(myfunc(df[i,]))
}
[1] "a100"
[1] "a90"
[1] "a8"
和
> paste(df[,1], df[,2], sep = "")
[1] "a100" "a90" "a8"
是否存在使用as.matrix添加的额外空格有用的情况?
答案 0 :(得分:23)
这是因为在as.matrix.data.frame
方法中转换非数字数据的方式。有一个简单的解决方法,如下所示。
?as.matrix
注意到转换是通过format()
完成的,并且在这里添加了额外的空格。具体而言,?as.matrix
在详细信息部分中包含此内容:
‘as.matrix’ is a generic function. The method for data frames
will return a character matrix if there is only atomic columns and
any non-(numeric/logical/complex) column, applying ‘as.vector’ to
factors and ‘format’ to other non-character columns. Otherwise,
the usual coercion hierarchy (logical < integer < double <
complex) will be used, e.g., all-logical data frames will be
coerced to a logical matrix, mixed logical-integer will give a
integer matrix, etc.
?format
还注意到
字符串用空格填充到最宽的显示宽度。
考虑这个例子来说明行为
> format(df[,2])
[1] "100" " 90" " 8"
> nchar(format(df[,2]))
[1] 3 3 3
format
没有 以这种方式工作,因为它有trim
:
trim: logical; if ‘FALSE’, logical, numeric and complex values are
right-justified to a common width: if ‘TRUE’ the leading
blanks for justification are suppressed.
e.g。
> format(df[,2], trim = TRUE)
[1] "100" "90" "8"
但是无法将此参数传递给as.matrix.data.frame
方法。
解决此问题的一种方法是通过format()
手动应用sapply
。在那里你可以传递trim = TRUE
> sapply(df, format, trim = TRUE)
id1 id2
[1,] "a" "100"
[2,] "a" "90"
[3,] "a" "8"
或者,使用vapply
我们可以说明我们期望返回的内容(此处长度为3 [nrow(df)
]的字符向量):
> vapply(df, format, FUN.VALUE = character(nrow(df)), trim = TRUE)
id1 id2
[1,] "a" "100"
[2,] "a" "90"
[3,] "a" "8"
答案 1 :(得分:9)
看起来有点奇怪。在手册(?as.matrix
)中,它解释了为转换为字符矩阵而调用format
:
如果存在,数据帧的方法将返回字符矩阵 只有原子列和任何非(数字/逻辑/复杂)列, 将as.vector应用于因子,将格式应用于其他非字符 列。
您可以看到,如果您直接致电format
,它会执行as.matrix
所做的事情:
format(df$id2)
[1] "100" " 90" " 8"
您需要做的是通过trim
arugment:
format(df$id2,trim=TRUE)
[1] "100" "90" "8"
但是,遗憾的是,as.matrix.data.frame
功能不允许您这样做。
else if (non.numeric) {
for (j in pseq) {
if (is.character(X[[j]]))
next
xj <- X[[j]]
miss <- is.na(xj)
xj <- if (length(levels(xj)))
as.vector(xj)
else format(xj) # This could have ... as an argument
# else format(xj,...)
is.na(xj) <- miss
X[[j]] <- xj
}
}
因此,您可以修改as.data.frame.matrix
。不过,我认为这将是一个很好的功能添加,将其包含在基础中。
但是,快速解决方案就是:
as.matrix(data.frame(lapply(df,as.character)))
id1 id2
[1,] "a" "100"
[2,] "a" "90"
[3,] "a" "8"
# As mentioned in the comments, this also works:
sapply(df,as.character)
答案 2 :(得分:5)
as.matrix
在内部调用format
:
> format(df$id2)
[1] "100" " 90" " 8"
额外空间的来源。 format
有一个额外的参数trim
来删除它们:
> format(df$id2, trim = TRUE)
[1] "100" "90" "8"
但是,您无法将此参数提供给as.matrix
。
答案 3 :(得分:1)
之前的答案已经解释了这种行为的原因,但我想提供另一种规避这种行为的方法:
df <- data.frame(id1=c(rep("a",3)),id2=c(100,90,8), stringsAsFactors = FALSE)
do.call(cbind,df)
id1 id2
[1,] "a" "100"
[2,] "a" "90"
[3,] "a" "8"
请注意,如果使用stringsAsFactors = TRUE
,则无法将因子级别转换为数字。
答案 4 :(得分:0)
另一种解决方案:如果您不介意下载软件包,trimWhiteSpace(x)(来自limma R pckg)也可以完成这项工作。
source("https://bioconductor.org/biocLite.R")
biocLite("limma")
library(limma)
df <- data.frame(id1=c(rep("a",3)),id2=c(100,90,8), stringsAsFactors = FALSE)
as.matrix(df)
id1 id2
[1,] "a" "100"
[2,] "a" " 90"
[3,] "a" " 8"
trimWhiteSpace(as.matrix(df))
id1 id2 enter code here
[1,] "a" "100"
[2,] "a" "90"
[3,] "a" "8"