DirtStats
DirtStats

Reputation: 599

How to detect sign change (eg, positive to negative) in time series data?

I have data of fluxes over time and would like to obtain a list of times when the flux changed from negative to positive or vice versa. This seems like a simple thing so it probably has a simple answer but I haven't been able to find one with my search terms.

Sample data:

day=c(1,2,3,5,10,20)
flux=c(-2,-4,1,-2,4,11)

I'd like to get something like a vector crossover_times= (2.5, 4, 7.5) to indicate the interpolated days that the change occurred and, ideally, also information indicating which way the sign change went, e.g. sign_changes =(positive, negative, positive).

I could, of course, write a loop to iterate through the data but I'm guessing that R has one or more helpful functions for this. Any suggestions?

Upvotes: 3

Views: 6806

Answers (2)

G. Grothendieck
G. Grothendieck

Reputation: 269654

diff(sign(flux)) will be non-zero at cross-overs and will have the sign of the crossing:

updn <- c(0, diff(sign(flux)))
ix <- which(updn != 0)

(day[ix] + day[ix-1])/2
## [1] 2.5 4.0 7.5

sign(updn)[ix]
## [1]  1 -1  1

UPDATED: Added sign of crossing. Improvements.

Upvotes: 16

josliber
josliber

Reputation: 44330

You could use the nifty rle function to compute the vector of positions where it crosses over and directions. rle computes the lengths and values of the runs of a vector where it's identical, and we've passed it the binary vector of whether your values are non-negative.

pos <- head(cumsum(rle(flux >= 0)$lengths), -1)
pos
# [1] 2 3 4

All the remains is the interpolation and getting the directions:

(day[pos] + day[pos+1]) / 2
# [1] 2.5 4.0 7.5
c("positive", "negative")[head(rle(flux >= 0)$values + 1, -1)]
# [1] "positive" "negative" "positive"

Upvotes: 6

Related Questions