4

I'm trying to find "peaks" in a vector, i.e. elements for which the nearest neighboring elements on both sides that do not have the same value have lower values.

So, e.g. in the vector

c(0,1,1,2,3,3,3,2,3,4,5,6,5,7)

there are peaks at positions 5,6,7,12 and 14

Finding local maxima and minima comes close, but doesn't quite fit.

Community
  • 1
  • 1
RoyalTS
  • 9,545
  • 12
  • 60
  • 101

1 Answers1

8

This should work. The call to diff(sign(diff(x)) == -2 finds peaks by, in essence, testing for a negative second derivative at/around each of the unique values picked out by rle.

x <- c(0,1,1,2,3,3,3,2,3,4,5,6,5,7)

r <- rle(x)
which(rep(x = diff(sign(diff(c(-Inf, r$values, -Inf)))) == -2, 
          times = r$lengths))
# [1]  5  6  7 12 14

(I padded your vector with -Infs so that both elements 1 and 14 have the possibility of being matched, should the nearest different-valued element have a lower value. You can obviously adjust the end-element matching rule by instead setting one or both of these to Inf.)

Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • ... and only now do I click through to the linked question and see that Ben Bolker and Tommy there used essentially the same approach and explanation as I did here! I guess that's a sign that this is about as good as you can do. – Josh O'Brien Oct 18 '12 at 00:25
  • Like I said: It was close to what I wanted but I couldn't quite figure out how to change it to achieve what I wanted. Thanks for even taking the time to explain how your solution work! – RoyalTS Oct 18 '12 at 00:28