Searching for specific maxima

7 views (last 30 days)
Sarah
Sarah on 17 Jan 2012
Hello Everyone,
I have a specific question. Let us say I have some random data set that looks like this:
1 2 1 2 3 2 3 4 122 3 4 5 3 51 4 2
I am expecting two "maxima" or peaks (not necessarily 122 or 51). I want to be able to autonomously detect the two largest maxima, save the two maxima values and the index where they occur. Is there an easy way to do this?
Thanks for your help.
  1 Comment
Walter Roberson
Walter Roberson on 17 Jan 2012
What if more than one location has the same maximum (or second highest) value?

Sign in to comment.

Answers (5)

the cyclist
the cyclist on 18 Jan 2012
x = [1 2 1 2 3 2 3 4 122 3 4 5 3 51 4 2];
[sorted_x indexToSortedValues] = sort(x,'descend');
This will sort the values from high-to-low, and tell you the indices of the sorted values. Be wary of the potential uncertainty that Walter mentions in his comment.
  5 Comments
the cyclist
the cyclist on 18 Jan 2012
I have to admit I totally missed the fact that you seem to be looking for localized peaks, as opposed to just the two largest values (i.e. maxima). There was an FEX "Pick of the Week" for finding local extrema, as discussed here: http://blogs.mathworks.com/pick/2008/05/09/finding-local-extrema/
Sarah
Sarah on 18 Jan 2012
Sorry guys, I should have been more clear, it is my fault. So I looked at two peak detection functions: findpeaks (built-in) and extrema (what the cyclist specified). Both do a good job of detecting the peaks. The problem is, are they adaptive? My signal is changing with every iteration, and my expectation is that eventually the two significant peaks that I care about will eventually become one. I want the peak detection function to realize this. Is that possible?

Sign in to comment.


Andrei Bobrov
Andrei Bobrov on 18 Jan 2012
A = [1 2 1 2 3 2 3 4 122 120 3 4 5 3 51 4 2]
[pks,locs] = findpeaks(A)
[ix,ix] = sort(pks,'descend')
outpks = pks(ix(1:2))
outidx = locs(ix(1:2))
  3 Comments
Sarah
Sarah on 18 Jan 2012
I have access to it, and it works pretty well for me, but I don't think it is robust enough to detect the transition from two peaks to one peak.
Andrei Bobrov
Andrei Bobrov on 18 Jan 2012
Hi cyclist! Variant without 'findpeaks'.
i1 = diff(A(:).')>0;
ix = strfind(i1,[1 0]);
[c i2] = sort(A(ix),'descend')
outpks = c(1:2)
outidx = ix(i2(1:2))

Sign in to comment.


Dr. Seis
Dr. Seis on 18 Jan 2012
Similar to andrei:
A = [1 2 1 2 3 2 3 4 122 120 3 4 5 3 51 4 2]
[pks,locs] = findpeaks(A)
maxpks = max(pks);
threshold = 0.4; % 40% threshold
outpks = pks(pks>threshold*maxpks);
outidx = locs(pks>threshold*maxpks);
Set the threshold so that it will only pick up either: 1) the biggest two picks or 2) one peak
  3 Comments
Dr. Seis
Dr. Seis on 18 Jan 2012
The threshold value is data dependent. It was just a random value that I picked that would yield the largest of the two peaks and would yield only one peak should those two transition together (since if they joined together there would be no other individual peak greater than 40% of 122+51). If, in reality, the peaks are going to be a lot closer in amplitude than the difference between 122 and 51, then you can make that threshold larger (like 80%). However, if you are expecting a larger range of differences between the largest peak and the second largest peak (before they transition into one peak) then you will need to set that threshold lower (like 20-30%).
Sarah
Sarah on 22 Jan 2012
This works, but it still does not robustly detect the peaks. :( I have a more clear explanation below, if you don't mind taking a look at that...

Sign in to comment.


Sarah
Sarah on 22 Jan 2012
Hey guys,
I am afraid that I still haven't found a solution to my problem :( Let me just explain what I am doing first to make everything clear.
I have two signals: a sinusoidal signal with unity frequency and amplitude, and a "rectangular pulse" signal. The pulse has a constant baseline for everywhere except a specific time range, where it becomes one rectangular pulse with a user specified amplitude. I combine these two signals together. Here is what that looks like:
I process this data and transform it into the frequency domain. Here is a link which shows what the frequency domain looks like with the combined signal.
So, there are clearly two peaks. The first peak represents the exact start of the rectangular pulse, and the second peak represents the exact end of the rectangular pulse. Everywhere else is generally constant because of the sinusoidal function.
Now, this is my study. At a specific sample time, I am gradually decreasing the width of the pulse using a for loop. At some point, it will not be clear to see those two peaks like you did before. At some specified width, you might see something like this, for example:
Clearly, though the point representing the second peak is still there, it is difficult to determine and can be construed as one peak. I would like to detect the value of the WIDTH at which this phenomenon occurs. How can I make a robust peak detection algorithm to accurately do this? Simply setting thresholds for findpeaks does not work...any help would be greatly appreciated.
  12 Comments
Sarah
Sarah on 22 Jan 2012
Yes, you are right. That is my problem, and why I say that the detection algorithm isnt quite robust enough to tackle this type of problem.
And as for your second question, I am not sure, as the values fluctuate a lot. But now that you bring that up, maybe I can try? I don't really know how I could define a "robust" range though. Do you have any idea?
Dr. Seis
Dr. Seis on 22 Jan 2012
Try a range of 1.1 times the number of indices associated with the width of your pulse.

Sign in to comment.


Dr. Seis
Dr. Seis on 22 Jan 2012
Next idea, instead of envelope of Freq we replace any negative (or really small) frequencies with linear interpolation of positive ones:
tempFreq = Freq(1,:);
maxFreq = max(tempFreq);
for i = 2 : length(tempFreq)-1
if tempFreq(i) <= 0.01*maxFreq
tempFreq(i) = mean(tempFreq([i-1,i+1]));
end
end
[Peaks,Index] = findpeaks(tempFreq);
[MaxPeak,IndPeak] = max(Peaks);
IndRange = abs(Index(IndPeak)-Index);
IndThres = 500; % Set this value to the number of indices
% associated with your pulse width * 1.1
OutPks = Peaks( IndRange <= IndThres );
OutPks = Index( IndRange <= IndThres );
[OutRow,OutColumn] = size(OutPks);
You will still need to define IndThres to be the about 1.1 times your pulse width above.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!