使用整数矩阵对数据框进行子集操作

10

我经常遇到这个问题,想知道是否有一个简单的解决方法。有些情况下,我发现在子集矩阵中进行思考更加合乎逻辑。

N <- 12
N.NA <- 6
dat <- data.frame(V1=runif(N),V2=runif(N))
sel.mat <- matrix(c(sample(seq(N),N.NA),sample(ncol(dat),N.NA,replace=TRUE)),ncol=2)

这对选择操作有效,但不适用于替换操作:

> dat[sel.mat]
[1] 0.2582569 0.8455966 0.8828083 0.5384263 0.9574810 0.5623158
> dat[sel.mat] <- NA
Error in `[<-.data.frame`(`*tmp*`, sel.mat, value = NA) : 
  only logical matrix subscripts are allowed in replacement

我知道错误信息的原因(如果您有多个替换指向同一个元素,它将不知道该怎么做),但这并不能阻止 R 允许对向量进行整数替换(例如:dat$V1[c(2,3)] <- NA)。

是否有一种方便的方法可以允许按整数矩阵进行替换?


1
在当前的R-devel快照中,dat[sel.mat] <- NA是被支持的。因此,从R-3.0.0开始将可用矩阵索引替换。(有关更多详细信息,请参见下面的答案。) - Josh O'Brien
5个回答

7

把它转换为矩阵:

dat.m <- as.matrix(dat)
dat.m[sel.mat] <- NA
> dat.m
             V1         V2
 [1,] 0.2539189         NA
 [2,] 0.5216975         NA
 [3,] 0.1206138 0.14714848
 [4,] 0.2841779 0.52352209
 [5,] 0.3965337         NA
 [6,] 0.1871074 0.23747235
 [7,] 0.2991774         NA
 [8,]        NA 0.09509202
 [9,] 0.4636460 0.59384430
[10,] 0.5493738 0.92334630
[11,] 0.7160894         NA
[12,] 0.9568567 0.80398264

编辑解释为什么我们在使用data.frame时出现错误

dat.m[sel.mat] <- NA

等同于执行以下操作:
temp <- dat
dat <- "[<-"(temp, sel.mat, value=NA)

 Error in `[<-.data.frame`(temp, sel.mat, value = NA) : 
 only logical matrix subscripts are allowed in replacement

现在我可以做以下操作并且它可以正常工作:

dat <- "[<-"(as.matrix(temp), sel.mat, value=NA)

6

您可以根据整数矩阵创建逻辑矩阵:

log.mat <- matrix(FALSE, nrow(dat), ncol(dat))
log.mat[sel.mat] <- TRUE

这个矩阵可用于将数据框中的值替换为NA(或其他值):

is.na(dat) <- log.mat

结果如下:
           V1         V2
1  0.76063534         NA
2  0.27713051 0.10593451
3  0.74301263 0.77689458
4  0.42202155         NA
5  0.54563816 0.10233017
6          NA 0.05818723
7  0.83531963 0.93805113
8  0.99316128 0.61505393
9  0.08743757         NA
10 0.95510231 0.51267338
11 0.14035257         NA
12 0.59408022         NA

这样可以让你将原始对象保留为数据框,允许不同类型的列存在。

2
在R语言中,表达式如下:
dat[sel.mat]
dat[sel.mat] <- NA

S3方法与等效于

`[.data.frame`(x=dat, i=sel.mat)
`[<-.data.frame`(x=dat, i=sel.mat, value=NA)

因为class(dat)是"data.frame",所以你可以查看源代码:

`[.data.farme`
`[<-.data.frame`

并将其修改为您想要的样子。


在您的情况下,可能您想要:

`[<-.data.frame` <- function(x, i, j, value) {
  if (class(i) != "matrix") return(base:::`[<-.data.frame`(x, i, j, value))
  if (class(i[1]) != "integer") return(base:::`[<-.data.frame`(x, i, j, value))
  # check the length of i and value here
  if (length(value) < nrow(i)) {
    if (nrow(i) %% length(value) != 0) warning("some warning message should be here")
    value <- rep(value, nrow(i) %/% length(value) + 1)
  }
  value <- value[1:nrow(i)]
  for(index in 1:nrow(i)) {
    x[i[index,1], i[index,2]] <- value[index]
  }
  return(x)
}

试一下:

N <- 12
N.NA <- 6
dat <- data.frame(V1=runif(N),V2=runif(N))
sel.mat <- matrix(c(sample(seq(N),N.NA),sample(ncol(dat),N.NA,replace=TRUE)),ncol=2)
dat[sel.mat] <- NA
dat

不确定为什么这个帖子没有得到更多的赞,除了通常最好避免重写基本函数以避免混淆。但这是一个很好的解释。 - Ari B. Friedman

2

值得一提的是,在当前的R-devel快照中,使用替换的矩阵索引确实有效(并将成为R-3.0.0的一部分)。显然,R-core中有人有与您相同的愿望。

正如R-devel NEWS文件中所记录的那样:

现在支持通过两列数字索引来进行数据框的矩阵索引,并且支持替换和提取操作。

以下是一个演示:

dat[sel.mat]
## [1] 0.3355509 0.4114056 0.2334332 0.6597042 0.7707762 0.7783584
dat[sel.mat] <- NA
dat[sel.mat]
## [1] NA NA NA NA NA NA

R.version.string
# [1] "R Under development (unstable) (2012-12-29 r61478)"

是的。他们在2012年十月添加了这段代码,但直到2013年四月才会被合并到正式版本中发布,不过我想这就是好的测试流程! - Josh O'Brien

0

也许可以使用循环?

for (i in 1:nrow(sel.mat))
{
  dat[sel.mat[i,1],sel.mat[i,2]] <- NA
}

> dat
              V1         V2
    1         NA 0.27002155
    2  0.7253383         NA
    3         NA 0.63847293
    4  0.1768720 0.64586587
    5  0.3796935 0.62261843
    6  0.6751365 0.78328647
    7  0.9801140 0.82259732
    8         NA 0.08606641
    9  0.3294625 0.44110121
    10 0.2830957         NA
    11 0.6868594 0.09767882
    12 0.9802349         NA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接