【问题标题】:Vectorized insertion of elements元素的向量化插入
【发布时间】:2013-04-02 23:49:41
【问题描述】:

我写了一个R 函数insert 将给定元素e 插入向量v 的给定位置p

这里是:

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函数编写等效函数?

有任何建议、帮助或更优雅高效的解决方案吗?提前致谢。

【问题讨论】:

  • 有一个未矢量化的base 函数append 似乎可以执行您希望insert 执行的操作。
  • @mnel:你是对的,谢谢。 append 相当于我的 insert 函数。仍然存在在不同位置插入更多元素的矢量化和有效解决方案的问题。
  • 我认为除非向量被过度分配,否则您将无法就地修改,否则您将不会返回全长向量(或者我可能不理解您的意思1)

标签: r vector vectorization


【解决方案1】:

这是一种允许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】:

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

    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 特别是对于稳定排序位。我的解决方案对此进行了扩展,以允许插入不同长度的值元素。
    【解决方案3】:

    对我来说,您似乎只是在由值而不是索引确定的“位置”处附加。在这种情况下,您只需要使用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 
    

    【讨论】:

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