【问题标题】:"subset" and "[" on dataframe give slightly different results, why?数据帧上的“子集”和“[”给出的结果略有不同,为什么?
【发布时间】:2014-12-15 14:50:12
【问题描述】:

有人能解释一下为什么我在下面的最后两行代码(identical() 调用)中得到不同的结果吗? 这两个对象似乎是相同的对象,但是当我在应用函数中使用它们时,我遇到了一些麻烦:

df <- data.frame(a = 1:5, b = 6:2, c = rep(7,5))
df_ab <- df[,c(1,2)]
df_AB <- subset(df, select = c(1,2))
identical(df_ab,df_AB)
[1] TRUE

apply(df_ab,2,function(x) identical(1:5,x))
    a     b 
TRUE FALSE

apply(df_AB,2,function(x) identical(1:5,x))
    a     b 
FALSE FALSE

【问题讨论】:

    标签: r dataframe matrix subset rowname


    【解决方案1】:

    apply() 函数在对每一列调用函数之前将其第一个参数强制转换为矩阵。因此,您的数据框被强制转换为矩阵对象。该转换的结果是 as.matrix(df_AB) 具有非空行名,而 as.matrix(df_ab) 没有:

    > str(as.matrix(df_ab))
     int [1:5, 1:2] 1 2 3 4 5 6 5 4 3 2
     - attr(*, "dimnames")=List of 2
      ..$ : NULL
      ..$ : chr [1:2] "a" "b"
    > str(as.matrix(df_AB))
     int [1:5, 1:2] 1 2 3 4 5 6 5 4 3 2
     - attr(*, "dimnames")=List of 2
      ..$ : chr [1:5] "1" "2" "3" "4" ...
      ..$ : chr [1:2] "a" "b"
    

    因此,当您 apply() 子集 df_AB 的一列时,您会得到一个命名向量,它与未命名向量不同。

    apply(df_AB, 2, str)
     Named int [1:5] 1 2 3 4 5
     - attr(*, "names")= chr [1:5] "1" "2" "3" "4" ...
     Named int [1:5] 6 5 4 3 2
     - attr(*, "names")= chr [1:5] "1" "2" "3" "4" ...
    NULL
    

    subset() 函数对比,该函数使用i 值的逻辑向量选择行。看起来像子集一个带有i 非缺失值的data.frame 会导致row.names 属性出现这种差异:

    > str(as.matrix(df[1:5, 1:2]))
     int [1:5, 1:2] 1 2 3 4 5 6 5 4 3 2
     - attr(*, "dimnames")=List of 2
      ..$ : chr [1:5] "1" "2" "3" "4" ...
      ..$ : chr [1:2] "a" "b"
    > str(as.matrix(df[, 1:2]))
     int [1:5, 1:2] 1 2 3 4 5 6 5 4 3 2
     - attr(*, "dimnames")=List of 2
      ..$ : NULL
      ..$ : chr [1:2] "a" "b"
    

    您可以使用 .Internal(inspect(x)) 函数查看 data.frames 之间差异的所有血腥细节。有兴趣的可以自己看看。

    正如 Roland 在他的 cmets 中指出的那样,您可以使用 .row_names_info() 函数仅查看行名称的差异。

    请注意,当i 缺失时,.row_names_info() 的结果为负数,但如果您使用非缺失的i 作为子集,则结果为正数。

    > .row_names_info(df_ab, type=1)
    [1] -5
    > .row_names_info(df_AB, type=1)
    [1] 5
    

    ?.row_names_info 中解释了这些值的含义:

    type: integer.  Currently ‘type = 0’ returns the internal
          ‘"row.names"’ attribute (possibly ‘NULL’), ‘type = 2’ the
          number of rows implied by the attribute, and ‘type = 1’ the
          latter with a negative sign for ‘automatic’ row names.
    

    【讨论】:

    • 原因是[ 创建了“自动”行名(请参阅.row_names_info(df_ab, type=1)subset 创建显式行名(请参阅.row_names_info(df_AB, type=1))。as.matrix 只是传播这个(矩阵不t 必须有行名)。
    • +1。看来identical(df_ab, df_AB)应该返回false?
    • @SeñorO -- identical(df_AB, df_ab, attrib.as.set=FALSE) 确实返回 FALSE。
    • row.names 的值不同,但其方式不应由as.matrix 自动命名。命名的不是subset
    • 查看我对 BondedDust 答案的评论,了解更多关于 as.matrix.data.frame 中两个对象的处理发生分歧的位置(即 df_ab 的 row.names 为 NULL 的位置)。
    【解决方案2】:

    如果要将值1:5 与列中的值进行比较,则不应使用apply,因为apply 在应用函数之前会将数据帧转换为矩阵。由于使用[ 创建的子集中的行名称(请参阅@Joshua Ulrich 的回答),1:5 的值与包含相同值的命名向量不同。

    您应该改用sapplyidentical 函数应用于列。这避免了将数据帧转换为矩阵:

    > sapply(df_ab, identical, 1:5)
        a     b 
     TRUE FALSE 
    > sapply(df_AB, identical, 1:5)
        a     b 
     TRUE FALSE 
    

    如您所见,在两个数据框中,第一列中的值与1:5 相同。

    【讨论】:

      【解决方案3】:

      在一个版本(使用[)中,您的列是整数,而在另一个版本(使用subset)中,您的列被命名为整数。

      apply(df_ab, 2, str)
      
       int [1:5] 1 2 3 4 5
       int [1:5] 6 5 4 3 2
      NULL
      
      
      apply(df_AB, 2, str)
      
       Named int [1:5] 1 2 3 4 5
       - attr(*, "names")= chr [1:5] "1" "2" "3" "4" ...
       Named int [1:5] 6 5 4 3 2
       - attr(*, "names")= chr [1:5] "1" "2" "3" "4" ...
      NULL
      

      【讨论】:

      • 这并不完全正确。如果您使用applyas.matrix 会产生这种差异。见lapply(df_AB, str)
      【解决方案4】:

      查看这两个对象的结构 之前 他们被提交到 apply 只显示一个区别:在行名中,但不是我期望产生差异的差异正在看。我不认为 Joshua 目前提供的“子集”作为解释这一点的逻辑索引。为什么row.names = c(NA, -5L)) 在使用“[”进行提取时会产生命名结果,目前尚无法解释。

      > dput(df_AB)
      structure(list(a = 1:5, b = c(6L, 5L, 4L, 3L, 2L)), .Names = c("a", 
      "b"), row.names = c(NA, 5L), class = "data.frame")
      > dput(df_ab)
      structure(list(a = 1:5, b = c(6L, 5L, 4L, 3L, 2L)), .Names = c("a", 
      "b"), class = "data.frame", row.names = c(NA, -5L))
      

      我同意需要进一步调查的是 as.matrix 强制:

      > attributes(df_AB[,1])
      NULL
      > attributes(df_ab[,1])
      NULL
      > attributes(as.matrix(df_AB)[,1])
      $names
      [1] "1" "2" "3" "4" "5"
      

      【讨论】:

      • 问题出现在as.matrix.data.frame(),在调用.row_names_info(来自namespace:base)的行中。它反过来调用.Internal(shortRowNames()),这为 OP 的两个 data.frame 对象提供了不同的结果。试试.Internal(shortRowNames(df_ab, 1L)).Internal(shortRowNames(df_AB, 1L)) 看看这两个data.frames的转换在哪里分歧...
      • 我不认为这是 as.matrix.data.frame 的问题。它不应该确定 [.data.frame 是否导致行名是显式的而不是隐式的。
      • 这提供了 5 和 -5,但它没有解释为什么没有行名的 data.frame 被命名。这两个对象都有我所期望的被视为“自动”行名。
      • @BondedDust:我将c(NA, -5L) 解释为“完全隐式”,将c(NA, 5L) 解释为“显式、标准1:nrow(x)”。
      • 这是文档中支持的区别吗?这是我第一次看到这样的区别。
      猜你喜欢
      • 2022-01-05
      • 2017-04-29
      • 2021-12-07
      • 2020-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-29
      相关资源
      最近更新 更多