I have a vector and I want to check every time it contains a sequence of ascending/descending values for length n. All I know so far is that I can use !is.unsorted() and is.unsorted() on a vector to figure out if it is ascending or descending but have no clue on how to do it on every element. Any suggestions would be appreciated!
v <- c(1,2,1,4,5,7,8,10,6,4,2,1,2)
n <- 5
I'm looking to output a vector containing 1, -1 and 0 for ascending, descending and unsorted. In this case, it would output:
[1] 0 0 0 0 0 0 1 1 0 0 0 -1 0
Sorry if this confusing.
CodePudding user response:
You could loop over the vector in length 5 segments and do a case handling if the diff
erences are all
less than, greater than zero or anything else
.
f <- \(v, n) {
r <- vapply(seq_along(v)[1:(length(v) - n 1)], \(i) {
d <- diff(v[(1:n) i - 1])
if (all(d < 0)) -1
else if (all(d > 0)) 1
else 0
}, 0)
return(c(rep(0L, n - 1), r))
}
f(v, 5)
# [1] 0 0 0 0 0 0 1 1 0 0 0 -1 0
## check
rbind(v, r=f(v, 5))
# [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
# v 1 2 1 4 5 7 8 10 6 4 2 1 2
# r 0 0 0 0 0 0 1 1 0 0 0 -1 0
Note: R >= 4.1 used.
If you're more familiar with for
loops you may also do
res <- vector('integer', length(v) - n 1)
for (i in seq_along(v)[1:(length(v) - n 1)]) {
d <- diff(v[(1:n) i - 1])
if (all(d < 0)) res[i] <- -1
else if (all(d > 0)) res[i] <- 1
else res[i] <- 0
}
c(rep(0L, n - 1), res)
# [1] 0 0 0 0 0 0 1 1 0 0 0 -1 0
but it might be slower.
CodePudding user response:
Using data.table
package:
d <- data.table::frollsum(sign(diff(v)), n - 1, 0)
c(0, data.table::fcase(d == n - 1, 1, d == 1 - n, -1, default = 0))
[1] 0 0 0 0 0 0 1 1 0 0 0 -1 0
Calculate adjacent difference and take signs (detecting ascending/descending). Then calculate rolling sum and detect when that sum is equal to n-1 (n consecutive ascending elements) or 1-n (n consecutive descending elements).