Reputation: 157
For time series analysis I handle data that often contains leading and trailing zero elements. In this example, there are 3 zeros at the beginning an 2 at the end. I want to get rid of these elements, and filter for the contents in the middle (that also may contain zeros)
vec <- c(0, 0, 0, 1, 2, 0, 3, 4, 0, 0)
I did this by looping from the beginning and end, and masking out the unwanted elements.
mask <- rep(TRUE, length(vec))
# from begin
i <- 1
while(vec[i] == 0 && i <= length(vec)) {
mask[i] <- FALSE
i <- i+1
}
# from end
i <- length(vec)
while(i >= 1 && vec[i] == 0) {
mask[i] <- FALSE
i <- i-1
}
cleanvec <- vec[mask]
cleanvec
[1] 1 2 0 3 4
This works, but I wonder if there is a more efficient way to do this, avoiding the loops.
Upvotes: 5
Views: 793
Reputation: 269852
Take the cumsum
forward and backward of abs(vec)
and keep only elements > 0. if it were known that all elements of vec
were non-negative, as in the question, then we could optionally omit abs
.
vec[cumsum(abs(vec)) > 0 & rev(cumsum(rev(abs(vec)))) > 0]
## [1] 1 2 0 3 4
Upvotes: 0
Reputation: 887501
We could use the range
and Reduce
to get the sequence
vec[Reduce(`:`, range(which(vec != 0)))]
#[1] 1 2 0 3 4
Upvotes: 0
Reputation: 9257
vec[ min(which(vec != 0)) : max(which(vec != 0)) ]
Basically the which(vec != 0)
part gives the positions of the numbers that are different from 0, and then you take the min and max of them.
Upvotes: 6