在R中的向量中插入元素

时间:2013-09-23 03:10:41

标签: r

我在R中有一个向量

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

让我们说我想有效地插入以下向量,b和c,

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

在第3和第7位,产生,

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

如果不递归地使用cbind左右,我将如何在R中有效地执行此操作。

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

6 个答案:

答案 0 :(得分:13)

试试这个:

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

请注意奖励错误检查位置和插入的数量是否不匹配。

答案 1 :(得分:11)

您可以使用以下功能

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))]
    )
}

答案 2 :(得分:6)

这是另一个函数,使用里卡多的语法,费迪南德的分裂和@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 > length(a))length(bs)!=length(pos)

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

答案 3 :(得分:5)

直截了当的方法:

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

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

答案 4 :(得分:2)

这是使用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

答案 5 :(得分:2)

在使用费迪南德的功能后,我试着写自己的功能,令人惊讶的是它效率更高。
这是我的:

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