【问题标题】:insert elements in a vector in R在R中的向量中插入元素
【发布时间】:2019-08-02 23:36:42
【问题描述】:

我在 R 中有一个向量,

a = c(2,3,4,9,10,2,4,19)

假设我想有效地插入以下向量 b 和 c,

b = c(2,1)
d = c(0,1)

就在第 3 和第 7 位(“4”条目)之后,结果是,

e = c(2,3,4,2,1,9,10,2,4,0,1,19)

我如何在 R 中有效地做到这一点,而无需递归使用 cbind 左右。

我找到了一个包 R.basic,但它不是 CRAN 包的一部分,所以我考虑使用受支持的版本。

【问题讨论】:

  • 我想你还有一个额外的 4。
  • 对于向量,c 有效(而不是 cbind),这可能是不将向量命名为“c”的一个原因...
  • 请注意,您应该使用<- 而不是=

标签: r


【解决方案1】:

试试这个:

result <- vector("list",5)
result[c(TRUE,FALSE)] <- split(a, cumsum(seq_along(a) %in% (c(3,7)+1)))
result[c(FALSE,TRUE)] <- list(b,d)
f <- unlist(result)

identical(f, e)
#[1] TRUE

编辑:推广到任意数量的插入很简单:

insert.at <- function(a, pos, ...){
    dots <- list(...)
    stopifnot(length(dots)==length(pos))
    result <- vector("list",2*length(pos)+1)
    result[c(TRUE,FALSE)] <- split(a, cumsum(seq_along(a) %in% (pos+1)))
    result[c(FALSE,TRUE)] <- dots
    unlist(result)
}


> insert.at(a, c(3,7), b, d)
 [1]  2  3  4  2  1  9 10  2  4  0  1 19

> insert.at(1:10, c(4,7,9), 11, 12, 13)
 [1]  1  2  3  4 11  5  6  7 12  8  9 13 10

> insert.at(1:10, c(4,7,9), 11, 12)
Error: length(dots) == length(pos) is not TRUE

如果位置和插入的数量不匹配,请注意额外的错误检查。

【讨论】:

  • 你能解释一下5的逻辑吗?我试图将它推广到任何组合,但我很难,得到很多类型错误......谢谢......
  • @user2805568 拼在一起的片数:5 =(插入次数)*2 + 1。
  • @user2805568 我很好奇你为什么接受了一个不符合预期结果的答案?
【解决方案2】:

你可以使用下面的函数,

ins(a, list(b, d), pos=c(3, 7))
# [1]  2  3  4  2  1  9 10  2  4  0  1  4 19

在哪里:

ins <- function(a, to.insert=list(), pos=c()) {

  c(a[seq(pos[1])], 
    to.insert[[1]], 
    a[seq(pos[1]+1, pos[2])], 
    to.insert[[2]], 
    a[seq(pos[2], length(a))]
    )
}

【讨论】:

    【解决方案3】:

    这是另一个函数,使用 Ricardo 的语法、Ferdinand 的 split 和来自另一个问题的 @Arun's interleaving trick

    ins2 <- function(a,bs,pos){
        as <- split(a,cumsum(seq(a)%in%(pos+1)))
        idx <- order(c(seq_along(as),seq_along(bs)))
        unlist(c(as,bs)[idx])
    }
    

    优点是这应该扩展到更多的插入。但是,当传递无效参数时,它可能会产生奇怪的输出,例如,any(pos &gt; length(a))length(bs)!=length(pos)

    如果您不想命名 a 的项目,可以将最后一行更改为 unname(unlist(...

    【讨论】:

    • 是的,“a bs pos”对于一个函数来说是一大堆诅咒。
    • 鸭嘴兽还是战神? (+1)!
    【解决方案4】:

    直截了当的方法:

    b.pos <- 3
    d.pos <- 7
    c(a[1:b.pos],b,a[(b.pos+1):d.pos],d,a[(d.pos+1):length(a)])
    [1]  2  3  4  2  1  9 10  2  4  0  1 19
    

    注意括号对: 运算符边界的重要性。

    【讨论】:

    • 转成函数用:insert_vec &lt;- function(old, new, loc) c(old[1:loc], new, old[-c(1:loc)])
    【解决方案5】:

    在使用了费迪南德的函数后,我尝试编写自己的函数,令人惊讶的是它的效率要高得多。
    这是我的:

    insertElems = function(vect, pos, elems) {
    
    l = length(vect)
      j = 0
      for (i in 1:length(pos)){
        if (pos[i]==1)
          vect = c(elems[j+1], vect)
        else if (pos[i] == length(vect)+1)
          vect = c(vect, elems[j+1])
        else
          vect = c(vect[1:(pos[i]-1+j)], elems[j+1], vect[(pos[i]+j):(l+j)])
        j = j+1
      }
      return(vect)
    }
    
    tmp = c(seq(1:5))
    insertElems(tmp, c(2,4,5), c(NA,NA,NA))
    # [1]  1 NA  2  3 NA  4 NA  5
    
    insert.at(tmp, c(2,4,5), c(NA,NA,NA))
    # [1]  1 NA  2  3 NA  4 NA  5
    

    还有基准测试结果:

    > microbenchmark(insertElems(tmp, c(2,4,5), c(NA,NA,NA)), insert.at(tmp, c(2,4,5), c(NA,NA,NA)), times = 10000)
    Unit: microseconds
                                            expr    min     lq     mean median     uq      max neval
     insertElems(tmp, c(2, 4, 5), c(NA, NA, NA))  9.660 11.472 13.44247  12.68 13.585 1630.421 10000
       insert.at(tmp, c(2, 4, 5), c(NA, NA, NA)) 58.866 62.791 70.36281  64.30 67.923 2475.366 10000
    

    我的代码在某些情况下效果更好:

    > insert.at(tmp, c(1,4,5), c(NA,NA,NA))
    # [1]  1  2  3 NA  4 NA  5 NA  1  2  3
    # Warning message:
    # In result[c(TRUE, FALSE)] <- split(a, cumsum(seq_along(a) %in% (pos))) :
    #   number of items to replace is not a multiple of replacement length
    
    > insertElems(tmp, c(1,4,5), c(NA,NA,NA))
    # [1] NA  1  2  3 NA  4 NA  5
    

    【讨论】:

      【解决方案6】:

      这是使用append 的替代方法。这对小向量很好,但我无法想象它对大向量是有效的,因为循环的每次迭代都会创建一个新向量(这显然是不好的)。诀窍是反转需要插入的东西的向量,以使append 将它们插入到相对于原始向量的正确位置。

      a = c(2,3,4,9,10,2,4,19)
      b = c(2,1)
      d = c(0,1)
      
      pos <- c(3, 7)
      z <- setNames(list(b, d), pos)
      z <- z[order(names(z), decreasing=TRUE)]
      
      
      for (i in seq_along(z)) {
        a <- append(a, z[[i]], after = as.numeric(names(z)[[i]]))
      }
      
      a
      #  [1]  2  3  4  2  1  9 10  2  4  0  1 19
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-05
        • 1970-01-01
        • 1970-01-01
        • 2013-04-02
        • 2010-12-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多