import numpy as np
import matplotlib.pyplot as plt
from numpy.fft import fft, fftfreq, ifft
#Varibles
A = 1
f = 10
t = 1/f
nss = 10
fs = nss*f
ts = 1/fs
cycles = 1
#CREATING SINE WAVE
t1 = np.arange(0,cycles*t+ts,ts)
x = A*np.sin(2*np.pi*f*t1)
#PLOTTING SINE WAVE
plt.figure(1)
plt.subplot(2,2,1)
plt.plot(t1,x, label = 'Sine Wave')
plt.legend()
plt.grid(True)
#FFT CALCULATIONS
freqs = fftfreq(nss)
fft_vals = fft(x,n=512)
fft_vals2 = np.fft.fftshift(fft_vals)
freqs = np.linspace(-fs/2,fs/2,len(fft_vals2))
#mask = freqs > 0
#fft_theo = 2.0*np.abs(fft_vals/nss)
#Plotting fft values
plt.subplot(2,1,2)
plt.plot(freqs, fft_vals2, label= "Raw fft Values")
plt.legend()
plt.grid(True)
so i have this code for one sine wave, that finds the FFT of sine wave and plots it however i want to be plot multiple sine waves/ the summation and display the FFT of that. I'm mainly confused on how you would code it for a set amount of cycles, rather than a random amount of cycles. This is the output i would like however instead of multiple cycles on the original signal i would like to plot a set amount of cycles that is declarable at the start of the code
Not sure if this is what you're looking for, but you can use a for loop. Here, the loop represents cycle=1,2,3,4,...,9,10, but you can change it to modify whatever variables you want:
import numpy as np
import matplotlib.pyplot as plt
from numpy.fft import fft, fftfreq, ifft
fig, ax = plt.subplots(10, 2, figsize=(40,40))
for i in range(10):
#Varibles
A = 1
f = 10
t = 1/f
nss = 10
fs = nss*f
ts = 1/fs
cycles = i + 1
#CREATING SINE WAVE
t = np.arange(0,cycles*t+ts,ts)
x = A*np.sin(2*np.pi*f*t)
#PLOTTING SINE WAVE
ax[i][0].plot(t,x, label='Sine Wave')
ax[i][0].grid(True)
#FFT CALCULATIONS
freqs = fftfreq(nss)
fft_vals = fft(x,n=512)
fft_vals2 = np.fft.fftshift(fft_vals)
freqs = np.linspace(-fs/2,fs/2,len(fft_vals2))
#mask = freqs > 0
#fft_theo = 2.0*np.abs(fft_vals/nss)
#Plotting fft values
ax[i][1].plot(freqs, fft_vals2, label= "Raw fft Values")
ax[i][1].grid(True)
fig.show()
Related
I have a histogram with a fitted gaussian curve, and I'd like to find and calculate the full width at half maximum for this curve. The data used in this code is a single column from a dataframe. I've included a link to an image of my plot. I'm new to python and have no idea how to do this.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
def gaussian(n, mean, amplitude, standard_deviation):
return amplitude * np.exp( - (n - mean)**2 / (2*standard_deviation ** 2))
n = df_OI_CMC['Area_1_Micrometers']
#Plot Histogram 1
bin_heights, bin_borders, _ = plt.hist(n, bins =
(0,1,5,10,25,50,75,100,125,150,200,250,500,750,1000,2500,5000,7500,10000), label='histogram',
edgecolor ='white')
bin_widths = np.diff(bin_borders)
bin_centers = bin_borders[:-1] + np.diff(bin_borders) / 2
#Generate enough x values to make the curves look smooth
n_interval_for_fit = np.linspace(bin_borders[0], bin_borders[-1], 10000)
n_interval_for_fit_2 = np.linspace(bin_borders[0], bin_borders_2[-1], 10000)
#CurveFit to Histogram
popt, _ = curve_fit(gaussian, bin_centers, bin_heights, p0=[-44.0543433,
1480.64682738,68.86641026])
plt.rcParams["figure.figsize"] = [12,12]
plt.plot(n_interval_for_fit, gaussian(n_interval_for_fit, *popt), label='fit')
plt.ylim([0, 1500])
plt.xlim([-10,1000])
#for months, I have been trying to fit a model to x and y data in python, but could not #achieve that. The aim is to estimate P1 and P2. See one of codes that fails below.
import numpy as np
from numpy import*
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
#IMPORTING DATA
Xdata = np.array([2.0,4.0,6.0,8.0,10.0,12.0,14.0,16.0,18.0,20.0,22.0,24.0])
Ydata = np.array([0.19,1.25,2.53,4.27,7.02,9.03,11.05,14.85,15.23,17.32,18.71,19.91])
#Plotting data
plt.figure(figsize=(6,3))
plt.xlim(0.0,25.0)
plt.ylim(0.0,25.0)
plt.xlabel("Time (hour)")
plt.ylabel("Data")
plt.title("plotting Data")
plt.plot(Xdata,Ydata, 'or',ms='10', label = 'Data')
#definining fitting function
def Q1(X,P1,P2):
lambda1=sqrt(X/P2)
top = P1*CL*A
bottom = lambda1*sinh(lambda1)
return top/bottom/X
CL = 1.0 # umol/mL == umol/cm^3
A = 1.0 # cm^2
#Optiminization
popt,pcov = curve_fit(Q1,Xdata,Ydata)
print(popt)
#plot the fitted function
Xstart = 1
Xstop = 24
increment = 0.1
xfit = np.arange(Xstart,Xstop,increment)
yfit = Q1(xfit,*popt)
plt.plot(xfit,yfit,'b',label = "fit")
plt.legend()
The following python program plots a sinusoid:
import matplotlib.pyplot as plt
import numpy as np
# Canvas
plt.style.use("ggplot")
# Frequency, Oscillations & Range
f = int(input("Enter frequency: "))
n_o = int(input("Enter number of oscillations: "))
t_max = n_o/f
t = np.linspace(0, t_max, 1000)
# Sine
y_sin = np.sin(2*np.pi*f*t)
# Setting subplots on separate axes
fig, axs = plt.subplots(2, 1, constrained_layout = True)
# Sine axis
axs[0].plot(t, y_sin, color = "firebrick", label = "sin({}Hz)".format(f))
axs[0].axhline(y = 0, color = "grey", linestyle = "dashed", label = "y = 0")
axs[0].legend(loc = "lower left", frameon = True, fancybox = True,
shadow = True, facecolor = "white")
# Title
axs[0].set_title("Sine")
axs[0].set_xlabel("Time(s)")
axs[0].set_ylabel("Amplitude")
# Axis Limits
axs[0].axis([-0.05*t_max, t_max+0.05*t_max, -1.5, 1.5])
plt.show()
How can i plot the Fourier transform of this frequency in the second subplot? I have seen various examples but they only work with small frequencies, whereas i'm working with frequencies above 100 Hz. Thanks.
By correctly applying FFT on your signal you should be just fine:
# FFT
# number of samples
N = len(t)
# time step
dt = t[1]-t[0]
# max number of harmonic to display
H_max = 5
xf = np.linspace(0.0, 1.0/(2.0*dt), N//2)
yf = np.fft.fft(y_sin)
axs[1].plot(xf, (2/N)*np.abs(yf[:N//2]))
axs[1].set_xlim([0, H_max*f])
axs[1].set_xlabel('f (Hz)')
axs[1].set_ylabel('$||H_i||_2$')
which gives for inputs f=100 and n_o=3:
Hope this helps.
I am a newbie in Signal Processing. In here, I want to ask how to get FFT coeffients from FFT from in python. This is the example of my code:
from scipy.fftpack import fft
# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
import matplotlib.pyplot as plt
plt.plot(xf, 2.0/N * np.abs(yf[0:N/2]))
plt.grid()
plt.show()
Hmm I don't really know about signal processing either but maybe this works:
from scipy.signal import argrelmax
f = xf[scipy.signal.argrelmax(yf[0:N/2])]
Af = np.abs(yf[argrelmax(yf[0:N/2])])
Quoting #hotpaw, in this similar answer:
"The real and imaginary arrays, when put together, can represent a complex array. Every complex element of the complex array in the frequency domain can be considered a frequency coefficient, and has a magnitude sqrt(RR + II))".
So, the coefficients are the complex elements in the array returned by the fft function. Also, it is important to play with the size (the number) of the bins for the FFT function. It would make sense to test a bunch of values and pick the one that makes more sense to your application. Often, it is in the same magnitude of the number of samples. This was as assumed by most of the answers given, and produces great and reasonable results. In case one wants to explore that, here is my code version:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
fig = plt.figure(figsize=[14,4])
N = 600 # Number of samplepoints
Fs = 800.0
T = 1.0 / Fs # N_samps*T (#samples x sample period) is the sample spacing.
N_fft = 80 # Number of bins (chooses granularity)
x = np.linspace(0, N*T, N) # the interval
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x) # the signal
# removing the mean of the signal
mean_removed = np.ones_like(y)*np.mean(y)
y = y - mean_removed
# Compute the fft.
yf = scipy.fftpack.fft(y,n=N_fft)
xf = np.arange(0,Fs,Fs/N_fft)
##### Plot the fft #####
ax = plt.subplot(121)
pt, = ax.plot(xf,np.abs(yf), lw=2.0, c='b')
p = plt.Rectangle((Fs/2, 0), Fs/2, ax.get_ylim()[1], facecolor="grey", fill=True, alpha=0.75, hatch="/", zorder=3)
ax.add_patch(p)
ax.set_xlim((ax.get_xlim()[0],Fs))
ax.set_title('FFT', fontsize= 16, fontweight="bold")
ax.set_ylabel('FFT magnitude (power)')
ax.set_xlabel('Frequency (Hz)')
plt.legend((p,), ('mirrowed',))
ax.grid()
##### Close up on the graph of fft#######
# This is the same histogram above, but truncated at the max frequence + an offset.
offset = 1 # just to help the visualization. Nothing important.
ax2 = fig.add_subplot(122)
ax2.plot(xf,np.abs(yf), lw=2.0, c='b')
ax2.set_xticks(xf)
ax2.set_xlim(-1,int(Fs/6)+offset)
ax2.set_title('FFT close-up', fontsize= 16, fontweight="bold")
ax2.set_ylabel('FFT magnitude (power) - log')
ax2.set_xlabel('Frequency (Hz)')
ax2.hold(True)
ax2.grid()
plt.yscale('log')
Output:
I have code that draws from a gaussian in 1D:
import numpy as np
from scipy.stats import norm
from scipy.optimize import curve_fit
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import gauss
# Beginning in one dimension:
mean = 0; Var = 1; N = 1000
scatter = np.random.normal(mean,np.sqrt(Var),N)
scatter = np.sort(scatter)
mu,sigma = norm.fit(scatter)
I obtain mu and sigma using norm.fit()
Now I'd like to obtain my parameters using
xdata = np.linspace(-5,5,N)
pop, pcov = curve_fit(gauss.gauss_1d,xdata,scatter)
The problem is I don't know how to map my scattered points (drawn from a 1D gaussian) to the x-line in order to use curve_fit.
Also, suppose I simply use and mu and sigma as earlier.
I plot using:
n, bins, patches = plt.hist(scatter,50,facecolor='green')
y = 2*max(n)*mlab.normpdf(bins,mu,sigma)
l = plt.plot(bins,y,'r--')
plt.xlabel('x-coord')
plt.ylabel('Occurrences')
plt.grid(True)
plt.show()
But I have to guess the amplitude as 2*max(n). It works but it's not robust. How can I find the amplitude without guessing?
To avoid guessing the amplitude, call hist() with normed=True, then the amplitude corresponds to normpdf().
For doing a curve fit, I suggest to use not the density but the cumulative distribution: Each sample has a height of 1/N, which successively sum up to 1. This has the advantage that you don't need to group samples in bins.
import numpy as np
from scipy.stats import norm
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
# Beginning in one dimension:
mean = 0; Var = 1; N = 100
scatter = np.random.normal(mean,np.sqrt(Var),N)
scatter = np.sort(scatter)
mu1,sigma1 = norm.fit(scatter) # classical fit
scat_sum = np.cumsum(np.ones(scatter.shape))/N # cumulative samples
[mu2,sigma2],Cx = curve_fit(norm.cdf, scatter, scat_sum, p0=[0,1]) # curve fit
print(u"norm.fit(): µ1= {:+.4f}, σ1={:.4f}".format(mu1, sigma1))
print(u"curve_fit(): µ2= {:+.4f}, σ2={:.4f}".format(mu2, sigma2))
fg = plt.figure(1); fg.clf()
ax = fg.add_subplot(1, 1, 1)
t = np.linspace(-4,4, 1000)
ax.plot(t, norm.cdf(t, mu1, sigma1), alpha=.5, label="norm.fit()")
ax.plot(t, norm.cdf(t, mu2, sigma2), alpha=.5, label="curve_fit()")
ax.step(scatter, scat_sum, 'x-', where='post', alpha=.5, label="Samples")
ax.legend(loc="best")
ax.grid(True)
ax.set_xlabel("$x$")
ax.set_ylabel("Cumulative Probability Density")
ax.set_title("Fit to Normal Distribution")
fg.canvas.draw()
plt.show()
prints
norm.fit(): µ1= +0.1534, σ1=1.0203
curve_fit(): µ2= +0.1135, σ2=1.0444
and plots