似乎不可能获得R中的因子矩阵。这是真的吗?如果是,为什么?如果没有,我该怎么办?
f <- factor(sample(letters[1:5], 20, rep=TRUE), letters[1:5])
m <- matrix(f,4,5)
is.factor(m) # fail.
m <- factor(m,letters[1:5])
is.factor(m) # oh, yes?
is.matrix(m) # nope. fail.
dim(f) <- c(4,5) # aha?
is.factor(f) # yes..
is.matrix(f) # yes!
# but then I get a strange behavior
cbind(f,f) # is not a factor anymore
head(f,2) # doesn't give the first 2 rows but the first 2 elements of f
# should I worry about it?
最佳答案
在这种情况下,它可能像鸭子一样走路,甚至像鸭子一样嘎嘎叫,但是f
来自:
f <- factor(sample(letters[1:5], 20, rep=TRUE), letters[1:5])
dim(f) <- c(4,5)
即使
is.matrix()
声称它严格是一个矩阵,它实际上也不是一个矩阵。要成为就is.matrix()
而言的矩阵,f
仅需为向量并具有dim
属性。通过将属性添加到f
,您可以通过测试。如您所见,但是,一旦开始使用f
作为矩阵,它就会很快失去使其成为一个因素的功能(最终使用级别或尺寸会丢失)。实际上,原子矢量类型只有矩阵和数组:
逻辑上
整数,
真实,
复杂,
字符串(或字符),以及
生的
另外,正如@hadley提醒我的那样,您还可以拥有列表矩阵和数组(通过在列表对象上设置
dim
属性。例如,参见Hadley的书《 Advanced R》的Matrices & Arrays部分)。这些类型以外的任何内容都可以通过
as.vector()
强制转换为较低的类型。这发生在matrix(f, nrow = 3)
中,不是因为f
根据is.atomic()
是原子的(返回TRUE
为f
,因为它在内部存储为整数,并且typeof(f)
返回"integer"
),而是因为它具有class
属性。这会将OBJECT
的内部表示形式的f
位置1,并且具有类的任何东西都应该通过as.vector()
强制为一种原子类型:matrix <- function(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
dimnames = NULL) {
if (is.object(data) || !is.atomic(data))
data <- as.vector(data)
....
通过
dim<-()
添加维度是创建数组而不复制对象的快速方法,但是如果您通过其他方法将f
强制转换为矩阵,这将绕过R所做的一些检查和平衡matrix(f, nrow = 3) # or
as.matrix(f)
当您尝试使用对矩阵起作用的基本函数或使用方法分派时,就会发现这一点。请注意,在为
f
分配尺寸后,f
仍然属于"factor"
类:> class(f)
[1] "factor"
解释
head()
行为;您没有得到head.matrix
行为,因为f
不是矩阵,至少就S3机制而言:> debug(head.matrix)
> head(f) # we don't enter the debugger
[1] d c a d b d
Levels: a b c d e
> undebug(head.matrix)
而
head.default
方法调用具有[
方法的factor
,因此观察到的行为:> debugonce(`[.factor`)
> head(f)
debugging in: `[.factor`(x, seq_len(n))
debug: {
y <- NextMethod("[")
attr(y, "contrasts") <- attr(x, "contrasts")
attr(y, "levels") <- attr(x, "levels")
class(y) <- oldClass(x)
lev <- levels(x)
if (drop)
factor(y, exclude = if (anyNA(levels(x)))
NULL
else NA)
else y
}
....
cbind()
行为可以从记录的行为中解释(来自?cbind
,重点是我的):函数
cbind
和rbind
是S3通用的.......
在默认方法中,所有向量/矩阵都必须是原子的
(请参见
vector
)或列表。不允许使用表达式。语言对象(例如公式和调用)和配对列表将被强制
到列表:其他对象(例如名称和外部指针)将
作为元素包含在列表结果中。任何类别的输入
可能已被丢弃(尤其是,因素已替换为
其内部代码)。
同样,
f
是"factor"
类的事实使您不胜其烦,因为将调用默认的cbind
方法,它将剥离级别信息并返回您观察到的内部整数代码。在许多方面,您必须忽略或至少不完全相信
is.foo
函数告诉您的内容,因为它们只是使用简单的测试来说明某事物是否为foo
对象。从特定的角度来看,is.matrix()
和is.atomic()
在f
(带有尺寸)方面显然是错误的。它们在实现方面也是正确的,或者至少可以从实现中理解其行为;我认为is.atomic(f)
是不正确的,但是如果“如果是原子类型” R Core的意思是“类型”是typeof(f)
返回的东西,那么is.atomic()
是正确的。一个更严格的测试是is.vector()
,该测试f
失败:> is.vector(f)
[1] FALSE
因为它的属性超出了
names
属性:> attributes(f)
$levels
[1] "a" "b" "c" "d" "e"
$class
[1] "factor"
$dim
[1] 4 5
至于如何获得因子矩阵,至少在想要保留因子信息(水平的标签)的情况下,是无法做到的。一种解决方案是使用字符矩阵,该矩阵将保留标签:
> fl <- levels(f)
> fm <- matrix(f, ncol = 5)
> fm
[,1] [,2] [,3] [,4] [,5]
[1,] "c" "a" "a" "c" "b"
[2,] "d" "b" "d" "b" "a"
[3,] "e" "e" "e" "c" "e"
[4,] "a" "b" "b" "a" "e"
并且我们存储
f
的级别以备将来使用,以防我们在此过程中丢失矩阵的某些元素。或使用内部整数表示形式:
> (fm2 <- matrix(unclass(f), ncol = 5))
[,1] [,2] [,3] [,4] [,5]
[1,] 3 1 1 3 2
[2,] 4 2 4 2 1
[3,] 5 5 5 3 5
[4,] 1 2 2 1 5
并且您随时可以通过以下方式再次回到级别/标签:
> fm2[] <- fl[fm2]
> fm2
[,1] [,2] [,3] [,4] [,5]
[1,] "c" "a" "a" "c" "b"
[2,] "d" "b" "d" "b" "a"
[3,] "e" "e" "e" "c" "e"
[4,] "a" "b" "b" "a" "e"
使用数据框似乎并不理想,因为数据框的每个组件都将被视为一个单独的因素,而您似乎想将数组视为具有一组级别的单个因素。
如果您真的想做自己想做的事情,那就是拥有一个因子矩阵,那么您很可能需要创建自己的S3类来执行此操作,以及与此相关的所有方法。例如,您可以将因子矩阵存储为字符矩阵,但使用类
"factorMatrix"
,其中将级别与因子矩阵一起存储为额外的属性说。然后,您将需要编写[.factorMatrix
,它将获取级别,然后在矩阵上使用默认的[
方法,然后再次添加level属性。您也可以编写cbind
和head
方法。但是,所需方法的列表将迅速增加,但是可能会采用一种简单的实现,并且如果您使对象具有类c("factorMatrix", "matrix")
(即从"matrix"
类继承),则将选择该类的所有属性/方法。 "matrix"
类(将删除级别和其他属性),因此您至少可以使用对象并查看需要在何处添加新方法来填充类的行为。
关于r - 我们可以得到R中的因子矩阵吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28723059/