矢量化插入元素

时间:2013-04-10 22:32:51

标签: r vector vectorization

我写了R函数insert,以便在向量e的给定位置p插入给定元素v

这是:

insert <- function(v, e, p) {
  if (length(e) != 1 || length(p) != 1) {
    stop('supported the insertion of only one element per call.')
  }
  len <- length(v)
  nms <- names(v)
  enm <- names(e)
  res <- NULL
  if (p > 1 && p <= len) {
    res <- c(v[1:(p-1)], e, v[p:len]) # insert
  } else if (p == 1) {
    res <- c(e, v) # prepend 
  } else if (p == (len+1)) {
    res <- c(v, e) # append
  } else {
    stop('wrong position')
  }
  if (!is.null(enm)) {
      names(res) <- insert(nms, enm, p)
  }
  res
}

请注意,此函数与R中的几乎所有内容一样,都会返回一个新向量。另外(参见递归调用),还插入元素的名称,如果有的话。

以下是一个简单的使用示例:

a <- c(1,2,3,4,5,7,8,10)
names(a) <- c(letters[1:5], letters[7:8], letters[10])
a
# a  b  c  d  e  g  h  j 
# 1  2  3  4  5  7  8 10  
b <- c(9)
names(b) <- letters[9]
insert(a, b, 8)
# a  b  c  d  e  g  h  i  j 
# 1  2  3  4  5  7  8  9 10

我正在尝试编写此函数的矢量化(高效)版本

现在,我写了一个优雅的解决方案

vinsert <- function(v, elems, positions) {
  out <- v
  for (i in 1:length(elems)) {
    out <- insert(out, elems[i], positions[i])
  }
  out
}

这是一个简单的使用示例:

a <- c(1,2,3,4,5,7,8,10)
names(a) <- c(letters[1:5], letters[7:8], letters[10])
a
# a  b  c  d  e  g  h  j 
# 1  2  3  4  5  7  8 10
z <- c(6,9)
names(z) <- c(letters[6], letters[9])
z
# f  i
# 6  9
vinsert(a, z, z)
# a  b  c  d  e  f  g  h  i  j 
# 1  2  3  4  5  6  7  8  9 10

因此,我正在考虑的两个函数(insertvinsert)的问题是:

  1. 返回一个新的向量或修改向量并返回它?
  2. 使用Rcpp编写等效函数?
  3. 可以使用第一个订单R函数编写等效函数吗?
  4. 任何建议,帮助或更优雅高效的解决方案?提前谢谢。

3 个答案:

答案 0 :(得分:4)

似乎存在许多问题,例如,插入顺序如何受先前插入的影响,以及插入多个元素的序列时该怎么做。这里我们有一个原始序列

x <- letters[1:10]

以及我们想要插入的一些内容

v <- LETTERS[1:4]

我们想要插入它们

at <- c(4, 7, 2, 6)

进行插入的一种方法是确定新索引值at相对于原始值的顺序; order正在提供稳定订单

o <- order(c(seq_along(x), at))

然后进行插入

> c(x, v)[o]
 [1] "a" "b" "C" "c" "d" "A" "e" "f" "D" "g" "B" "h" "i" "j"

插入规则与原始

不完全相同
> o = order(c(seq_along(a), z))
> c(a, z)[o]
 a  b  c  d  e  g  f  h  j  i 
 1  2  3  4  5  7  6  8 10  9 

答案 1 :(得分:2)

这是一种允许after的多个值的方法(要求值为相同长度的列表

vappend <- function(x, values, after){
  lx <- length(x)
  if(missing(after)){
    after <- length(x)
  }
  la <- length(after)
  if(la ==1L){
    return(append(x, unlist(values), after))
  } else {
    stopifnot(length(values) == length(after))
    lv <- sapply(values, length)

    aae <- rep.int(after,lv) 
    c(x, unlist(values))[order(c(seq_along(x), aae))]

  }
}


vappend(x = 1:5, values = list(2:3,3:5),after = 1:2)
## [1] 1 2 3 2 3 4 5 3 4 5

答案 2 :(得分:1)

对我来说,看起来你只是追加价值而不是指数决定的“位置”。在这种情况下,您只需使用corder

a <- c(1, 2, 3, 4, 5, 7, 8, 10)
names(a) <- letters[a]
a
##  a  b  c  d  e  g  h  j 
##  1  2  3  4  5  7  8 10 


z <- c(6, 9)
names(z) <- letters[z]
z
## f i 
## 6 9 


result <- c(a, z)[order(c(a, z))]
result
##  a  b  c  d  e  f  g  h  i  j 
##  1  2  3  4  5  6  7  8  9 10