See also this Google Doc.
Exploring some of the signal processing bits and bobs in NumPy and SciPy.
In [1]:
import numpy as np
from numpy.fft import fft, fftfreq
import matplotlib.pyplot as plt
%matplotlib inline
This is from the matplotlib
examples collection.
In [2]:
from pylab import specgram
dt = 0.001
t = np.arange(0.0, 3.0, dt)
s1 = np.sin(2*np.pi*20*t)
s2 = 2*np.sin(2*np.pi*20*t)
# create a transient "chirp"
mask = np.where(np.logical_and(t>1, t<2), 1.0, 0.0)
s2 = s2 * mask
# add some noise into the mix
nse = 0.01*np.random.randn(len(t))
x = s1 + s2 + nse # the signal
NFFT = 256 # the length of the windowing segments
Fs = int(1.0/dt) # the sampling frequency
# Pxx is the segments x freqs array of instantaneous power, freqs is
# the frequency vector, bins are the centers of the time bins in which
# the power is computed, and im is the matplotlib.image.AxesImage
# instance
plt.figure(figsize=(12,8))
plt.subplot(211)
plt.plot(t, x)
ax1 = plt.subplot(212)
Pxx, freqs, bins, im = plt.specgram(x, NFFT=NFFT, Fs=Fs, noverlap=255)
ax1.set_ylim((0,256))
plt.show()
This is from the Glowing Python blog.
In [3]:
# Import the libraries we'll need
import urllib2
import StringIO
import zipfile
# Read the data from the web.
web_data = urllib2.urlopen('http://www.thesoundarchive.com/simpsons/homer/mcrumble.wav')
# Convert the bytes into a file-like object in memory
wav_in_memory = StringIO.StringIO(web_data.read())
In [4]:
import scipy.io.wavfile as wavfile
rate,data = wavfile.read(wav_in_memory)
plt.figure(figsize=(12,8))
plt.subplot(411)
plt.plot(range(len(data)),data)
plt.subplot(412)
# NFFT is the number of data points used in each block for the FFT
# and noverlap is the number of points of overlap between blocks
plt.specgram(data, NFFT=128, noverlap=64) # small window
plt.subplot(413)
plt.specgram(data, NFFT=512, noverlap=128)
plt.subplot(414)
plt.specgram(data, NFFT=1024, noverlap=512) # big window
plt.show()
This is from Tom10 on StackOverflow. He adds:
note here that only the spectrogram is useful. If you can see a good waveform or spectra, the spectrogram probably won't be interesting: 1) if the waveform is clear you probably don't have enough data and time over which the frequency is both well defined and changes enough to be interesting; 2) if the full spectra is clear, you probably don't have enough variation in frequency for the spectrogram, since the spectrum is basically just an average of what you see changing with time in the spectrogram.
In [5]:
# create a wave with 1Mhz and 0.5Mhz frequencies
dt = 0.001 # 1 ms
t = np.arange(0, 20, dt)
fscale = t/max(t)
y = np.cos(2 * np.pi * 30 * t*fscale) + (np.cos(2 * np.pi * 100 * t*fscale) * np.cos(2 * np.pi * 50 * t*fscale))
y *= np.hanning(len(y))
yy = np.concatenate((y, ([0] * 10 * len(y))))
# FFT of this
Fs = 1 / dt # sampling rate
n = len(yy) # length of the signal
k = np.arange(n)
T = n / Fs
frq = k / T # two sides frequency range
frq = frq[range(n / 2)] # one side frequency range
Y = fft(yy) / n # fft computing and normalization
Y = Y[range(n / 2)] / max(Y[range(n / 2)])
plt.figure(figsize=(12,8))
# plotting the data
plt.subplot(3, 1, 1)
plt.plot(t * 1e3, y, 'r')
plt.xlabel('Time (ms)')
plt.ylabel('Amplitude')
plt.grid()
# plotting the spectrum
plt.subplot(3, 1, 2)
plt.plot(frq[0:600], abs(Y[0:600]), 'k')
plt.xlabel('Freq (Hz)')
plt.ylabel('|Y(freq)|')
plt.grid()
# plotting the specgram
ax1 = plt.subplot(3, 1, 3)
Pxx, freqs, bins, im = plt.specgram(y, NFFT=512, Fs=Fs, noverlap=10)
#ax1.set_ylim((0,250))
plt.show()
In [6]:
import scipy.signal
In [7]:
timebase = np.arange(0,6,0.001)
f0 = 1.0
t1 = 6.0
f1 = 64.0
ch = scipy.signal.chirp(timebase, f0, t1, f1, method='quadratic')
In [8]:
plt.plot(ch)
plt.show()
In [9]:
ax1 = plt.subplot(111)
plt.specgram(ch, Fs=1000)[3]
ax1.set_ylim((0,64))
plt.show()
In [10]:
f02 = 128.0
t12 = 6.0
f12 = 8.0
ch2 = scipy.signal.chirp(timebase, f02, t12, f12, method='logarithmic')
In [11]:
ax1 = plt.subplot(111)
plt.specgram(ch+ch2, Fs=1000)[3]
ax1.set_ylim((0,128))
plt.show()
In [12]:
speech_filename = 'benchmark_signals/speech.txt'
with open(speech_filename) as f:
s = f.readlines()
s[:10]
Out[12]:
In [13]:
speech = np.loadtxt(speech_filename)
In [14]:
speech.size
Out[14]:
In [15]:
fs_speech = 8000 # sampling frequency, in Hz
In [16]:
# Let's make a function to plot a signal and its specgram
def tf(signal, fs, w=256, ylim=None):
'''
Plot a 1D signal and its spectrogram
signal: a 1D array of amplitudes
fs: the sampling frequency
w: the window length
'''
dt = 1./fs
n = signal.size
t = np.arange(0.0, n*dt, dt)
# add some noise into the mix
#nse = 0.01*np.random.randn(len(t))
noverlap = w - 1
# Pxx is the segments x freqs array of instantaneous power, freqs is
# the frequency vector, bins are the centers of the time bins in which
# the power is computed, and im is the matplotlib.image.AxesImage
# instance
plt.figure(figsize=(12,8))
plt.subplot(211)
plt.plot(t, signal)
ax1 = plt.subplot(212)
Pxx, freqs, bins, im = plt.specgram(signal, NFFT=w, Fs=fs, noverlap=noverlap, cmap='cubehelix_r')
if ylim:
ax1.set_ylim((0,ylim))
plt.show()
In [17]:
tf(speech, 8000)
In [18]:
synthetic = np.loadtxt('benchmark_signals/synthetic.txt')
In [19]:
tf(synthetic, 800, w=128, ylim=256)
In [20]:
seismic = np.loadtxt('benchmark_signals/seismic_trace.txt')
In [21]:
tf(seismic, 500, w=128, ylim=128)
In [22]:
tremor = np.loadtxt('benchmark_signals/tremor.txt')
In [23]:
tf(tremor, 100)
In [24]:
microseismic = np.loadtxt('benchmark_signals/microseismic.txt')
In [25]:
tf(microseismic, 2000, w=32)
In [26]:
tohoku = np.loadtxt('benchmark_signals/tohoku.txt')
In [27]:
tf(tohoku, 20, w=1024, ylim=0.5)
Specgram uses the short-time Fourier transform. We want to try decomposiing with some other algorithms, specifically the S-transform, the CWT, matching pursuit decomposition (MPD), empirical mode decomposition (EMD), dynamic mode decomposition (DMD).
fftw
seems to be a prerequisite for anything interesting; brew install fftw
worked best for me.
pyhht
librarypip install pypursuit
: (is correct name) requires fftw-3, can't compilesetup.py
after this patch to setup.py
:from numpy.distutils.misc_util import get_numpy_include_dirs
include_dirs=get_numpy_include_dirs(),
We'd also like to try reassignment, which can be applied to any time-frequency representation, to supposedly improve its localization, especially in frequency — I'm especially interested in how reversible this is.