为啥 R 不能在一组空索引上正确删除元素?
Posted
技术标签:
【中文标题】为啥 R 不能在一组空索引上正确删除元素?【英文标题】:Why does R not remove elements properly over an empty set of indices?为什么 R 不能在一组空索引上正确删除元素? 【发布时间】:2022-01-05 10:07:40 【问题描述】:我在R
中遇到了一些奇怪的行为。假设我有一个矩阵,我想删除一组指定的行和列。这是一个完美运行的示例。
#Create a matrix
MATRIX <- matrix(1:50, nrow = 4, ncol = 5)
rownames(MATRIX) <- c('a', 'b', 'c', 'd')
colnames(MATRIX) <- c('a', 'b', 'c', 'd', 'e')
#Specify rows and columns to remove
REMOVE.ROW <- 3
REMOVE.COL <- 2
#Print the matrix without these rows or columns
MATRIX[-REMOVE.ROW, -REMOVE.COL]
a c d e
a 1 9 13 17
b 2 10 14 18
d 4 12 16 20
但是,当 REMOVE.ROW
或 REMOVE.COL
中的一个或两个对象为空时,它不会删除任何内容(因此返回原始矩阵),而是返回一个空矩阵.
#Specify rows and columns to remove
REMOVE.ROW <- integer(0)
REMOVE.COL <- integer(0)
#Print the matrix without these rows or columns
MATRIX[-REMOVE.ROW, -REMOVE.COL]
<0 x 0 matrix>
直觉上,我本以为删除一组空索引会留下原始索引集,因此我本以为从该命令返回完整矩阵。出于某种原因,R
在这种情况下从矩阵中删除了所有行和列。据我所知,这似乎是 R
中的一个错误,但也许有一些我不知道的充分理由。
问题:有人能解释一下为什么R
会这样吗?除了使用 if-then 语句来处理特殊情况之外,我是否可以进行任何简单的调整以使 R
按我的意愿行事?
【问题讨论】:
不幸的是,负行/列索引具有这个令人讨厌的特性/错误,正如您所展示的那样。这不是新的。我不知道是否已在某个时候编写了错误报告并且被拒绝或仍在等待中,但我会支持“修复”此功能/错误的建议。NotEmpty <- function(z, default = TRUE) if (!length(z)) default else z
然后MATRIX[-REMOVE.ROW, NotEmpty(-REMOVE.COL)]
修复了症状,但几乎没有更容易/更好。所有使用负索引进行删除的代码都必须始终以这种方式进行防御。
-integer(0)
与 integer(0)
相同,因此您没有选择行和列
【参考方案1】:
空对象有一个奇怪的属性,它们不是NULL
,长度为0,但不是子集。一种可能的解决方法是考虑所有可能的组合并使用length(integer0)
等于零的属性。我知道这个解决方案可能并不理想。
is.na(integer(0))
#> logical(0)
is.null(integer(0))
#> [1] FALSE
length(integer(0))
#> [1] 0
integer(0)[[1]]
#> Error in integer(0)[[1]]: subscript out of bounds
integer(0)[[0]]
#> Error in integer(0)[[0]]: attempt to select less than one element in get1index <real>
MATRIX <- matrix(1:50, nrow = 4, ncol = 5)
#> Warning in matrix(1:50, nrow = 4, ncol = 5): data length [50] is not a sub-
#> multiple or multiple of the number of rows [4]
REMOVE.ROW <- integer(0)
REMOVE.COL <- integer(0)
if (all(length(REMOVE.ROW > 0) , length(REMOVE.COL) > 0))
MATRIX[-REMOVE.ROW, -REMOVE.COL]
else
if (length(REMOVE.ROW) > 0 && length(REMOVE.COL) == 0)
MATRIX[-REMOVE.ROW, ]
else
if (length(REMOVE.ROW) == 0 && length(REMOVE.COL) > 0)
MATRIX[, -REMOVE.COL]
else
MATRIX
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 1 5 9 13 17
#> [2,] 2 6 10 14 18
#> [3,] 3 7 11 15 19
#> [4,] 4 8 12 16 20
由reprex package (v2.0.1) 于 2021 年 11 月 27 日创建
【讨论】:
【参考方案2】:问题是R
使用的是算术否定,而不是设置否定
根据有用的评论(IceCreamToucan 的提示),这似乎是因为使用负索引索引矩阵涉及两步过程,这些过程是使用算术否定而不是集合否定构造的。这似乎是操作的标准数学解释与计算解释不同的情况之一。
在对一组索引上的矩阵进行索引的数学解释中,我们将set negation 视为产生了一个由原始“样本空间”中但在否定集之外的元素组成的新集。在R
的计算解释中,负号的应用会产生负算术值,这些值随后被解释为调用矩阵时要删除的元素。
在这种情况下发生了什么:对于我们有一组非空索引的通常情况,使用否定符号只是将索引转换为负值然后当我们调用矩阵时,它会查看除负值之外的所有索引。
#Specify rows and columns to remove
REMOVE.ROW <- 3
REMOVE.COL <- 2
#See negatives of the removed indices
identical(MATRIX[-REMOVE.ROW, -REMOVE.COL], MATRIX[-3, -2])
[1] TRUE
但是,当我们使用空索引向量时,该向量的负值仍然是索引的空向量 --- 即向量 integer(0)
与其负值 -integer(0)
相同。因此,当我们试图移除索引的空向量时,我们实际上是要求在空向量的负数上调用矩阵,这仍然是空向量。
#The empty vector is equivalent to its negative
identical(integer(0), -integer(0))
[1] TRUE
#Therefore, calling over these vectors is equivalent
identical(MATRIX[-integer(0), -integer(0)], MATRIX[integer(0), integer(0)])
[1] TRUE
所以,这里的问题是您将-REMOVE.ROW
和-REMOVE.COL
解释为好像它们使用set negation,而实际上它们只是取值的初始向量并将它们变为负数(即,将它们乘以负数)。
解决问题: 似乎没有标准函数来调用矩阵以使用集合否定来解释索引,因此您需要使用条件逻辑来构造解决方案对于特定情况或自定义功能。这是一个自定义函数sub.matrix
,用于删除特定的行和列,这些行和列在集合否定的意义上被解释。
sub.matrix <- function(x, remove.rows = integer(0), remove.cols = integer(0))
#Check that input x is a matrix
if (!('matrix' %in% class(x)))
stop('This function is only for objects of class \'matrix\'')
#Create output matrix
R <- length(remove.rows)
C <- length(remove.cols)
if ((R > 0)&(C > 0)) OUT <- MATRIX[-remove.rows, -remove.cols]
if ((R == 0)&(C > 0)) OUT <- MATRIX[, -remove.cols]
if ((R > 0)&(C == 0)) OUT <- MATRIX[-remove.rows, ]
if ((R == 0)&(C == 0)) OUT <- MATRIX
#Return the output matrix
OUT
【讨论】:
以上是关于为啥 R 不能在一组空索引上正确删除元素?的主要内容,如果未能解决你的问题,请参考以下文章
JAVA字符串获取索引问题,为啥不能正确返回索引而是返回-1,代码如下