代码之家  ›  专栏  ›  技术社区  ›  iago-lito

我们能得到R中的因子矩阵吗?

  •  18
  • iago-lito  · 技术社区  · 9 年前

    似乎不可能得到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?
    
    2 回复  |  直到 6 年前
        1
  •  24
  •   Gavin Simpson    9 年前

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

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

    虽然真的不是矩阵 is.matrix() 声称它严格来说是一个。尽可能成为矩阵 是.matrix() 关注, f 只需要是一个向量 dim 属性通过将属性添加到 f 你通过了考试。然而,正如您所看到的,一旦您开始使用 f 作为一个矩阵,它很快就失去了使它成为一个因素的特性(你最终会处理级别或维度)。

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

    1. 必然的
    2. 整数
    3. 真实的
    4. 复杂的
    5. 字符串(或字符),以及
    6. 未经加工的

    此外,正如@hadley提醒我的,您还可以使用列表矩阵和数组(通过设置 暗淡的 属性。例如,请参见 Matrices & Arrays 哈德利书中的一节, 高级R .)

    这些类型之外的任何内容都将通过 as.vector() 。这发生在 matrix(f, nrow = 3) 不是因为 f 是原子的 根据 is.atomic() (返回 TRUE 对于 f 因为它在内部存储为整数 typeof(f) 回报 "integer" )但因为它有 class 属性这将设置 OBJECT 内部表示的位 f 任何具有类的对象都应该通过 作为.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<-() 是在不复制对象的情况下创建数组的快速方法,但这绕过了R在强制执行 f 通过其他方法连接到矩阵

    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 是一流的 “因子” 因为默认值 cbind公司 方法将被调用,它将剥离级别信息并返回您观察到的内部整数代码。

    在许多方面,你必须忽略或至少不完全信任 is.foo 函数告诉你,因为它们只是使用简单的测试来判断某个东西是否是 foo 对象 是.matrix() 是.atomic() 显然是错误的 f (带尺寸) 从特定的角度来看 他们在执行方面也是正确的,或者至少可以从执行中理解他们的行为;我想 is.atomic(f) 不正确,但如果 “如果是原子类型” R核心的意思是“类型” 类型(f) 然后 是.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 ,这将获取级别,然后使用默认值 [ 方法,然后再次添加levels属性。你可以写 cbind公司 head 方法。然而,所需方法的列表将快速增长,但如果您使对象具有类 c("factorMatrix", "matrix") (即继承自 "matrix" 类),您将获取 “矩阵” 类(这将删除级别和其他属性),这样您至少可以处理对象,并查看需要在哪里添加新方法来填充类的行为。

        2
  •  7
  •   BrodieG    9 年前

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

    > typeof(factor(letters[1:3]))
    [1] "integer  
    

    这就是发生在 matrix , cbind 。他们不知道如何处理因子,但他们知道如何处理整数,因此他们将因子视为整数。 head 实际上是相反的。它确实知道如何处理一个因子,但它从不介意检查你的因子是否也是一个矩阵,所以只是把它当作一个正常的无量纲因子向量。

    如果你的矩阵中有一些因素,那么你最好的办法就是强迫它成为角色。一旦你完成了你的运算,你就可以把它恢复成因子形式。你也可以用整数形式来做,但是你会冒一些奇怪的风险(例如,你可以在整数矩阵上做矩阵乘法,但这对因子来说毫无意义)。

    请注意,如果您将类“矩阵”添加到因子中,一些(但不是所有)事情就会开始工作:

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

    生产:

         [,1] [,2] [,3]
    [1,] a    d    g   
    [2,] b    e    h   
    Levels: a b c d e f g h i
    

    这不能解决问题 rbind