Plotting histogram of a slice of a matrix - matlab

I have a matrix amp containing 10 row signals [1*1001]. So the total dimension of my data is [10*1001].
Each row is containing amplitude fluctuations(signal). Now by using plot(f,abs(amp)), I am plotting all 10 signals on the f vector which is having length of [1*1001].
This f vector is a frequency axes going from 70 to 110 kHz.
This is the graph which I am plotting.
figure,plot(f2,abs(amp));
xlabel('Frequency in KHz');ylabel('amp');
Now I want a histogram at 90Khz, that means at particular 90KHz frequency, how much amplitude of all 10 signals are changing?

It is somewhat difficult to understand your question, but it sounds like you would like a histogram of your row data near f = 90 KHz. If that's true, I think this should work:
%find the f2 value closest to 90000:
[f0, index] = min(abs(f2-90000));
%make a histogram of the data:
histogram(amp(:,index),10);

Related

How to make normalized frequency distribution plot for 2D matrices?

I have two 80*80 matrices. I would like to plot normalized frequency plot. I tried to normalize the 80* 80 grid with the following code:
A = per_monsoon_postmon; % (A is my 80*80 matrix)
A = rand (80,80);
minA = repmat(min(A), [size(A, 1), 1]);
normA = max(A) - min(A);
normA = repmat(normA, [length(normA) 1]);
normalizedA = (A - minA)./normA;
But this code didn't give me the desired result, as grids with nan values also has a number in it. For eg. earlier grid 1*1 is nan now it has a value of 0.8340. Could you please help me how to normalize the 2D matrix and then plot frequency distribution plot in MATLAB? Is there a way to directly plot normalized frequency distribution plot?
If you have nan values in your verctor you might have problems. I would first replace the nans (for example with zeros).
Normalisation between 0 and one works like this:
a=rand(80,80); %generates random 80x80 array
a=a-min(min(a)); %shifts the values from 0 to n, min(min() ) because it is 2x2
a=a./max(max(a));% shifts to 0 to 1
If you want tot plot these values in 3d I would use a surf plot therefore you first generate the sample values and then feed them the z values
[x,y]=meshgrid(1:80);
surf(x,y,z)

How to find the frequency of a periodic sound signal?

I'm working on sound signals of a walking pattern, which has obvious regular patterns:
Then I thought I can get the frequency of walking (approximately 1.7Hz from the image) using FFT function:
x = walk_5; % Walking sound with a size of 711680x2 double
Fs = 48000; % sound frquency
L=length(x);
t=(1:L)/Fs; %time base
plot(t,x);
figure;
NFFT=2^nextpow2(L);
X=fft(x,NFFT);
Px=X.*conj(X)/(NFFT*L); %Power of each freq components
fVals=Fs*(0:NFFT/2-1)/NFFT;
plot(fVals,Px(1:NFFT/2),'b','LineSmoothing','on','LineWidth',1);
title('One Sided Power Spectral Density');
xlabel('Frequency (Hz)')
ylabel('PSD');
But then it doesn't give me what I expected:
FFT result:
zoom image has lots of noises:
and there is no information near 1.7Hz
Here is the graph from log domain using
semilogy(fVals,Px(1:NFFT));
It's pretty symmetric though:
I couldn't find anything wrong with my code. Do you have any solutions to easily extract the 1.7Hz from the walking pattern?
here is the link for the audio file in mat
https://www.dropbox.com/s/craof8qkz9n5dr1/walk_sound.mat?dl=0
Thank you very much!
Kai
I suggest you to forget about DFT approach since your signal is not appropriate for this type of analysis due to many reasons. Even by looking on the spectrum in range of frequencies that you are interested in, there is no easy way to estimate the peak:
Of course you could try with PSD/STFT and other funky methods, but this is an overkill. I can think of two, rather simple methods, for this task.
First one is based simply on the Auto Correlation Function.
Calculate the ACF
Define the minimum distance between them. Since you know that expected frequency is around 1.7Hz, then it corresponds to 0.58s. Let's make it 0.5s as the minimum distance.
Calculate the average distance between peaks found.
This gave me an approximate frequency of 1.72 Hz .
Second approach is based on the observation to your signal already has some peaks which are periodic. Therefore we can simply search for them using findpeaks function.
Define the minimum peak distance in a same way as before.
Define the minimum peak height. For example 10% of maximum peak.
Get the average difference.
This gave me an average frequency of 1.7 Hz.
Easy and fast method. There are obviously some things that can be improved, such as:
Refining thresholds
Finding both positive and negative peaks
Taking care of some missing peaks, i.e. due to low amplitude
Anyway that should get you started, instead of being stuck with crappy FFT and lazy semilogx.
Code snippet:
load walk_sound
fs = 48000;
dt = 1/fs;
x = walk_5(:,1);
x = x - mean(x);
N = length(x);
t = 0:dt:(N-1)*dt;
% FFT based
win = hamming(N);
X = abs(fft(x.*win));
X = 2*X(1:N/2+1)/sum(win);
X = 20*log10(X/max(abs(X)));
f = 0:fs/N:fs/2;
subplot(2,1,1)
plot(t, x)
grid on
xlabel('t [s]')
ylabel('A')
title('Time domain signal')
subplot(2,1,2)
plot(f, X)
grid on
xlabel('f [Hz]')
ylabel('A [dB]')
title('Signal Spectrum')
% Autocorrelation
[ac, lag] = xcorr(x);
min_dist = ceil(0.5*fs);
[pks, loc] = findpeaks(ac, 'MinPeakDistance', min_dist);
% Average distance/frequency
avg_dt = mean(gradient(loc))*dt;
avg_f = 1/avg_dt;
figure
plot(lag*dt, ac);
hold on
grid on
plot(lag(loc)*dt, pks, 'xr')
title(sprintf('ACF - Average frequency: %.2f Hz', avg_f))
% Simple peak finding in time domain
[pkst, loct] = findpeaks(x, 'MinPeakDistance', min_dist, ...
'MinPeakHeight', 0.1*max(x));
avg_dt2 = mean(gradient(loct))*dt;
avg_f2 = 1/avg_dt2;
figure
plot(t, x)
grid on
hold on
plot(loct*dt, pkst, 'xr')
xlabel('t [s]')
ylabel('A')
title(sprintf('Peak search in time domain - Average frequency: %.2f Hz', avg_f2))
Here's a nifty solution:
Take the absolute value of your raw data before taking the FFT. The data has a ton of high frequency noise that is drowning out whatever low frequency periodicity is present in the signal. The amplitude of the high frequency noise gets bigger every 1.7 seconds, and the increase in amplitude is visible to the eye, and periodic, but when you multiply the signal by a low frequency sine wave and sum everything you still end up with something close to zero. Taking the absolute value changes this, making those amplitude modulations periodic at low frequencies.
Try the following code comparing the FFT of the regular data with the FFT of abs(data). Note that I took a few liberties with your code, such as combining what I assume were the two stereo channels into a single mono channel.
x = (walk_5(:,1)+walk_5(:,2))/2; % Convert from sterio to mono
Fs = 48000; % sampling frquency
L=length(x); % length of sample
fVals=(0:L-1)*(Fs/L); % frequency range for FFT
walk5abs=abs(x); % Take the absolute value of the raw data
Xold=abs(fft(x)); % FFT of the data (abs in Matlab takes complex magnitude)
Xnew=abs(fft(walk5abs-mean(walk5abs))); % FFT of the absolute value of the data, with average value subtracted
figure;
plot(fVals,Xold/max(Xold),'r',fVals,Xnew/max(Xnew),'b')
axis([0 10 0 1])
legend('old method','new method')
[~,maxInd]=max(Xnew); % Index of maximum value of FFT
walkingFrequency=fVals(maxInd) % print max value
And plotting the FFT for both the old method and the new, from 0 to 10 Hz gives:
As you can see it detects a peak at about 1.686 Hz, and for this data, that's the highest peak in the FFT spectrum.

Is there anyway to find the density of histogram values in MATLAB?

I have 7 vectors ranged between 0 and 0.99. The number of entries in each vector is different, so it would be "unfair" to compare their histograms because there should be a direct correlation between a bin count and the number of entries, assuming the variables are spaced. I'm interested in plotting a smooth curve of the density of the values. So, for a vector a with say n values from 0 and 0.99, I would like an x-axis of 0 to 0.99, with the y-axis being the probabilities associated with those values.
Any ideas or insight?
Not sure what kind of smoothing you want, but some idea how to start:
%some example vectors of different length
p=[10,100,1000,10000];
D=arrayfun(#(x)(rand(x,1)),p,'uni',false);
%defining the range
support=[0:.1:1];
%make sure we don't miss a value
esupport=support;
esupport(end+1)=inf;
%define a function which uses histc to calculate the emperical probability for each bin
epdf_bin=#(x)histc(x,esupport)/numel(x);
%evaluate emperical probability
E=cellfun(epdf_bin,D,'uni',false);
M=cat(2,E{:});
%plot
bar(M);
%print legend
legend(arrayfun(#num2str,p,'uni',false));
%fix x axis labels
set(gca,'XTick',.5:numel(support))
set(gca,'XTickLabel',support)
[h,b] = hist( my_data, Nbins );
plot( b, h / sum(h) );

I need to calculate the average power spectral density after passing packets through a butterworth filter using MATLAB

I am trying to generate 100 packets(row vectors each of size 8192) of random bits(1,-1),filter them using butterworth filter and then plot their average power spectral density. I have to do this using MATLAB.My o/p should be a filtered sinc with a very sharp peak. When I use this code for smaller sized packets say 100 it works. But for 8192 it doesn't. I want someone to review my code for errors please.
%generates a random square matrix of 8192x8192
n=rand(8192);
%initiates a row vector of 64 zeros
B=zeros(1,64);
%makes a butterworth(lowpass) filter
[num,den]=butter(20,.6);
%two for loops to generate 100 row vectors(packets) each of size 8192 that
%give 1 for any value greater than 0.5 and vice versa
for c=1:100
for k=1:8192
if n(c,k)>=0.5
n(c,k)=1;
else
n(c,k)=-1;
end
%filter the generated vectors and calculate average power spectral density
x=filter(num,den,n(c,:));
A=fftshift(fft(x,64));
psd=A.*conj(A);
B=B+psd;
end
end
plot(B./100)
xlabel 'Frequency', ylabel 'Average Power Spectral Density'
I saw two things:
First: I think the second end should be in a different position:
%generates a random square matrix of 8192x8192
n=rand(8192);
%initiates a row vector of 64 zeros
B=zeros(1,64);
%makes a butterworth(lowpass) filter
[num,den]=butter(20,.6);
%two for loops to generate 100 row vectors(packets) each of size 8192 that
%give 1 for any value greater than 0.5 and vice versa
for c=1:100
for k=1:8192
if n(c,k)>=0.5
n(c,k)=1;
else
n(c,k)=-1;
end
end
%filter the generated vectors and calculate average power spectral density
x=filter(num,den,n(c,:));
A=fftshift(fft(x,64));
psd=A.*conj(A);
B=B+psd;
end
plot(B./100)
xlabel 'Frequency', ylabel 'Average Power Spectral Density'
Second: It takes forever to get the +1 and -1 values using your code. You should use the indexing operators that Matlab provides:
%generates a random square matrix of 8192x8192
n=rand(8192);
n(n>=0.5)=1;
n(n<0.5)=-1;
%initiates a row vector of 64 zeros
B=zeros(1,64);
%makes a butterworth(lowpass) filter
[num,den]=butter(20,.6);
%two for loops to generate 100 row vectors(packets) each of size 8192 that
%give 1 for any value greater than 0.5 and vice versa
for c=1:100
%filter the generated vectors and calculate average power spectral density
x=filter(num,den,n(c,:));
A=fftshift(fft(x,64));
psd=A.*conj(A);
B=B+psd;
end
plot(B./100)
xlabel 'Frequency', ylabel 'Average Power Spectral Density'
Take absolute values of the filtered data
Replace
x=filter(num,den,n(c,:));
with
x=abs(filter(num,den,n(c,:)));
gives a sharp peak that can be seen here

Relative Frequency Histograms and Probability Density Functions

The function called DicePlot simulates rolling 10 dice 5000 times.
The function calculates the sum of values of the 10 dice of each roll, which will be a 1 ⇥ 5000 vector, and plot relative frequency histogram with edges of bins being selected in where each bin in the histogram represents a possible value of for the sum of the dice.
The mean and standard deviation of the 1 ⇥ 5000 sums of dice values will be computed, and the probability density function of normal distribution (with the mean and standard deviation computed) on top of the relative frequency histogram will be plotted.
Below is my code so far - What am I doing wrong? The graph shows up but not the extra red line on top? I looked at answers like this, and I don't think I'll be plotting anything like the Gaussian function.
% function[]= DicePlot()
for roll=1:5000
diceValues = randi(6,[1, 10]);
SumDice(roll) = sum(diceValues);
end
distr=zeros(1,6*10);
for i = 10:60
distr(i)=histc(SumDice,i);
end
bar(distr,1)
Y = normpdf(X)
xlabel('sum of dice values')
ylabel('relative frequency')
title(['NumDice = ',num2str(NumDice),' , NumRolls = ',num2str(NumRolls)]);
end
It is supposed to look like
But it looks like
The red line is not there because you aren't plotting it. Look at the documentation for normpdf. It computes the pdf, it doesn't plot it. So you problem is how do you add this line to the plot. The answer to that problem is to google "matlab hold on".
Here's some code to get you going in the right direction:
% Normalize your distribution
normalizedDist = distr/sum(distr);
bar(normalizedDist ,1);
hold on
% Setup your density function using the mean and std of your sample data
mu = mean(SumDice);
stdv = std(SumDice);
yy = normpdf(xx,mu,stdv);
xx = linspace(0,60);
% Plot pdf
h = plot(xx,yy,'r'); set(h,'linewidth',1.5);