"子集"和" ["在数据帧上给出略有不同的结果,为什么?

时间:2014-10-20 14:58:41

标签: r dataframe matrix subset rowname

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

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

4 个答案:

答案 0 :(得分:13)

apply()函数在调用每列上的函数之前强制将其第一个参数强制转换为矩阵。因此,您的数据框被强制转换为矩阵对象。转换的结果是as.matrix(df_AB)具有非空的rownames,而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在评论中指出的那样,您可以使用.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.

答案 1 :(得分:8)

如果要将值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相同。

答案 2 :(得分:5)

在一个版本中(使用[),您的列是整数,而在另一个版本中(使用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

答案 3 :(得分:3)

在之前查看这两个对象的结构,它们被提交到apply只显示了一个区别:在rownames中,但没有我预期产生的差异你看到的差异。我没有看到约书亚目前提供的&#39;子集&#39;作为解释这个的逻辑索引。为什么row.names = c(NA, -5L))在使用&#34; [&#34;尚未解释。

> 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"