为什么dataframe上的“subset”和“[”给予的结果略有不同?

f0brbegy  于 2023-03-27  发布在  其他
关注(0)|答案(4)|浏览(165)

有人能解释一下为什么我在下面的最后两行代码(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
pw9qyyiw

pw9qyyiw1#

apply()函数在对每一列调用该函数之前将其第一个参数强制转换为矩阵。因此,您的 Dataframe 被强制转换为矩阵对象。该转换的结果是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之间的所有细节差异,如果你感兴趣的话,你可以自己查看。
正如罗兰在他的评论中指出的那样,您可以使用.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.
cs7cruho

cs7cruho2#

如果你想比较1:5和列中的值,你不应该使用apply,因为apply在应用函数之前会将 Dataframe 转换为矩阵。由于使用[创建的子集中的行名称(参见@约书亚Ulrich的答案),值1:5与包含相同值的命名向量不相同。
您应该使用sapplyidentical函数应用于列。这可以避免将 Dataframe 转换为矩阵:

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

如您所见,在两个数据框中,第一列中的值都等于1:5

wwtsj6pe

wwtsj6pe3#

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

snz8szmq4#

在提交给apply之前,看看这两个对象的结构,只有一个区别:我不认为约书亚当前提供的“子集”作为逻辑索引来解释这一点。为什么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"

相关问题