4

I have a data looking like the following:

A= c(0,0,0,-1,0,0,0,1,1,1,0,0,-1,0,0,-1,-1,1,1,1,-1,0,0,0,-1,0,0,-1,-1,1,1,0,0,0,0,1,-1)

The goal is to extract alternating -1s and 1s. I want to make a function where the input vector contains 0,1, and -1. The output ideally spits out all the 0s and alternating -1s and 1s.

For instance, the desired output for the above example is:

 B= c(0,0,0,-1,0,0,0,1,0,0,0,0,-1,0,0,0,0,1,0,0,-1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,-1)

The two 1s in the 9th and 10th location in A is turned to 0 because we only keep the first 1 or -1 appearing. The -1s in 16th and 17th location of A is turned to 0 for this reason as well.

Anyone have a good idea for making such a function?

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
jay2020
  • 451
  • 1
  • 3
  • 12
  • 1
    Why is this question downvoted and vtc? It is well explained with an example. – Pierre L Feb 11 '16 at 21:36
  • The odd value it seems to me is position 16. It switches from `0` at position 15 to `-1` but is removed. Why? – Pierre L Feb 11 '16 at 21:40
  • The goal is to only keep unique 1s and -1s to oscillate. There is -1 in the 13th location, so until next 1 is observed, all -1s are turned in to 0s. – jay2020 Feb 11 '16 at 21:42

3 Answers3

5

Identify positions of nonzero values:

w = which(A != 0)

For each run of similar values, in A[w], take the position of the first:

library(data.table)
wkeep = tapply(w, rleid(A[w]), FUN = function(x) x[1])

Set all other values to zero:

# following @alexis_laz's approach
B = numeric(length(A)) 
B[ wkeep ] = A[ wkeep ]

This way, you don't have to make comparisons in a loop, which R is slow at, I think.


rleid comes from data.table. With base R, you can make wkeep with @alexis_laz's suggestion:

wkeep = w[c(TRUE, A[w][-1L] != A[w][-length(w)])]

Or write your own rleid, as in Josh's answer.

Community
  • 1
  • 1
Frank
  • 66,179
  • 8
  • 96
  • 180
  • 2
    On a same approach: `w = which(A != 0L); wkeep = w[c(TRUE, A[w][-1L] != A[w][-length(w)])]; ans = numeric(length(A)); ans[wkeep] = A[wkeep]; ans` – alexis_laz Feb 11 '16 at 22:40
  • 1
    @alexis_laz the existing answer is excellent. But I'd say yours is different enough to be posted on its own, especially as it uses base R functions only (I found it easier to understand). – TooTone Feb 11 '16 at 22:57
  • Thanks @alexis_laz . I've added your approach for `wkeep` and overwritten my third step with yours (for making `ans`). – Frank Feb 12 '16 at 01:20
  • Thank you for your input. I have a very big data and this is very helpful. – jay2020 Feb 12 '16 at 01:34
  • @jay2020 note you are able to change your accepted answer if after you accept the first answer a new answer appears that you prefer. (I usu. wait a few hours at least before accepting anything) – TooTone Feb 12 '16 at 20:34
3

This is really just a Reification of GWarius's pseudo-code. (I already had a structure but logic that was failing.)

last1 <- -A[which(A != 0)[1] ] # The opposite of the first non-zero item
for (i in seq_along(A) ){ 
          if( last1==1 &&  A[i]==-1  ){ last1 <- -1
          } else {if (last1 == -1 && A[i] == 1) { last1 <- 1
                 } else {A[i] <- 0}} }
 A
 [1]  0  0  0 -1  0  0  0  1  0  0  0  0 -1  0  0  0  0  1  0  0 -1  0  0
[24]  0  0  0  0  0  0  1  0  0  0  0  0  0 -1

> identical(A, B)
[1] TRUE
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • 1
    Yeah. I tried with last1=0 and that failed. I guess you could set it to be `-A[which(A != 0)[1] ]` – IRTFM Feb 11 '16 at 22:00
  • Thank you. I already solved it using Warlus's suggestion so I had to choose Warlus. But I really appreciate your time. – jay2020 Feb 11 '16 at 22:02
  • No problem. I've spent more time on some questions and gotten fewer upvotes. – IRTFM Feb 11 '16 at 22:03
2

you have to slide all the array and with a flag variable you check if previously you found 1 or -1. it could be possible pseudo-code algorithm:

while i < length(a):

   if flag == 1 && a[i]=-1:
      b[i]=a[i];
      flag = -1;
   else if flag == -1 && a[i] = 1:
      b[i]=a[i];
      flag = 1;
   else:
      b[i]=0;
   i++;
}//end of while
G Warlus
  • 46
  • 6