有条件地基于索引替换向量的元素

dim*_*_ps 12 r

最好用一个例子来解释.

我有一个data.frame名为的向量或列vec:

vec <- c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA)
Run Code Online (Sandbox Code Playgroud)

我想一个矢量化处理(不是for循环)来改变三个后NA一个时1,观察到.

结束向量将是:

c(NA, NA, 1, 1, 1, 1, NA, 1, 1, 1, 1, NA, NA, NA)
Run Code Online (Sandbox Code Playgroud)

如果我们有:

vec <- c(NA, NA, 1, NA, 1, NA, NA, 1, NA, NA, NA, NA, NA, NA)
Run Code Online (Sandbox Code Playgroud)

结束向量看起来像:

c(NA, NA, 1, 1, 1, 1, 1, 1, 1, 1, 1, NA, NA, NA)
Run Code Online (Sandbox Code Playgroud)

一个写得很糟糕的解决方案是:

vec2 <- vec
for(i in index(v)){
  if(!is.na(v[i])) vec2[i] <- 1
  if(i>3){
    if(!is.na(vec[i-1])) vec2[i] <- 1
    if(!is.na(vec[i-2])) vec2[i] <- 1
    if(!is.na(vec[i-3])) vec2[i] <- 1
  }
  if(i==3){
    if(!is.na(vec[i-1])) vec2[i] <- 1
    if(!is.na(vec[i-2])) vec2[i] <- 1
  }
  if(i==2){
    if(!is.na(vec[i-1])) vec2[i] <- 1
  }
}
Run Code Online (Sandbox Code Playgroud)

nic*_*ola 17

另外一个选项:

`[<-`(vec,c(outer(which(vec==1),1:3,"+")),1)
# [1] NA NA  1  1  1  1 NA  1  1  1  1 NA NA NA
Run Code Online (Sandbox Code Playgroud)

虽然上面的例子适用于实例,但vec如果在最后的位置找到1则延长了它的长度.最好做一个简单的检查并包装成一个函数:

threeNAs<-function(vec) {
    ind<-c(outer(which(vec==1),1:3,"+"))
    ind<-ind[ind<=length(vec)]
    `[<-`(vec,ind,1)
}
Run Code Online (Sandbox Code Playgroud)


Jaa*_*aap 13

另一个快速解决方

vec[rep(which(vec == 1), each = 3) + c(1:3)] <- 1
Run Code Online (Sandbox Code Playgroud)

这使:

> vec
 [1] NA NA  1  1  1  1 NA  1  1  1  1 NA NA NA
Run Code Online (Sandbox Code Playgroud)

基准测试仅在更大的数据集上完成时才有用.一个带有10k大向量的基准测试和几个已发布的解决方案:

library(microbenchmark)

microbenchmark(ans.jaap = {vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4); 
                           vec[rep(which(vec == 1), each = 3) + c(1:3)] <- 1},
               ans.989 = {vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4);
                          r <- which(vec==1);
                          vec[c(mapply(seq, r, r+3))] <- 1},
               ans.sotos = {vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4);
                            vec[unique(as.vector(t(sapply(which(vec == 1), function(i) seq(i+1, length.out = 3)))))] <- 1},
               ans.gregor = {vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4);
                             vec[is.na(vec)] <- 0;
                             n <- length(vec);
                             vec <- vec + c(0, vec[1:(n-1)]) + c(0, 0, vec[1:(n-2)]) + c(0, 0, 0, vec[1:(n-3)]);
                             vec[vec == 0] <- NA},
               ans.moody = {vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4);
                            output <- sapply(1:length(vec),function(i){any(!is.na(vec[max(0,i-3):i]))});
                            output[output] <- 1;
                            output[output==0] <- NA},
               ans.nicola = {vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4);
                             `[<-`(vec,c(outer(which(vec==1),1:3,"+")),1)})
Run Code Online (Sandbox Code Playgroud)

它给出了以下基准:

Unit: microseconds
       expr        min         lq       mean     median         uq        max neval   cld
   ans.jaap   1778.905   1937.414   3064.686   2100.595   2257.695  86233.593   100 a    
    ans.989  87688.166  89638.133  96992.231  90986.269  93326.393 182431.366   100   c  
  ans.sotos 125344.157 127968.113 132386.664 130117.438 132951.380 214460.174   100    d 
 ans.gregor   4036.642   5824.474  10861.373   6533.791   7654.587  87806.955   100  b   
  ans.moody 173146.810 178369.220 183698.670 180318.799 184000.062 264892.878   100     e
 ans.nicola    966.927   1390.486   1723.395   1604.037   1904.695   3310.203   100 a
Run Code Online (Sandbox Code Playgroud)

  • 感谢您将我包括在基准测试中以及取消删除(不确定您或@DavidArenburg:感谢David,如果是这样)我的回答.我删除了它,因为`which()`部分确实很常见.接受答案的其余部分的实现很差,我发表了一篇评论,希望能够进行编辑,这会使我的答案毫无用处. (6认同)

Sym*_*xAU 5

什么是'矢量化',如果不是用C语言编写的循环?

这是一个基准测试的C++循环.

vec <- c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA)

library(Rcpp)

cppFunction('NumericVector fixVec(NumericVector myVec){

    int n = myVec.size();
    int foundCount = 0;

    for(int i = 0; i < n; i++){
      if(myVec[i] == 1) foundCount = 1; 

      if(ISNA(myVec[i])){
        if(foundCount >= 1 & foundCount <= 3){
          myVec[i] = 1;
          foundCount++;
        }
      }
    }
    return myVec;
    }')

fixVec(vec)
# [1] NA NA  1  1  1  1 NA  1  1  1  1 NA NA NA
Run Code Online (Sandbox Code Playgroud)

基准

library(microbenchmark)

microbenchmark(
      ans.jaap = {
        vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4); 
      vec[rep(which(vec == 1), each = 4) + c(0:3)] <- 1
},

    ans.nicola = {
        vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4);
      `[<-`(vec,c(outer(which(vec==1),0:3,"+")),1)
        },

    ans.symbolix = {
        vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4);
      vec <- fixVec(vec)
        }
)

# Unit: microseconds
# expr              min       lq      mean   median        uq       max neval
# ans.jaap     2017.789 2264.318 2905.2437 2579.315 3588.4850  4667.249   100
# ans.nicola   1242.002 1626.704 3839.4768 2095.311 3066.4795 81299.962   100
# ans.symbolix  504.577  533.426  838.5661  718.275  966.9245  2354.373   100


vec <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4)
vec <- fixVec(vec)

vec2 <- rep(c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA),1e4)
vec2[rep(which(vec2 == 1), each = 4) + c(0:3)] <- 1

identical(vec, vec2)
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)


989*_*989 0

这个怎么样:

r <- which(vec==1)
vec[c(mapply(seq, r, r+3))] <- 1
Run Code Online (Sandbox Code Playgroud)

例子:

vec <- c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, NA)
#[1] NA NA  1  1  1  1 NA  1  1  1  1 NA NA NA

vec <- c(NA, NA, 1, NA, 1, NA, NA, 1, NA, NA, NA, NA, NA, NA)
#[1] NA NA  1  1  1  1  1  1  1  1  1 NA NA NA
Run Code Online (Sandbox Code Playgroud)

  • 我看到你躲在标志后面,所以这里再说一遍: `outer(r, 0:3, '+')` 与 `mapply(seq, r, r+3)` 完全不同,因为 A: `outer` 是完全矢量化,而“maply”只是一个循环。B:运行“+”比“seq”便宜得多(计算上),而且总体上是一个完全不同的概念。@nicolas 的想法更好,满足了 vectoization 要求,甚至与您的解决方案相差甚远。另外,请查看基准测试和 ~X60 时间速度差异。 (9认同)
  • 不要在您的答案中包含对投票行为的投诉。 (9认同)
  • @JorisMeys如果问题要求一个“向量化”(我们可能会快速阅读或高效)解决方案,而有人用非向量化解决方案回答,那么这是一个好的答案(或使用术语“有用”)吗?如果您认为这是一个有用的答案,请投票;那些认为它没有用的人可以投反对票。我不明白“有效”与此有什么关系? (6认同)
  • @Jaap 非常正确,尽管我确实理解他的挫败感。它并不是严格意义上的矢量化,但它是一个有效的答案,不值得那么多反对票(准确地说是 8 票)。我不明白为什么有人投票删除这个。这只是刻薄,没有帮助SO前进。 (5认同)
  • 如果末尾有“1”,此代码将更改向量的长度。例如 `vec &lt;- c(NA, NA, 1, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA, 1)` (4认同)
  • 在不使用循环的情况下,它是如何矢量化的? (4认同)
  • @GavinSimpson 现在我们可以进行语义讨论,解释为什么 `maply` 不是一个真正的向量化函数,而且“不是一个 for 循环”(正如 OP 所要求的),但这是令人毛骨悚然的。由于这不是一个很好的解决方案,因此不值得投票。因为它确实完成了这项工作并且没有使用 for 循环,所以对我来说这还不足以投反对票。所以我就不管它并支持其他答案。这实际上是 5 年来我第一次看到有效的解决方案在某一时刻被否决至 -5。这就是与此有关的事情。青年MMV (3认同)