I have many different data sets which are discrete data. The local minimum is not necessary the smallest data but it is the valley around the first peak. I am trying to find the indices of the first valley around the the first peak. My idea is to search the difference between two neighbor points and when the difference is less than some critical value and when the forward point larger than the backward point, then that's the point we wanted. e.g.
for k=PEAK_POS:END_POS
if ( (abs(y(k)-y(k-1))<=0.01) && (y(k-1)>y(k)) )
expected_pos = k;
break;
end
end
this works for some data set but not for all since some dataset might have different sample step so we might change the critical condition but I have so many data set to analyse, I don't think I can analyze each set manually. I am looking for any better way to find that minimum. Thanks.
As mentioned by #JakobS., optimization theory is a large field in Mathematics, with its own journals and conferences and everything.
Your problem does not sound complicated enough to justify the optimization toolbox (correct me if I'm wrong). It sounds like fminsearch is sufficient for your needs. Here's a brief tutorial for it. Type help fminsearch or doc fminsearch for more info.
% example cost function to minimize
z = #(x) sin(x(:,1)).*cos(x(:,2));
% Make a plot
x = -pi:0.01:pi;
y = x;
[x,y] = meshgrid(x,y);
figure(1), surf(x,y, reshape(z([x(:) y(:)]), size(x)), 'edgecolor', 'none')
% find local minimum
[best, fval] = fminsearch(z, [pi,pi])
The result is
best =
1.570819831365890e+00 3.141628097071647e+00
fval =
-9.999999990956473e-01
Which is obviously a very reasonable approximation to the expected local optimum.
Optimization problems are a very broad topic and there has been done a lot already, it's not necessarily a good idea to start coding your own algorithms. For matlab there is the optimization toolbox, which might help: http://www.mathworks.de/products/optimization/
I would use the condition that to the left of a minimum its derivation <0 and to the right >0.
Like in this example:
x = cumsum(rand(1,100)); % nonuniform distance
y = 5*sin(x/10)+randn(size(x)); % example data
dd = diff(y);
ig = [false (dd(1:end-1)<0 & dd(2:end)>0) false]; % patch to correct length
plot(x,y)
line(x(ig),y(ig),'Marker','+','LineStyle','none','Color','r')
and as you stat you'd like the first after a peak:
x_peak = 15;
candidates = x(ig);
i_min=find(candidates>x_peak,1,'first');
candidates(i_min)
Related
I am working on a script which performs a FFT of given short audio file in a loop. I also want to store the peak frequency but I do not know how to do that.
The code looks similar to this:
n = ...
Frequencies = zeros(1,n); % Allocating memory for the peak frequencies
for k = 1:n
str(k)
textFileName = [num2str(k) '.m4a'];
[data,fs] = audioread(textFileName);
%...
% Fast Fourier transform and plotting part works ok
%...
[peaks,frequencies] = findpeaks(abs(cutP2),cutf,'MinPeakHeight',10e-3);
% Here starts the problem
maximum_Peak = max(peaks);
Frequencies(k) = ... % I need to store the frequency which is coupled
% with the maximum amplitude but I do not know how
end
close(figure(n)) %The loop opens one redundant blank plot, I could not
%find out any other way to close it
I do not want to store the amplitudes of peak frequencies, but frequencies of peak amplitudes. If you could help me with the redundant figure, I would be happy. I tried to implement an if statement but did not work.
max contains a second output which returns the index of the maximum value. Use this second value to stores the value of interest.
[maximum_Peak,I] = max(peaks); %Note I Use 'I' for index - personal habit
Frequencies(k) = frequencies(I);
Also, if your goal is only to find the max point findpeaks may be overkill and you could potentially use:
[maximum_Peak,I] = max(abs(cutP2));
%Might want to check that max is high enough
Frequencies(k) = cutf(I);
Note although the code is similar it is not the same and depends on what you want to do.
Finally, some unsolicited advice, your use of frequencies and Frequencies is a bit of a red flag. Generally differences based on capitalization are not a good idea. Consider renaming the latter to freq_of_max_amp
I am trying to fit a line to some data without using polyfit and polyval. I got some good help already on how to implement this and I have gotten it to work with a simple sin function. However, when applied to the function I am trying to fit, it does not work. Here is my code:
clear all
clc
lb=0.001; %lowerbound of data
ub=10; %upperbound of data
step=.1; %step-size through data
a=.03;
la=1482/120000; %1482 is speed of sound in water and 120kHz
ep1=.02;
ep2=.1;
x=lb:step:ub;
r_sq_des=0.90; %desired value of r^2 for the fit of data without noise present
i=1;
for x=lb:step:ub
G(i,1)= abs(sin((a/la)*pi*x*(sqrt(1+(1/x)^2)-1)));
N(i,1)=2*rand()-1;
Ghat(i,1)=(1+ep1*N(i,1))*G(i,1)+ep2*N(i,1);
r(i,1)=x;
i=i+1;
end
x=r;
y=G;
V=[x.^0];
Vfit=[x.^0];
for i=1:1:1000
V = [x.^i V];
c = V \ y;
Vfit = [x.^i Vfit];
yFit=Vfit*c;
plot(x,y,'o',x,yFit,'--')
drawnow
pause
end
The first two sections are just defining variables and the function. The second for loop is where I am making the fit. As you can see, I have it pause after every nth order in order to see the fit.
I changed your fit formula a bit, I got the same answers but quickly got
a warning that the matrix was singular. No sense in continuing past
the point that the inversion is singular.
Depending on what you are doing you can usually change out variables or change domains.
This doesn't do a lot better, but it seemed to help a little bit.
I increased the number of samples by a factor of 10 since the initial part of the curve
didn't look sampled highly enough.
I added a weighting variable but it is set to equal weight in the code below. Attempts
to deweight the tail didn't help as much as I hoped.
Probably not really a solution, but perhaps will help with a few more knobs/variables.
...
step=.01; %step-size through data
...
x=r;
y=G;
t=x.*sqrt(1+x.^(-2));
t=log(t);
V=[ t.^0];
w=ones(size(t));
for i=1:1:1000
% Trying to solve for value of c
% c that
% yhat = V*c approximates y
% or y = V*c
% V'*y = V'*V * c
% c = (V'*V) \ V'*y
V = [t.^i V];
c = (V'*diag(w.^2)*V ) \ (V'*diag(w.^2)*y) ;
yFit=V*c;
subplot(211)
plot(t,y,'o',t,yFit,'--')
subplot(212)
plot(x,y,'o',x,yFit,'--')
drawnow
pause
end
It looks like more of a frequency estimation problem, and trying to fit a unknown frequency
with polynomial tends to be touch and go. Replacing the polynomial basis with a quick
sin/cos basis didn't seem to do to bad.
V = [sin(t*i) cos(t*i) V];
Unless you specifically need a polynomial basis, you can apply your knowledge of the problem domain to find other potential basis functions for your fit, or to attempt to make the domain in which you are performing the fit more linear.
As dennis mentioned, a different set of basis functions might do better. However you can improve the polynomial fit with QR factorisation, rather than just \ to solve the matrix equation. It is a badly conditioned problem no matter what you do however, and using smooth basis functions wont allow you to accurately reproduce the sharp corners in the actual function.
clear all
close all
clc
lb=0.001; %lowerbound of data
ub=10; %upperbound of data
step=.1; %step-size through data
a=.03;
la=1482/120000; %1482 is speed of sound in water and 120kHz
ep1=.02;
ep2=.1;
x=logspace(log10(lb),log10(ub),100)';
r_sq_des=0.90; %desired value of r^2 for the fit of data without noise present
y=abs(sin(a/la*pi*x.*(sqrt(1+(1./x).^2)-1)));
N=2*rand(size(x))-1;
Ghat=(1+ep1*N).*y+ep2*N;
V=[x.^0];
xs=(lb:.01:ub)';
Vfit=[xs.^0];
for i=1:1:20%length(x)-1
V = [x.^i V];
Vfit = [xs.^i Vfit];
[Q,R]=qr(V,0);
c = R\(Q'*y);
yFit=Vfit*c;
plot(x,y,'o',xs,yFit)
axis([0 10 0 1])
drawnow
pause
end
Here is my Matlab code to solve the second order ODE for a mass-spring-dashpot system:
function Spring
clear all;
close all;
options=odeset('RelTol',1e-6);
p0 = [1 0]; %initial position and velocity
[t,p] = ode45(#SpringFunction, [0 20], p0, options);
plot(t,p(:,1)) %plot the first column (x) vs. time
return
function pdot = SpringFunction(t,p)
c = 5; w = 2;
g = sin(t); % forcing function
pdot = zeros(size(p));
pdot(1) = p(2);
pdot(2) = g-c*p(2)-(w^2)*p(1);
return
I believe the output I am getting is wrong because for this case I think a plot of displacement vs. time should look like a sinusoid with decreasing amplitude. Instead, it looks like this:
This seems incorrect to me, but please correct me if I'm wrong. I cannot see what is incorrect in the code.
You're sinusoidally forcing a damped system so you should expect the steady state to be a sinusoid. Here's a nice vibrations tutorial (PDF) β see pp. 448β450 on damped sinusoidal forcing. Try removing the forcing or greatly reducing its amplitude. Also, it looks like you have a lot of damping. Your damping ratio, ΞΆ (zeta), appears to be c/(2*w) = 5/4. This implies that the unforced form of your system is over-damped β with no forcing you won't see oscillation.
Also, be careful using ode45 with oscillatory systems. If your system happens to be too stiff, you may either need to adjust the tolerances or use a stiff solver such as ode15s to get accurate results. You can always try using tighter tolerances to check that they yield qualitatively similar results (same period, amplitude, rate of growth/decay, etc.).
I am fairly new to vibrations and using matalb fft.I am given a set of data (1D array) of length 15000 (not sure if this is relevant) and I am trying to figure out if there is any wave buried in this data at all. I was instructed to possibly use matlab fft. Is that the correct way to do it? what shall I expect to see? I am really not sure how to interpret any results I would get.
Please let me know what you all think.
Thanks and if more details are needed I will provide them.
Example:
% Df=[array is given to me and it is of size 15000];
% t=[time used for the above array, and if it is of the same size, also provided to me]
N_0= length(t);
fs_0=length(Dfxz);
Y_0=fft(Dfxz,N_0);
k_0=-N_0/2:N_0/2-1;
%Find the phase angle
p_0 = (angle(Y_0));
R_0 = norm(Y_0);
ff_0 = (0:length(Y_0)-1)'/length(Y_0)*100; % Frequency vector
FT_power1_0 = abs(Y_0);
plot(k_0*fs_0/N_0,fftshift(abs(Y_0)))
I only see 1 peek at frequency = 0 but I am sure that there are non zero frequencies, what am I doing wrong?
Thanks!
PS:I am not sure how to pick the sampling frequency either? any tips please (keep in mind that I do not know the original frequency)
Try my version. It looks to me like you have all the information you need to find frequency peaks in your data if they exist. If all you can see is a big peak at zero frequency, you probably have a massive DC offset that is drowning out all your other data. I have put a remedy in my code . .
x = randn(15000,1); %//This is the data you were given - I'll use noise for demonstration - replace x with your Df data
%//If youre getting a massive peak at zero that dwarfs everything else, you
%//probably have a large DC offset. Easily removed in the time domain using
%//the following ..
x = x-mean(x);
tAxis = linspace(3/15000,3,15000); %//You said you have this too - I'll make up something for demonstration - make sure you replace this with your t data
dt = diff(tAxis(1:2)); %//sample period from time axis
fs = 1/dt;%//sample rate from sample period
NFFT = numel(x); %//number of fft bins - change if you like
Y = abs(fft(x, NFFT)).^2; %power spectrum
%//Calculate frequency axis
df = fs/NFFT;
fAxis = 0:df:(fs-df);
%//Plot it all
figure; plot(fAxis(1:NFFT/2), Y(1:NFFT/2))
xlabel('Frequency in Hz')
ylabel('Power')
If that works out OK, you can go into more depth by checking out another FFT answer on stackoverflow.
I'm trying to come up with a scoring system for some behavioural psychology research.
I ask people to draw a letter, then trace over it, both on a graphics tablet. I want to assess the accuracy of this trace. So, you draw any letter ('a'), then you do it again, then I score it based on how similar it was to the first time you drew it. The drawings are stored as pixel locations.
Accuracy is assessed as closeness to the original letter. The method does not need to allow for scale, rotation or position changing. Conceptually it's like the area between the two lines, only the lines are highly irregular, so integrals (to my knowledge) wont work.
I'm writing in MATLAB, but any conceptual help would be appreciated. I've tried summing the minimum distance between all pixels drawn on, but this gives good (low) scores to well placed single points.
This must have been done before, but I'm not having any luck with my searches.
--- Partial Solution using method suggested by #Bill below. Doesn't work, as the bwdist gradient is too steep. Rather than the nice second image Bill shows, it looks more like the original.
%% Letter to image
im = zeros(1080,1920,3); % The screen (possible pixel locations)
% A small square a bit like the letter 'a', a couple of pixels wide.
pixthick = 5;
im(450:450+pixthick,[900:1100],:) = 1;
im(550:550+pixthick,[900:1100],:) = 1;
im([450:550],900:900+pixthick,:) = 1;
im([450:570],1100:1100+pixthick,:) = 1;
subplot(2,1,1); imagesc(im); %% atransbw = bwdist(im(:,:,1)<0.5); subplot(2,1,2);
imagesc(atransbw);
Shape contexts are a powerful feature descriptor based on "polar histograms" of the shapes. The Wikipedia page is in-depth, but here is another page with additional information (and a good visual explanation of the technique), as well as MATLAB demo code. Matching letters was one of the original applications of the method, and the demo code I link to doesn't require you to convert your trace vectors to images.
A more simplistic method might be an "image difference" defined as the exclusive-or of two letters. This would require converting your trace vectors to binary images. Something like:
x = xor(im1,im2);
d = sum(x(:)) / sum(im1(:)); %# normalize to the first image
Finally, if your trace vectors have the same number of points, or can be made to by sampling, Procrustes Analysis could be useful. The idea of Procrustes analysis is to find a least-squares optimum linear transformation (rotation, translation and scaling) between two sets of points. Goodness of fit between the two point sets is given by the "Procrustes statistic" or other measures like root-mean-square deviation of the points.
%# Whatever makes sense;
%# procrustes needs N x 2 matrices with (x,y) coords for N points.
coords1 = [x1 y1];
coords2 = [x2 y2];
%# This sampling may be too naive.
n = max( size(coords1,1), size(coords2,1) );
coords1 = coords1(1:n,:);
coords2 = coords2(1:n,:);
%# d is sum-of-squares error
%# z is transformed coords2
%# tr is the linear transformation
[ d, z, tr ] = procrustes( coords1, coords2 );
%# RMS deviation of points may be better than SSE.
n = size(coords1,1);
rmsd = sqrt((sum((coords1(:) - z(:)).^2) / n));
What could help you is a distance transform, implemented in MATLAB as bwdist. This rewards lines being close, even if they don't match.
a_img_1 = imread('a.jpg');
imagesc(a_img_1);
a_img_1_dist_transform = bwdist( a(:, :, 1) < 250 );
imagesc(a_img_1_dist_transform);
You can do the same with the second image, and sum up the difference in pixel values in the distance transformed images, something like:
score = sum( abs( a_img_1_dist_transform(:) - a_img_2_dist_transform(:) ) )
(Note that this will give higher scores to less similar images and v.v.)
To help prevent issues that you mention of "good (low) scores to well placed single points", you could experiment with other distance measures, such as squared distance between pixel values.
You may want to find an affine transform that will match with some error criterion, say mean squared error. This way you will be invariant to translation and scaling. Or if you wanted to penalize translation, you could add the cost of translation as well. (It would help us help you if you give more information on what kind of features do consider similar or otherwise)
Now, an efficient implementation is another matter. Perhaps you should look into image registration. I'm sure this has been done numerous times.
This is my final, overcomplicated solution, which basically uses Bill Cheatham's method. Thanks for all the help!
% pixLet is the 2D vector contain locations where drawing occurred. First convert it to an image.
im = zeros(1000,1000); % This is the image
for pix = 2:size(pixLet,1)
y1 = pixLet(pix-1,2); x1 = pixLet(pix-1,1);
y2 = pixLet(pix,2); x2 = pixLet(pix,1);
xyd = round(pdist([x1 y1; x2 y2])*2);
xs = round(linspace(x1,x2,xyd));
ys = round(linspace(y1,y2,xyd));
for linepix = 1:length(xs)
im(ys(linepix),xs(linepix)) = 1;
end
end
% Blur the image
blur = fspecial('gaussian',[sz sz],reach);
gausIm = conv2(im,blur,'same');
% I made a function of the above to do this for both the template and the trace.
score = sum(sum(abs(gausIm1-gausIm2)));
I would actually suggest a much more high-level solution. Find an OCR machine learning algorithm that returns some kind of confidence. Or, if you don't have confidence, test the distance between the output text and the actual.
This is like a human that watches the handwriting and attempts to understand it. The higher the confidence, the better the result.