how to get single elements in a vector with repeated observations

Viewed 58

I am trying to get single non-consequently repeated observation from a vector in R, let's put as an example: v <- c(1,1,1,2,2,2,1,1,1,2,1,1,2,2,2,2,2,1,1,1) what I need is basically a function that gives this output c(1,2,1,2,1,2,1) I thought of a for loop for doing this, that should be something like:

uniq_v <- v[1]
for(i in c(1:length(v)-1)[c(1:length(v)-1) >0]){
     if (v[i]!=v[i+1]){
    uniq_v <- c(uniq_v, v[i+1])
}
 }

I am pretty sure that there is a better, simpler way, but I cannot figure it out. Thank you, Giuseppe

2 Answers

How about this (using dplyr):

v[v!=lead(v)] %>% head(-1)

Edit: I realise my answer was not correct. I thought we want to ignore the last value as it doesn't change anymore, but if we want to include it, I think the easiest way is to set a default value in the lead function that's not NA and creates a mismatch

> v[v!=lead(v, default = Inf)]
[1] 1 2 1 2 1 2 1
v <- c(1,1,1,2,2,2,1,1,1,2,1,1,2,2,2,2,2,1,1,1)

These two solutions are equivalent. They have the problem that they ignore the last digit.

library(dplyr, quietly = TRUE, verbose=FALSE, mask.ok=TRUE)
v[v != lead(v)] %>% head(-1)
#> [1] 1 2 1 2 1 2

v[v != v[c(2:length(v), NA)]] |> head(-1)
#> [1] 1 2 1 2 1 2

The reason is because the last comparison is 1 != NA which returns NA when we’d need TRUE. If we change it to this it works:

v[!mapply(identical, v, lead(v))]
#> [1] 1 2 1 2 1 2 1

v[!mapply(identical, v, v[c(2:length(v), NA)])]
#> [1] 1 2 1 2 1 2 1

The easiest solution is rle(v)$values suggested by @Chris. While the solutions above are somewhat self-explanatory, the advantage of this solution is speed.

rle(v)$values
#> [1] 1 2 1 2 1 2 1

If speed is crucial there may even be a better solution:

v[diff(c(v, Inf)) != 0]
#> [1] 1 2 1 2 1 2 1

Here the comparison:

library(microbenchmark)
microbenchmark(
  v[!mapply(identical, v, lead(v))],
  indexed = v[!mapply(identical, v, v[c(2:length(v), NA)])],
  v[v!=lead(v, default = Inf)],
  v[diff(c(v, Inf)) != 0],
  rle(v)$values
)
#> Unit: microseconds
#>                               expr  min    lq   mean median    uq  max neval
#>  v[!mapply(identical, v, lead(v))] 62.3 64.85 66.307  66.20 67.65 73.3   100
#>                            indexed 36.7 38.20 39.920  39.65 40.90 60.8   100
#>     v[v != lead(v, default = Inf)] 24.4 26.95 28.619  28.30 29.10 74.3   100
#>            v[diff(c(v, Inf)) != 0]  4.2  5.20  6.330   6.20  6.95 24.3   100
#>                      rle(v)$values 10.8 13.00 15.029  15.05 16.30 30.8   100

Created on 2022-06-10 by the reprex package (v2.0.1)

Related