I was wondering how is it possible to detect new peaks within an FFT plot in Python.
let's say i have this simple Plot:
And i want to automatically measure the 'Similarity' or the Peaks location within a noisy Signal, i have tried to use the cosine Similarity but my real Signal is way too noisy, and with even if i add a new peak to the signal, i keep getting a Cosine of 0.9 since it's only one peak.
This is an example of my real signal, and i also have the problem that my signal can be shiffted within the measures, so i can't get a stable frequency array they can be within a window of +/- 100 Hz :
This the code that used for the first Plot :
import numpy as np
from pylab import *
import scipy.fftpack
# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.7*np.sin(30.0 * 2.0*np.pi*x)+ 0.5*np.sin(10.0 * 2.0*np.pi*x)
y2 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.2*np.sin(60.0 * 2.0*np.pi*x)+ 0.4*np.sin(40.0 * 2.0*np.pi*x)
yf1 = scipy.fftpack.fft(y1)
yf2 = scipy.fftpack.fft(y2)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
fig, ax = plt.subplots()
plot(xf, 2.0/N * np.abs(yf1[:N/2]))
plot(xf, 2.0/N * np.abs(yf2[:N/2]))
xlabel('Freq (Hz)',fontsize=16,weight='bold')
ylabel('|Y(freq)|',fontsize=16,weight='bold')
ax = gca()
fontsize = 14
for tick in ax.xaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
tick.label1.set_fontweight('bold')
for tick in ax.yaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
tick.label1.set_fontweight('bold')
grid(True)
show()
def cosine_similarity(v1,v2):
"compute cosine similarity of v1 to v2: (v1 dot v2)/{||v1||*||v2||)"
sumxx, sumxy, sumyy = 0, 0, 0
for i in range(len(v1)):
x = v1[i]; y = v2[i]
sumxx += x*x
sumyy += y*y
sumxy += x*y
return sumxy/math.sqrt(sumxx*sumyy)
print 'Cosine Similarity', cosine_similarity(2.0/N * np.abs(yf1[:N/2]),2.0/N * np.abs(yf2[:N/2]))
I have also though of setting a threshold, but sometime the peaks within the real signal can be smaller than the pre-defined Threshold.
Any ideas ?
There are many ways to find peaks, and even to interpolate their sub-sample location.
Once you have the peaks, just check if you find a new one.
You can use the peakutils package to find the peaks. You can set there the threshold and minimum distance between peaks.
import numpy as np
from pylab import *
import scipy.fftpack
# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y1 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.7*np.sin(30.0 * 2.0*np.pi*x)+ 0.5*np.sin(10.0 * 2.0*np.pi*x)
y2 = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)+ 0.2*np.sin(60.0 * 2.0*np.pi*x)+ 0.4*np.sin(40.0 * 2.0*np.pi*x)
yf1 = scipy.fftpack.fft(y1)
yf2 = scipy.fftpack.fft(y2)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
v1 = 2.0/N * np.abs(yf1[:N/2])
v2 = 2.0/N * np.abs(yf2[:N/2])
# Find peaks
import peakutils
peaks_ind1 = peakutils.indexes(v1, thres=0.2, min_dist=5)
peaks_ind2 = peakutils.indexes(v2, thres=0.2, min_dist=5)
dist_th_for_new_peaks = 3
new_peaks = []
for p in peaks_ind2:
found_new_peak = np.all(np.abs(p - peaks_ind1) > dist_th_for_new_peaks)
if found_new_peak:
new_peaks.append(p)
print("New Peak!! - %d" % p)
fig, ax = plt.subplots()
plot(xf, v1, color='blue')
plot(xf, v2, color='green')
for p in peaks_ind1:
ax.scatter(xf[p], v1[p], s=40, marker='s', color='blue', label='v1')
for p in peaks_ind2:
ax.scatter(xf[p], v2[p], s=40, marker='s', color='green', label='v2')
for p in new_peaks:
ax.scatter(xf[p], v2[p], s=40, marker='s', color='red', label='new peaks')
xlabel('Freq (Hz)',fontsize=16,weight='bold')
ylabel('|Y(freq)|',fontsize=16,weight='bold')
ax = gca()
fontsize = 14
for tick in ax.xaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
tick.label1.set_fontweight('bold')
for tick in ax.yaxis.get_major_ticks():
tick.label1.set_fontsize(fontsize)
tick.label1.set_fontweight('bold')
ax.set_xlim([0,400])
ax.set_ylim([0,0.8])
grid(True)
show()
The red squares are the new peaks that were found in the green signal:
Related
I have code that looks like this:
import matplotlib.pyplot as plt
import numpy as np
from nfft import nfft
# number of sample points
N = 400
# Simulated non-uniform data
x = np.linspace(0.0, 1 / 2, N) + np.random.random((N)) * 0.001
y = np.sin(50.0 * 2.0 * np.pi * x) + 0.5 * np.sin(80.0 * 2.0 * np.pi * x)
yf = np.abs(nfft(x, y))
fig, axs = plt.subplots(1)
fig_f, axs_f = plt.subplots(1)
axs.plot(x, y, '.', color='red')
axs_f.plot(x, yf, color='red')
How do I convert the values on the second graph to represent frequency?
The use of the nfft module is not required, answers using pynfft or scipy will be greatly appreciated.
See also:
How do I obtain the frequencies of each value in an FFT?
The following seems to work. Notice the line inserted before graphing the Fourier transform, to generate the frequencies, and that we graph N/2 of the data.
import matplotlib.pyplot as plt
import numpy as np
from nfft import nfft
# number of sample points
N = 400
# Simulated non-uniform data
x = np.linspace(0.0,0.5-0.02, N) + np.random.random((N)) * 0.001
print(x)
print( 'random' )
print( np.random.random((N)) * 0.001 )
y = np.sin(50.0 * 2.0 * np.pi * x) + 0.5 * np.sin(80.0 * 2.0 * np.pi * x)
yf = np.abs(nfft(x, y))
fig, axs = plt.subplots(1)
fig_f, axs_f = plt.subplots(1)
axs.plot(x, y, '.', color='red')
xf = np.fft.fftfreq(N,1./N)
axs_f.plot(xf[:int(N/2)], yf[:int(N/2)], color='red')
plt.show()
Output:
Supposing that I have following signal:
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(100.0 * 2.0*np.pi*x) + 0.2*np.sin(200 * 2.0*np.pi*x)
how can I filter out in example 100Hz using Band-stop filter in Python? In this signal there are peaks at 50Hz, 100Hz and 200Hz. It would be helpful it it could be visualized using FFT in order to confirm that this frequency has been filtered correctly.
Basing on answers from:
Plotting a Fast Fourier Transform in Python
and:
Bandstop filter
I wrote following code:
import pandas as pd
import time
from scipy.signal import lfilter
import matplotlib.pyplot as plt
import scipy
import numpy as np
# In the below lines data are being filtered using Bandstop filter
print("Filtering using Bandstop filter...")
start_filtering_bandstop = time.time()
# Define filtering parameters:
order = 2
fs = 800.0 # sample rate, Hz
lowcut = 90 # desired cutoff frequency of the filter, Hz
highcut = 110
# Define plots
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7))
# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / fs
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(100.0 * 2.0*np.pi*x) + 0.2*np.sin(200 * 2.0*np.pi*x) # You can put there pandas series too...
ax1.plot(x, y, label='Signal before filtering')
print("Calculating FFT, please wait...")
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
ax1.set_title('Signal')
ax2.set_title('FFT')
ax2.plot(xf, 2.0/N * np.abs(yf[:N//2]), label='Before filtering')
def butter_bandstop_filter(data, lowcut, highcut, fs, order):
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = scipy.signal.butter(order, [low, high], btype='bandstop')#, fs, )
y = lfilter(b, a, data)
return y
print("Filtering signal, please wait...")
signal_filtered = butter_bandstop_filter(y, lowcut, highcut, fs, order)
ax1.plot(x, signal_filtered, label='Signal after filtering')
ax1.set(xlabel='X', ylabel='Signal values')
ax1.legend() # Don't forget to show the legend
ax1.set_xlim([0,0.8])
ax1.set_ylim([-1.5,2])
# Number of samplepoints
N = len(signal_filtered)
# sample spacing
T = 1.0 / fs
x = np.linspace(0.0, N*T, N)
y = signal_filtered
print("Calculating FFT after filtering, please wait...")
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
# Plot axes...
ax2.plot(xf, 2.0/N * np.abs(yf[:N//2]), label='After filtering')
ax2.set(xlabel='Frequency [Hz]', ylabel='Magnitude')
ax2.legend() # Don't forget to show the legend
plt.savefig('FFT_after_bandstop_filtering.png', bbox_inches='tight', dpi=300) # If dpi isn't set, the script execution will be faster
# Alternatively for immediate showing of plot:
# plt.show()
plt.close()
end_filtering_bandstop = time.time()
print("Data filtered using Bandstop filter in",round(end_filtering_bandstop - start_filtering_bandstop,2),"seconds!")
and obtained following plots:
As we can see, the 100Hz has been filtered out using band-stop filter.
Why magnitude for frequency 50 Hz decreased from 1 to 0.7 after Fast Fourier Transform?
I want to remove one frequency (one peak) from signal and plot my function without it. After fft I found frequency and amplitude and I am not sure what I need to do now. For example I want to remove my highest peak (marked with red dot on plot).
import numpy as np
import matplotlib.pyplot as plt
# create data
N = 4097
T = 100.0
t = np.linspace(-T/2,T/2,N)
f = np.sin(50.0 * 2.0*np.pi*t) + 0.5*np.sin(80.0 * 2.0*np.pi*t)
#plot function
plt.plot(t,f,'r')
plt.show()
# perform FT and multiply by dt
dt = t[1]-t[0]
ft = np.fft.fft(f) * dt
freq = np.fft.fftfreq(N, dt)
freq = freq[:N/2+1]
amplitude = np.abs(ft[:N/2+1])
# plot results
plt.plot(freq, amplitude,'o-')
plt.legend(('numpy fft * dt'), loc='upper right')
plt.xlabel('f')
plt.ylabel('amplitude')
#plt.xlim([0, 1.4])
plt.plot(freq[np.argmax(amplitude)], max(amplitude), 'ro')
print "Amplitude: " + str(max(amplitude)) + " Frequency: " + str(freq[np.argmax(amplitude)])
plt.show()
One option is to transform the signal to the frequency domain then remove the selected frequency.
import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import rfft, irfft, fftfreq, fft
# Number of samplepoints
N = 500
# sample spacing
T = 0.1
x = np.linspace(0.0, (N-1)*T, N)
# x = np.arange(0.0, N*T, T) # alternate way to define x
y = 5*np.sin(x) + np.cos(2*np.pi*x)
yf = fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N//2)
#fft end
f_signal = rfft(y)
W = fftfreq(y.size, d=x[1]-x[0])
cut_f_signal = f_signal.copy()
cut_f_signal[(W>0.6)] = 0 # filter all frequencies above 0.6
cut_signal = irfft(cut_f_signal)
# plot results
f, axarr = plt.subplots(1, 3, figsize=(9, 3))
axarr[0].plot(x, y)
axarr[0].plot(x,5*np.sin(x),'g')
axarr[1].plot(xf, 2.0/N * np.abs(yf[:N//2]))
axarr[1].legend(('numpy fft * dt'), loc='upper right')
axarr[1].set_xlabel("f")
axarr[1].set_ylabel("amplitude")
axarr[2].plot(x,cut_signal)
axarr[2].plot(x,5*np.sin(x),'g')
plt.show()
You can design a bandstop filter:
from scipy import signal
wc = freq[np.argmax(amplitude)] / (0.5 / dt)
wp = [wc * 0.9, wc / 0.9]
ws = [wc * 0.95, wc / 0.95]
b, a = signal.iirdesign(wp, ws, 1, 40)
f = signal.filtfilt(b, a, f)
I would like to draw a sphere with points on its surface using Matplotlib. These points shall be connected by a spiral that spirals from one side of the sphere to the other.
To clarify this a little bit the plot should more or less look like this:
Has anyone a tip about how to do this?
Need to know parameters of spiral, formula or set of points.
However I post a code to plot a line with markers on a sphere for your start:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_aspect('equal')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = 1 * np.outer(np.cos(u), np.sin(v))
y = 1 * np.outer(np.sin(u), np.sin(v))
z = 1 * np.outer(np.ones(np.size(u)), np.cos(v))
elev = 10.
rot = 80. / 180. * np.pi
ax.plot_surface(x, y, z, rstride=1, cstride=1, color='y', linewidth=0, alpha=0.5)
# plot lines in spherical coordinates system
a = np.array([-np.sin(elev / 180 * np.pi), 0, np.cos(elev / 180 * np.pi)])
b = np.array([0, 1, 0])
b = b * np.cos(rot) + np.cross(a, b) * np.sin(rot) + a * np.dot(a, b) * (1 - np.cos(rot))
ax.plot(np.sin(u),np.cos(u),0,color='r', linestyle = '-', marker='o', linewidth=2.5)
ax.view_init(elev = elev, azim = 0)
plt.show()
I'd like to plot pulse propagation in such a way at each step, it plots the pulse shape. In other words, I want a serie of x-z plots, for each values of y. Something like this (without color):
How can I do this using matplotlib (or Mayavi)? Here is what I did so far:
def drawPropagation(beta2, C, z):
""" beta2 in ps / km
C is chirp
z is an array of z positions """
T = numpy.linspace(-10, 10, 100)
sx = T.size
sy = z.size
T = numpy.tile(T, (sy, 1))
z = numpy.tile(z, (sx, 1)).T
U = 1 / numpy.sqrt(1 - 1j*beta2*z * (1 + 1j * C)) * numpy.exp(- 0.5 * (1 + 1j * C) * T * T / (1 - 1j*beta2*z*(1 + 1j*C)))
fig = pyplot.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
surf = ax.plot_wireframe(T, z, abs(U))
Change to:
ax.plot_wireframe(T, z, abs(U), cstride=1000)
and call:
drawPropagation(1.0, 1.0, numpy.linspace(-2, 2, 10))
will create the following graph:
If you need the curve been filled with white color:
import numpy
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot
from matplotlib.collections import PolyCollection
def drawPropagation(beta2, C, z):
""" beta2 in ps / km
C is chirp
z is an array of z positions """
T = numpy.linspace(-10, 10, 100)
sx = T.size
sy = z.size
T = numpy.tile(T, (sy, 1))
z = numpy.tile(z, (sx, 1)).T
U = 1 / numpy.sqrt(1 - 1j*beta2*z * (1 + 1j * C)) * numpy.exp(- 0.5 * (1 + 1j * C) * T * T / (1 - 1j*beta2*z*(1 + 1j*C)))
fig = pyplot.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
U = numpy.abs(U)
verts = []
for i in xrange(T.shape[0]):
verts.append(zip(T[i, :], U[i, :]))
poly = PolyCollection(verts, facecolors=(1,1,1,1), edgecolors=(0,0,1,1))
ax.add_collection3d(poly, zs=z[:, 0], zdir='y')
ax.set_xlim3d(numpy.min(T), numpy.max(T))
ax.set_ylim3d(numpy.min(z), numpy.max(z))
ax.set_zlim3d(numpy.min(U), numpy.max(U))
drawPropagation(1.0, 1.0, numpy.linspace(-2, 2, 10))
pyplot.show()