Searching for specific maxima

11 次查看(过去 30 天)
Sarah
Sarah 2012-1-17
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 个评论
Walter Roberson
Walter Roberson 2012-1-17
What if more than one location has the same maximum (or second highest) value?

请先登录,再进行评论。

回答(5 个)

the cyclist
the cyclist 2012-1-18
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 个评论
the cyclist
the cyclist 2012-1-18
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 2012-1-18
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?

请先登录,再进行评论。


Andrei Bobrov
Andrei Bobrov 2012-1-18
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 个评论
Sarah
Sarah 2012-1-18
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 2012-1-18
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))

请先登录,再进行评论。


Dr. Seis
Dr. Seis 2012-1-18
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 个评论
Dr. Seis
Dr. Seis 2012-1-18
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 2012-1-22
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...

请先登录,再进行评论。


Sarah
Sarah 2012-1-22
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 个评论
Sarah
Sarah 2012-1-22
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 2012-1-22
Try a range of 1.1 times the number of indices associated with the width of your pulse.

请先登录,再进行评论。


Dr. Seis
Dr. Seis 2012-1-22
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!

Translated by