tags:

views:

398

answers:

4

Hi, I have a logical vector, which i wish to insert new elements at particular indexes - I've come up with a clusmy solution below, but is there a neater way?

probes <- rep(TRUE, 15)
ind <- c(5, 10)
probes.2 <- logical(length(probes)+length(ind))
probes.ind <- ind + 1:length(ind)
probes.original <- (1:length(probes.2))[-probes.ind]
probes.2[probes.ind] <- FALSE
probes.2[probes.original] <- probes

print(probes)

gives

[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

and

print(probes.2)

gives

[1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
[13]  TRUE  TRUE  TRUE  TRUE  TRUE

So it works but is ugly looking - any suggestions?

+2  A: 

That is sorta tricky. Here's one way. It iterates over the list, inserting each time, so it's not too efficient.

probes <- rep(TRUE, 15)
probes.ind <- ind + 0:(length(ind)-1)
for (i in probes.ind) {
  probes <- c(probes[1:i], FALSE, probes[(i+1):length(probes)])
}

> probes
 [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
[13]  TRUE  TRUE  TRUE  TRUE  TRUE

This should even work if ind has repeated elements, although ind does need to be sorted for the probes.ind construction to work.

Harlan
+2  A: 

How about this:

> probes <- rep(TRUE, 15)
> ind <- c(5, 10)

> probes.ind <- rep(NA, length(probes))
> probes.ind[ind] <- FALSE
> new.probes <- as.vector(rbind(probes, probes.ind))
> new.probes <- new.probes[!is.na(new.probes)]
> new.probes
 [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
[13]  TRUE  TRUE  TRUE  TRUE  TRUE
Jonathan Chang
Sneaky! Uses the fact that as.vector does a column-wise collapse of the matrix created by rbind. This is because a matrix is just a vector with additional information that indicates the number of rows, and is internally stored in column-wise order. This is part of the R language definition, but might be a bit obscure, depending on who's reading the code...
Harlan
+3  A: 

You can do some magic with indexes:

First create vector with output values:

probs <- rep(TRUE, 15)
ind <- c(5, 10)
val <- c( probs, rep(FALSE,length(ind)) )
# > val
#  [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
# [13]  TRUE  TRUE  TRUE FALSE FALSE

Now trick. Each old element gets rank, each new element gets half-rank

id  <- c( seq_along(probs), ind+0.5 )
# > id
#  [1]  1.0  2.0  3.0  4.0  5.0  6.0  7.0  8.0  9.0 10.0 11.0 12.0 13.0 14.0 15.0
# [16]  5.5 10.5

Then use order to sort in proper order:

val[order(id)]
#  [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
# [13]  TRUE  TRUE  TRUE  TRUE  TRUE
Marek
Nice and sneaky - how well does order scale with thousands/millions of elements?
Aaron Statham
On 1.58GHz Core2 system.time(order(rnorm(1e6))) takes <2s
Marek
+6  A: 

These are all very creative approaches. I think working with indexes is definitely the way to go (Marek's solution is very nice).

I would just mention that there is a function to do roughly that: append().

probes <- rep(TRUE, 15)
probes <- append(probes, FALSE, after=5)
probes <- append(probes, FALSE, after=11)

Or you could do this recursively with your indexes (you need to grow the "after" value on each iteration):

probes <- rep(TRUE, 15)
ind <- c(5, 10)
for(i in 0:(length(ind)-1)) 
    probes <- append(probes, FALSE, after=(ind[i+1]+i))

Incidentally, this question was also previously asked on R-Help. As Barry says:

"Actually I'd say there were no ways of doing this, since I dont think you can actually insert into a vector - you have to create a new vector that produces the illusion of insertion!"

Shane
I didn't know about the append function, thanks for bringing it to my attention! I should've specified in my question that I didn't want a loop because my real vectors will have thousands/millions of elements
Aaron Statham