Discrete Fourier Transform using scipy.fftpack - python

I have a question regarding the scipy.fft package, and how I can use this to generate a Fourier transform of a pulse.
I am trying to do this for an arbitrary pulse in the future, but I wanted to make it as simple as possible so I have been attempting to FFT a time domain rectangular pulse, which should produce a frequency domain Sinc function. You can see more information here: https://en.wikipedia.org/wiki/Rectangular_function
From my understanding of FFTs, a signal needs to be repeating and periodic, so in a situation like a rectangular pulse, I will need to shift this in order for the FFT algorithm to 'see' it as a symmetric pulse.
My problem arises when I observe the real and imaginary components of my Fourier transform. I expect a rectangular pulse (As it is a real and even function) to be real and symmetrical. However, I notice that there are imaginary components, even when I don't use complex numbers.
My approach to this has been the following:
Define my input pulse
Shift my input pulse so that the function is symmetric around the
origin
Fourier transform this and shift it so negative frequencies are shown
first
Separate imaginary and real components
Plot amplitude and phase of my frequencies
I have attached graphs showing what I have attempted and outlining these steps.
This is my first question on stack overflow so I am unable to post images, but a link to the imgur album is here: https://imgur.com/a/geufY
I am having trouble with the phase information of my frequency, from the images in the imgur folder, I have a linearly increasing phase difference, which should in the ideal case be flat.
I expect it is a problem with how I am shifting my input pulse, and have tried several other methods (I can post them if that would help)
Any help with this would be much appreciated, I have been pouring over examples but these mostly refer to infinite sinusoidal functions rather than pulses.
My Code is shown below:
import numpy as np
import scipy.fftpack as fft
import matplotlib.pyplot as plt
'''Numerical code starts here'''
#Define number of points and time/freq arrays
npts = 2**12
time_array = np.linspace(-1, 1, npts)
freq_array = fft.fftshift(fft.fftfreq(len(time_array), time_array[1]-time_array[0]))
#Define a rectangular pulse
pulse = np.zeros(npts)
pulse_width = 100
pulse[npts/2 - pulse_width/2:npts/2 + pulse_width/2] = 1
#Shift the pulse so that the function is symmetrical about origin
shifted_pulse = fft.fftshift(pulse)
#Calculate the fourier transform of the shifted pulse
pulse_frequencies = fft.fftshift(fft.fft(shifted_pulse))
'''Plotting code starts here'''
#Plot the pulse in the time domain
fig, ax = plt.subplots()
ax.plot(time_array, pulse)
ax.set_title('Time domain pulse', fontsize=22)
ax.set_ylabel('Field Strength', fontsize=22)
ax.set_xlabel('Time', fontsize=22)
#Plot the shifted pulse in the time domain
fig, ax = plt.subplots()
ax.plot(time_array, shifted_pulse)
ax.set_title('Shifted Time domain pulse', fontsize=22)
ax.set_ylabel('Field Strength', fontsize=22)
ax.set_xlabel('Time', fontsize=22)
#Plot the frequency components in the frequency domain
fig, ax = plt.subplots()
ax.plot(freq_array, np.real(pulse_frequencies), 'b-', label='real')
ax.plot(freq_array, np.imag(pulse_frequencies), 'r-', label='imaginary')
ax.set_title('Pulse Frequencies real and imaginary', fontsize=22)
ax.set_ylabel('Spectral Density', fontsize=22)
ax.set_xlabel('Frequency', fontsize=22)
ax.legend()
#Plot the amplitude and phase of the frequency components in the frequency domain
fig, ax = plt.subplots()
ax.plot(freq_array, np.abs(pulse_frequencies), 'b-', label='amplitude')
ax.plot(freq_array, np.angle(pulse_frequencies), 'r-', label='phase')
ax.set_title('Pulse Frequencies intenisty and phase', fontsize=22)
ax.set_ylabel('Spectral Density', fontsize=22)
ax.set_xlabel('Frequency', fontsize=22)
ax.legend()
plt.show()

Related

Autocorrelation plot intuitive

I am analyzing a time series dataset and I used seasonal_decompose function in statsmodel library to obtain trend and seasonal behavior. I obtained the autocorrelation plot and the decomposition of the time-series provided should provide a “remainder” component that should be uncorrelated. By observing the autocorrelation plot how do we say that auto-correlation function indicate that the remainder is indeed uncorrelated?
I am attaching the code I used to obtain autocorrelation plot and the plot obtained.
fig, ax = plt.subplots(figsize=(20, 5))
plot_acf(data, ax=ax)
plt.show()
Autocorrelation_plot
if the results of auto correlation are close to zero then the features not not correlated. I use lag of 40, but you will need to adjust this value dependant on your data.
plt.clf()
fig,ax = plt.subplots(figsize=(12,4))
plt.style.use('seaborn-pastel')
fig = tsaplots.plot_acf(df['value'], lags=40,ax=ax)
plt.show()
print('values close to 1 are showing strong positive correlation. The blue regions are showing areas of uncertainty')

Python Fourier zero padding

Problem
I have a spectrum that can be download here: https://www.dropbox.com/s/ax1b32aotuzx9f1/example_spectrum.npy?dl=0
Using Python, I am trying to use zero padding to increase the number of points in the frequency domain. To do so I rely on scipy.fft and scipy.ifft functions. I do not obtain the desired result, and would be grateful for anyone that could explain why that is.
Code
Here is the code I have tried:
import numpy as np
from scipy.fft import fft, ifft
import matplotlib.pyplot as plt
spectrum = np.load('example_spectrum.npy')
spectrum_time = ifft(spectrum) # In time domain
spectrum_oversampled = fft(spectrum_time, len(spectrum)+1000) # FFT of zero padded spectrum
xaxis = np.linspace(0, len(spectrum)-1, len(spectrum_oversampled)) # to plot oversampled spectrum
fig, (ax1, ax2) = plt.subplots(2,1)
ax1.plot(spectrum, '.-')
ax1.plot(xaxis, spectrum_oversampled)
ax1.set_xlim(500, 1000)
ax1.set_xlabel('Arbitrary units')
ax1.set_ylabel('Normalized flux')
ax1.set_title('Frequency domain')
ax2.plot(spectrum_time)
ax2.set_ylim(-0.02, 0.02)
ax2.set_title('Time domain')
ax2.set_xlabel('bin number')
plt.tight_layout()
plt.show()
Results
Added figure to show results. Blue is original spectrum, orange is zero padded spectrum.
Expected behavior
I would expect the zero padding to result in a sort of sinc interpolation of the original spectrum. However, the orange curve does not go through the points of the original spectrum.
Does anyone have any idea why I obtain this behavior and/or how to fix this?

How to plot a Spectrogram with very small values? [duplicate]

I am using matplotlib.pyplot.specgram and matplotlib.pyplot.pcolormesh to make spectrogram plots of a seismic signal.
Background information -The reason for using pcolormesh is that I need to do arithmitic on the spectragram data array and then replot the resulting spectrogram (for a three-component seismogram - east, north and vertical - I need to work out the horizontal spectral magnitude and divide the vertical spectra by the horizontal spectra). It is easier to do this using the spectrogram array data than on individual amplitude spectra
I have found that the plots of the spectrograms after doing my arithmetic have unexpected values. Upon further investigation it turns out that the spectrogram plot made using the pyplot.specgram method has different values compared to the spectrogram plot made using pyplot.pcolormesh and the returned data array from the pyplot.specgram method. Both plots/arrays should contain the same values, I cannot work out why they do not.
Example:
The plot of
plt.subplot(513)
PxN, freqsN, binsN, imN = plt.specgram(trN.data, NFFT = 20000, noverlap = 0, Fs = trN.stats.sampling_rate, detrend = 'mean', mode = 'magnitude')
plt.title('North')
plt.xlabel('Time [s]')
plt.ylabel('Frequency [Hz]')
plt.clim(0, 150)
plt.colorbar()
#np.savetxt('PxN.txt', PxN)
looks different to the plot of
plt.subplot(514)
plt.pcolormesh(binsZ, freqsZ, PxN)
plt.clim(0,150)
plt.colorbar()
even though the "PxN" data array (that is, the spectrogram data values for each segment) is generated by the first method and re-used in the second.
Is anyone aware why this is happening?
P.S. I realise that my value for NFFT is not a square number, but it's not important at this stage of my coding.
P.P.S. I am not aware of what the "imN" array (fourth returned variable from pyplot.specgram) is and what it is used for....
First off, let's show an example of what you're describing so that other folks
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
# Brownian noise sequence
x = np.random.normal(0, 1, 10000).cumsum()
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(8, 10))
values, ybins, xbins, im = ax1.specgram(x, cmap='gist_earth')
ax1.set(title='Specgram')
fig.colorbar(im, ax=ax1)
mesh = ax2.pcolormesh(xbins, ybins, values, cmap='gist_earth')
ax2.axis('tight')
ax2.set(title='Raw Plot of Returned Values')
fig.colorbar(mesh, ax=ax2)
plt.show()
Magnitude Differences
You'll immediately notice the difference in magnitude of the plotted values.
By default, plt.specgram doesn't plot the "raw" values it returns. Instead, it scales them to decibels (in other words, it plots the 10 * log10 of the amplitudes). If you'd like it not to scale things, you'll need to specify scale="linear". However, for looking at frequency composition, a log scale is going to make the most sense.
With that in mind, let's mimic what specgram does:
plotted = 10 * np.log10(values)
fig, ax = plt.subplots()
mesh = ax.pcolormesh(xbins, ybins, plotted, cmap='gist_earth')
ax.axis('tight')
ax.set(title='Plot of $10 * log_{10}(values)$')
fig.colorbar(mesh)
plt.show()
Using a Log Color Scale Instead
Alternatively, we could use a log norm on the image and get a similar result, but communicate that the color values are on a log scale more clearly:
from matplotlib.colors import LogNorm
fig, ax = plt.subplots()
mesh = ax.pcolormesh(xbins, ybins, values, cmap='gist_earth', norm=LogNorm())
ax.axis('tight')
ax.set(title='Log Normalized Plot of Values')
fig.colorbar(mesh)
plt.show()
imshow vs pcolormesh
Finally, note that the examples we've shown have had no interpolation applied, while the original specgram plot did. specgram uses imshow, while we've been plotting with pcolormesh. In this case (regular grid spacing) we can use either.
Both imshow and pcolormesh are very good options, in this case. However,imshow will have significantly better performance if you're working with a large array. Therefore, you might consider using it instead, even if you don't want interpolation (e.g. interpolation='nearest' to turn off interpolation).
As an example:
extent = [xbins.min(), xbins.max(), ybins.min(), ybins.max()]
fig, ax = plt.subplots()
mesh = ax.imshow(values, extent=extent, origin='lower', aspect='auto',
cmap='gist_earth', norm=LogNorm())
ax.axis('tight')
ax.set(title='Log Normalized Plot of Values')
fig.colorbar(mesh)
plt.show()

Plot Markers on Curve where Value of X is known in matplotlib

I plotted a curve w.r.t time-series from the data which I got from an experiment. Data is collected at 10ms interval. Data is single row array.
I also have calculated an array which contains the time at which a certain device is triggered. I drew axvlines of these triggered locations.
Now I want to show markers where my curve crosses these axvlines. How can I do it?
Time of trigger (X- is known). Curve is drawn but don't have any equation (irregular experiment data). Trigger interval is also not always the same.
Thanks.
p.s - I also use multiple parasite axes on figure too. Not that it really matters but just in case.
Want Markers On Curve Where AXVline Crosses
You can use numpy.interp() to interpolate the data.
import numpy as np
import matplotlib.pyplot as plt
trig = np.array([0.4,1.3,2.1])
time = np.linspace(0,3,9)
signal = np.sin(time)+1.3
fig, ax = plt.subplots()
ax.plot(time, signal)
for x in trig:
ax.axvline(x, color="limegreen")
#interpolate:
y = np.interp(trig, time, signal)
ax.plot(trig, y, ls="", marker="*", ms=15, color="crimson")
plt.show()

python, matplotlib: specgram data array values does not match specgram plot

I am using matplotlib.pyplot.specgram and matplotlib.pyplot.pcolormesh to make spectrogram plots of a seismic signal.
Background information -The reason for using pcolormesh is that I need to do arithmitic on the spectragram data array and then replot the resulting spectrogram (for a three-component seismogram - east, north and vertical - I need to work out the horizontal spectral magnitude and divide the vertical spectra by the horizontal spectra). It is easier to do this using the spectrogram array data than on individual amplitude spectra
I have found that the plots of the spectrograms after doing my arithmetic have unexpected values. Upon further investigation it turns out that the spectrogram plot made using the pyplot.specgram method has different values compared to the spectrogram plot made using pyplot.pcolormesh and the returned data array from the pyplot.specgram method. Both plots/arrays should contain the same values, I cannot work out why they do not.
Example:
The plot of
plt.subplot(513)
PxN, freqsN, binsN, imN = plt.specgram(trN.data, NFFT = 20000, noverlap = 0, Fs = trN.stats.sampling_rate, detrend = 'mean', mode = 'magnitude')
plt.title('North')
plt.xlabel('Time [s]')
plt.ylabel('Frequency [Hz]')
plt.clim(0, 150)
plt.colorbar()
#np.savetxt('PxN.txt', PxN)
looks different to the plot of
plt.subplot(514)
plt.pcolormesh(binsZ, freqsZ, PxN)
plt.clim(0,150)
plt.colorbar()
even though the "PxN" data array (that is, the spectrogram data values for each segment) is generated by the first method and re-used in the second.
Is anyone aware why this is happening?
P.S. I realise that my value for NFFT is not a square number, but it's not important at this stage of my coding.
P.P.S. I am not aware of what the "imN" array (fourth returned variable from pyplot.specgram) is and what it is used for....
First off, let's show an example of what you're describing so that other folks
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
# Brownian noise sequence
x = np.random.normal(0, 1, 10000).cumsum()
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(8, 10))
values, ybins, xbins, im = ax1.specgram(x, cmap='gist_earth')
ax1.set(title='Specgram')
fig.colorbar(im, ax=ax1)
mesh = ax2.pcolormesh(xbins, ybins, values, cmap='gist_earth')
ax2.axis('tight')
ax2.set(title='Raw Plot of Returned Values')
fig.colorbar(mesh, ax=ax2)
plt.show()
Magnitude Differences
You'll immediately notice the difference in magnitude of the plotted values.
By default, plt.specgram doesn't plot the "raw" values it returns. Instead, it scales them to decibels (in other words, it plots the 10 * log10 of the amplitudes). If you'd like it not to scale things, you'll need to specify scale="linear". However, for looking at frequency composition, a log scale is going to make the most sense.
With that in mind, let's mimic what specgram does:
plotted = 10 * np.log10(values)
fig, ax = plt.subplots()
mesh = ax.pcolormesh(xbins, ybins, plotted, cmap='gist_earth')
ax.axis('tight')
ax.set(title='Plot of $10 * log_{10}(values)$')
fig.colorbar(mesh)
plt.show()
Using a Log Color Scale Instead
Alternatively, we could use a log norm on the image and get a similar result, but communicate that the color values are on a log scale more clearly:
from matplotlib.colors import LogNorm
fig, ax = plt.subplots()
mesh = ax.pcolormesh(xbins, ybins, values, cmap='gist_earth', norm=LogNorm())
ax.axis('tight')
ax.set(title='Log Normalized Plot of Values')
fig.colorbar(mesh)
plt.show()
imshow vs pcolormesh
Finally, note that the examples we've shown have had no interpolation applied, while the original specgram plot did. specgram uses imshow, while we've been plotting with pcolormesh. In this case (regular grid spacing) we can use either.
Both imshow and pcolormesh are very good options, in this case. However,imshow will have significantly better performance if you're working with a large array. Therefore, you might consider using it instead, even if you don't want interpolation (e.g. interpolation='nearest' to turn off interpolation).
As an example:
extent = [xbins.min(), xbins.max(), ybins.min(), ybins.max()]
fig, ax = plt.subplots()
mesh = ax.imshow(values, extent=extent, origin='lower', aspect='auto',
cmap='gist_earth', norm=LogNorm())
ax.axis('tight')
ax.set(title='Log Normalized Plot of Values')
fig.colorbar(mesh)
plt.show()

Categories

Resources