Matlab: how to find fundamental frequency from list of energy peaks - matlab

In a spectrogram, I have a set of harmonic frequencies (peaks in the spectrum) for a given time frame:
5215
3008.1
2428.1
2214.9
1630.2
1315
997.01
881.39
779.04
667.47
554.21
445.77
336.39
237.69
124.6
If I do -diff(ans), I get the differences between the formants, which hint me to the fact that the fundamental frequency f_0 of this frame is around 110 Hz:
2206.9
580.06
213.11
584.72
315.24
317.97
115.62
102.35
111.57
113.26
108.44
109.38
98.705
113.08
It is clear that the last 9 values of the first list are harmonics of the same f_0, because the last 8 values of the second list are around the same value. Their mean is 109.05 (but I'm not sure if that is the correct f_0). How can I calculate f_0 in a neat function?

I found an answer myself: I calculate the difference between the two peaks with the lowest frequency values and with energy values above a certain threshold. Then, I check if that difference is (within a certain range) in the list of frequencies.

Related

Finding peaks value in an ECG signal starting from the filtered ones in MATLAB

I have a table element containing an ECG signal. This has been filtered in order to find the peaks values and peaks locations (in terms of time). The task is to find the peak values in the original signal starting from the peaks I found within a range of 40 elements. This is what I tried:
for i=1:length(peaks_ECG_IV)
A=x_ECG_IV(i) - 20*Tc :Tc: x_ECG_IV(i) + 20*Tc;
if x_ECG_IV(i) + 20*Tc < x_ECG_IV(length(peaks_ECG_IV)) && x_ECG_IV(i) - 20*Tc > x_ECG_IV(1)
[y_i,x_i] = findpeaks(DatiECGPPG.ECGLeadIV,A);* %DatiECGPPG.ECGLeadIV and A have different size*
end
end
Tc is 1/Fsamp
x_ECG_IV(i) is the peak location
peaks_ECG_IV is the peak value in the filter signal which is known
y_i and x_i are the value and the location of the peaks I need to determine.
DatiECGPPG.ECGLeadIV is the whole array of ECG values (100000 elements)
The issue is that A and DatiECGPPG.ECGLeadIV have different sizes so I think I need to consider only the interval of 40 values of DatiECGPPG.ECGLeadIV that correspond to A but I can't figure out how.

understand and coding of the zero-lag cross-correlation matlab

First of all, I am sorry if I am a dummy and cant understand this part of an article. I have a set of data with 200 channels in which every specific two channels are co-dependent. In the paper it is mentioned:
"
For each channel, we filtered both signals
between 0.5 and 2.5 Hz to preserve only the cardiac component and
normalized the resulting signals to balance any difference between
their amplitude.
"
Question1: this means I need to normalize both co-dependent channels to the average of the median? or just normalize each signal to its own median?
Here is the rest of the paragraph
"
Then, we computed the cross-correlation
extracted the value at a time lag of 0 to quantify the similarity
between the filtered signals. In-phase and counter-phase identical
waveforms yielded a zero-lag cross-correlation value of 1 and -1
respectively, whereas a null value derived from totally uncorrelated
signals. "
I wrote the code below: but I get -1 or plus one everywhere even for signals that are not codependent it gives me 1 or -1. I guess I am wrong in part of the code but rationally I don't know where. Here is the code
datafile='data_sess_03.nirs'
ch_num=1
[w,src,det,mlOrg,mlo,mlm,Data,datap,acc1,acc2]=readData(datafile);
fc=[0.5 2.5];
dataf=filterData(Data,fc);
[c,lags]=xcorr(dataf(1,:),dataf(5,:),0); % channel 1 and 5 are
codependent
%% c is -1 and plus one every when even in the noisy channels
plot(acor,'black')
[~,I] = max(abs(acor));
lagDiff = lag(I)/fs
Any help will be really appreciated. Thanks a lot to help me

Filter data vector - get rid of noise - retain peaks (max and min)

I have a distance sensor (acoustic) that has temperature-dependent noise signal. I want to retain the peaks, but get rid of the temperature related noise.
I need a program that checks the absolute value of the difference between the current and previous measurement (e.g. abs(d1[i+1]-d1[i])). If the difference is less than or equal to a threshold (delta), then d2[i+1] = d1[i]. If the difference is greater than delta, then d2[i+1]=d1[i+1]. An example vector looks something like:
data vector d1 is 4,6,5,7,6,5,7,10,55,56,58,30,10
The desired, filtered data (d2) would look something like (in this case delta=1): 4,6,6,7,7,7,7,10,55,55,58,30,10
I looked at lowess() and sma(), but they were unsatisfactory, or my ability to apply them correctly is unsatisfactory. I have used lowess in previous attempts, but it was inconsistent in capturing peaks and valleys. It seems a combination of sapply() and an ifelse() function should be able to do this, but my skill level is too poor. Any help would be most appreciated.

How do I plot values in an array vs the number of times those values appear in Matlab?

I have a set of ages (over 10000 of them) and I want to plot a graph with the age from 20 to 100 on the x axis and then the number of times each of those ages appears in the data on the y axis. I have tried several ways to do this and I can't figure it out. I also have some other data which requires me to plot values vs how many times they occur so any advice on how to do this would be much appreciated.
I'm quite new to Matlab so it would be great if you could explain how things in your answer work rather than just typing out some code.
Thanks.
EDIT:
So I typed histogram(Age, 80) because as I understand that will plot the values in Age on a histogram split up into 80 bars (1 for each age). Instead I get this:
The bars aren't aligned and it's clearly not 1 per age nor has it plotted the number of times each age occurs on the y axis.
You have to use histogram(), and that's correct.
Let's see with an example.
I extract 100 ages between 20 and 100:
ages=randsample([20:100],100,true);
Now I call histogram() in this manner:
h=histogram(ages,[20:100]);
where h is an histogram object and this will also show the following plot:
However, this might look easy due to the fact that my ages vector is in range 20:100, so it will not contain any other values. If your vector, as instead, contains also ages not in range 20:100, you can specify the additional option 'BinLimits' as third input in histogram() like this:
h=histogram(ages,length([20:100]),'BinLimits',[20:100]);
and this option plots a histogram using the values in ages that fall between 20 and 100 inclusive.
Note: by inspecting h you can actually see and/or edit some proprieties of your histogram. An attribute (field) of such object you might be interested to is Values. This is a vector of length 80 (in our case, since we work with 80 bins) in which the i-th element is the number of items is the i-th bin. This will help you count the occurrences (just in case you need them to go on with your analysis).
Like Luis said in comments, hist is the way to go. You should specify bin edges, rather than the number of bins:
ages = randi([20 100], [1 10000]);
hist(ages, [20:100])
Is this what you were looking for?

Arbitrary distribution -> Uniform distribution (Probability Integral Transform?)

I have 500,000 values for a variable derived from financial markets. Specifically, this variable represents distance from the mean (in standard deviations). This variable has a arbitrary distribution. I need a formula that will allow me to select a range around any value of this variable such that an equal (or close to it) amount of data points fall within that range.
This will allow me to then analyze all of the data points within a specific range and to treat them as "similar situations to the input."
From what I understand, this means that I need to convert it from arbitrary distribution to uniform distribution. I have read (but barely understood) that what I am looking for is called "probability integral transform."
Can anyone assist me with some code (Matlab preferred, but it doesn't really matter) to help me accomplish this?
Here's something I put together quickly. It's not polished and not perfect, but it does what you want to do.
clear
randList=[randn(1e4,1);2*randn(1e4,1)+5];
[xCdf,xList]=ksdensity(randList,'npoints',5e3,'function','cdf');
xRange=getInterval(5,xList,xCdf,0.1);
and the function getInterval is
function out=getInterval(yPoint,xList,xCdf,areaFraction)
yCdf=interp1(xList,xCdf,yPoint);
yCdfRange=[-areaFraction/2, areaFraction/2]+yCdf;
out=interp1(xCdf,xList,yCdfRange);
Explanation:
The CDF of the random distribution is shown below by the line in blue. You provide a point (here 5 in the input to getInterval) about which you want a range that gives you 10% of the area (input 0.1 to getInterval). The chosen point is marked by the red cross and the
interval is marked by the lines in green. You can get the corresponding points from the original list that lie within this interval as
newList=randList(randList>=xRange(1) & randList<=xRange(2));
You'll find that on an average, the number of points in this example is ~2000, which is 10% of numel(randList)
numel(newList)
ans =
2045
NOTE:
Please note that this was done quickly and I haven't made any checks to see if the chosen point is outside the range or if yCdfRange falls outside [0 1], in which case interp1 will return a NaN. This is fairly straightforward to implement, and I'll leave that to you.
Also, ksdensity is very CPU intensive. I wouldn't recommend increasing npoints to more than 1e4. I assume you're only working with a fixed list (i.e., you have a list of 5e5 points that you've obtained somehow and now you're just running tests/analyzing it). In that case, you can run ksdensity once and save the result.
I do not speak Matlab, but you need to find quantiles in your data. This is Mathematica code which would do this:
In[88]:= data = RandomVariate[SkewNormalDistribution[0, 1, 2], 10^4];
Compute quantile points:
In[91]:= q10 = Quantile[data, Range[0, 10]/10];
Now form pairs of consecutive quantiles:
In[92]:= intervals = Partition[q10, 2, 1];
In[93]:= intervals
Out[93]= {{-1.397, -0.136989}, {-0.136989, 0.123689}, {0.123689,
0.312232}, {0.312232, 0.478551}, {0.478551, 0.652482}, {0.652482,
0.829642}, {0.829642, 1.02801}, {1.02801, 1.27609}, {1.27609,
1.6237}, {1.6237, 4.04219}}
Verify that the splitting points separate data nearly evenly:
In[94]:= Table[Count[data, x_ /; i[[1]] <= x < i[[2]]], {i, intervals}]
Out[94]= {999, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000}