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)