Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vectorized insertion of elements

I wrote a R function insert to insert a given element e at a given position p of a vector v.

Here it is:

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
}

Note that this function, like almost everything in R, returns a new vector. In addition (see recursive call), it inserts also the name of the element, if it has one.

Here is a simple example of use:

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

I'm trying to write a vectorized (and efficient) version of this function.

For now, I wrote a little elegant solution:

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

And here a simple example of use:

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

So, the issues that I am considering about both functions (insert and vinsert) are:

  1. return a new vector or modify the vector in place and return it?
  2. write an equivalent function with Rcpp?
  3. it is possible to write an equivalent function using first order R function?

Any suggest, help or more elegant and efficient solution? Thanks in advance.

like image 280
leodido Avatar asked Nov 22 '25 11:11

leodido


1 Answers

It seems like there are a number of issues, e.g., how the order of insertion is influenced by prior insertions, and what to do when inserting sequences of more than one element. Here we have an original sequence

x <- letters[1:10]

and some things we'd like to insert into it

v <- LETTERS[1:4]

and where we'd like to insert them

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

One way of doing the insertion is to figure out the order of the new index values at relative to the original; order is providing a stable order

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

and then to do the insertion

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

The insertion rules are not quite the same as your original

> 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 
like image 103
Martin Morgan Avatar answered Nov 25 '25 03:11

Martin Morgan