我们可以在R中得到因子矩阵吗?

iag*_*ito 17 integer r vector matrix r-factor

似乎不可能在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?
Run Code Online (Sandbox Code Playgroud)

Rei*_*son 20

在这种情况下,它可能像鸭子一样走路,甚至像鸭子一样嘎嘎叫,但f来自:

f <- factor(sample(letters[1:5], 20, rep=TRUE), letters[1:5])
dim(f) <- c(4,5)
Run Code Online (Sandbox Code Playgroud)

真的不是一个矩阵,即使is.matrix()声称它严格是一个矩阵.就所is.matrix()涉及的矩阵而言,f只需要是一个向量并具有一个dim属性.通过添加属性给f您通过测试.然而,正如您所看到的,一旦您开始使用f矩阵,它很快就会失去使其成为一个因素的功能(您最终会使用级别或维度丢失).

实际上只有原子向量类型的矩阵和数组:

  1. 合乎逻辑的,
  2. 整数,
  3. 真实,
  4. 复杂,
  5. 字符串(或字符),和
  6. 生的

另外,正如@hadley提醒我的那样,你也可以有列表矩阵和数组(通过dim在列表对象上设置属性.例如,参见Hadley的书,Advanced RMatrices&Arrays部分.)

任何超出这些类型的东西都会被强制转换为某种较低类型的东西as.vector().这发生在matrix(f, nrow = 3)不是因为f是原子根据 is.atomic()(它返回TRUEf,因为它在内部存储为一个整数和typeof(f)返回"integer"),但因为它具有class属性.这将设置OBJECT内部表示的位,f并且任何具有类的内容都应该通过以下方式强制转换为其中一种原子类型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)
....
Run Code Online (Sandbox Code Playgroud)

通过添加维度dim<-()是一种快速创建数组而无需复制对象的方法,但这会绕过R f通过其他方法强制转换为矩阵时所做的一些检查和平衡

matrix(f, nrow = 3) # or
as.matrix(f)
Run Code Online (Sandbox Code Playgroud)

当您尝试使用在矩阵上工作的基本函数或使用方法分派时,会发现这一点.请注意,在分配尺寸后f,f仍然是类"factor":

> class(f)
[1] "factor"
Run Code Online (Sandbox Code Playgroud)

这解释了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)
Run Code Online (Sandbox Code Playgroud)

并且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
}
....
Run Code Online (Sandbox Code Playgroud)

cbind()行为可以从(从记录的行为进行解释?cbind,重点煤矿):

功能cbindrbindS3通用,...

....

在默认方法中,所有向量/矩阵必须是原子(参见vector)或列表.表达是不允许的.语言对象(例如公式和调用)和pairlists将被强制转换为列表:其他对象(如名称和外部指针)将作为元素包含在列表结果中. 输入可能具有的任何类都被丢弃(特别是,因子被其内部代码替换).

同样,这其实f是阶级的"factor"是打败你,因为默认的cbind方法被调用,它将剥离水平的信息,当你观察到的返回内部整数代码.

在许多方面,你必须忽略或至少不完全信任is.foo函数告诉你的内容,因为它们只是使用简单的测试来说明某些东西是否是某个foo对象.is.matrix()is.atomic()是显然是错误的,当涉及到f(用尺寸)从一个特定点.它们在实施方面也是正确的,或者至少可以从实施中理解它们的行为; 我认为is.atomic(f)是不正确的,但如果用"如果是一个原子类型的" [R核心意思是"型"是由返回的东西typeof(f),然后is.atomic()是正确的.更严格的测试is.vector(),它f失败:

> is.vector(f)
[1] FALSE
Run Code Online (Sandbox Code Playgroud)

因为它具有超出names属性的属性:

> attributes(f)
$levels
[1] "a" "b" "c" "d" "e"

$class
[1] "factor"

$dim
[1] 4 5
Run Code Online (Sandbox Code Playgroud)

至于你应该如何获得因子矩阵,至少如果你想要它保留因子信息(水平的标签),你就不能.一种解决方案是使用字符矩阵,它将保留标签:

> 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"
Run Code Online (Sandbox Code Playgroud)

并且我们存储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
Run Code Online (Sandbox Code Playgroud)

并且您可以通过以下方式再次返回级别/标签:

> 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"
Run Code Online (Sandbox Code Playgroud)

使用数据框似乎并不理想,因为数据框的每个组件都将被视为一个单独的因素,而您似乎希望将数组视为具有一组级别的单个因子.

如果你真的想做你想要的,有一个因子矩阵,你很可能需要创建自己的S3类来完成这个,加上所有的方法.例如,您可以将因子矩阵存储为字符矩阵但"factorMatrix"存储类,其中您将级别存储在因子矩阵旁边作为额外属性.然后你需要编写[.factorMatrix,这将获取级别,然后使用[矩阵上的默认方法,然后再次添加级别属性.你也可以写cbindhead方法.然而,所需方法的列表会快速增长,但是一个简单的实现可能适合,如果你让你的对象有类c("factorMatrix", "matrix")(即继承自"matrix"类),你将获取该类的所有属性/方法"matrix"(这将删除级别和其他属性)因此您至少可以使用这些对象,并查看您需要添加新方法以填写类的行为.


Bro*_*ieG 6

不幸的是,因子支持在R中并不完全通用,因此许多R函数默认将因子视为其内部存储类型,即integer:

> typeof(factor(letters[1:3]))
[1] "integer  
Run Code Online (Sandbox Code Playgroud)

这是发生的事情matrix,cbind.他们不知道如何处理因子,但他们确实知道如何处理整数,因此他们将你的因子视为整数. head实际上恰恰相反.它确实知道如何处理一个因子,但它从来不会检查你的因子也是一个矩阵,所以只需将它视为一个普通的无量纲因子向量.

你最好的操作就好像你的矩阵有因素一样,就是强迫它去角色.完成操作后,可以将其恢复为因子形式.您也可以使用整数形式执行此操作,但是您可能会遇到奇怪的事情(例如,您可以对整数矩阵进行矩阵乘法,但这对因子没有意义).

请注意,如果您将"矩阵"类添加到您的因子中,则某些(但不是全部)事情开始起作用:

f <- factor(letters[1:9])
dim(f) <- c(3, 3)
class(f) <- c("factor", "matrix")
head(f, 2)
Run Code Online (Sandbox Code Playgroud)

生产:

     [,1] [,2] [,3]
[1,] a    d    g   
[2,] b    e    h   
Levels: a b c d e f g h i
Run Code Online (Sandbox Code Playgroud)

这不能解决rbind等问题.