ShortTimeFFT#
- class scipy.signal.ShortTimeFFT(win, hop, fs, *, fft_mode='onesided', mfft=None, dual_win=None, scale_to=None, phase_shift=0)[source]#
Provide a parametrized discrete Short-time Fourier transform (stft) and its inverse (istft).
The stft calculates sequential FFTs by sliding a window (
win
) over an input signal byhop
increments. It can be used to quantify the change of the spectrum over time.The stft is represented by a complex-valued matrix S[q,p] where the p-th column represents an FFT with the window centered at the time t[p] = p *
delta_t
= p *hop
*T
whereT
is the sampling interval of the input signal. The q-th row represents the values at the frequency f[q] = q *delta_f
withdelta_f
= 1 / (mfft
*T
) being the bin width of the FFT.The inverse STFT istft is calculated by reversing the steps of the STFT: Take the IFFT of the p-th slice of S[q,p] and multiply the result with the so-called dual window (see
dual_win
). Shift the result by p *delta_t
and add the result to previous shifted results to reconstruct the signal. If only the dual window is known and the STFT is invertible,from_dual
can be used to instantiate this class.Due to the convention of time t = 0 being at the first sample of the input signal, the STFT values typically have negative time slots. Hence, negative indexes like
p_min
ork_min
do not indicate counting backwards from an array’s end like in standard Python indexing but being left of t = 0.More detailed information can be found in the Short-Time Fourier Transform section of the SciPy User Guide.
Note that all parameters of the initializer, except
scale_to
(which usesscaling
) have identical named attributes.- Parameters:
- winnp.ndarray
The window must be a real- or complex-valued 1d array.
- hopint
The increment in samples, by which the window is shifted in each step.
- fsfloat
Sampling frequency of input signal and window. Its relation to the sampling interval
T
isT = 1 / fs
.- fft_mode‘twosided’, ‘centered’, ‘onesided’, ‘onesided2X’
Mode of FFT to be used (default ‘onesided’). See property
fft_mode
for details.- mfft: int | None
Length of the FFT used, if a zero padded FFT is desired. If
None
(default), the length of the windowwin
is used.- dual_winnp.ndarray | None
The dual window of
win
. If set toNone
, it is calculated if needed.- scale_to‘magnitude’, ‘psd’ | None
If not
None
(default) the window function is scaled, so each STFT column represents either a ‘magnitude’ or a power spectral density (‘psd’) spectrum. This parameter sets the propertyscaling
to the same value. See methodscale_to
for details.- phase_shiftint | None
If set, add a linear phase
phase_shift
/mfft
*f
to each frequencyf
. The default value 0 ensures that there is no phase shift on the zeroth slice (in which t=0 is centered). See propertyphase_shift
for more details.
- Attributes:
T
Sampling interval of input signal and of the window.
delta_f
Width of the frequency bins of the STFT.
delta_t
Time increment of STFT.
dual_win
Canonical dual window.
f
Frequencies values of the STFT.
f_pts
Number of points along the frequency axis.
fac_magnitude
Factor to multiply the STFT values by to scale each frequency slice to a magnitude spectrum.
fac_psd
Factor to multiply the STFT values by to scale each frequency slice to a power spectral density (PSD).
fft_mode
Mode of utilized FFT (‘twosided’, ‘centered’, ‘onesided’ or ‘onesided2X’).
fs
Sampling frequency of input signal and of the window.
hop
Time increment in signal samples for sliding window.
invertible
Check if STFT is invertible.
k_min
The smallest possible signal index of the STFT.
lower_border_end
First signal index and first slice index unaffected by pre-padding.
m_num
Number of samples in window
win
.m_num_mid
Center index of window
win
.mfft
Length of input for the FFT used - may be larger than window length
m_num
.onesided_fft
Return True if a one-sided FFT is used.
p_min
The smallest possible slice index.
phase_shift
If set, add linear phase
phase_shift
/mfft
*f
to each FFT slice of frequencyf
.scaling
Normalization applied to the window function (‘magnitude’, ‘psd’ or
None
).win
Window function as real- or complex-valued 1d array.
Methods
extent
(n[, axes_seq, center_bins])Return minimum and maximum values time-frequency values.
from_dual
(dual_win, hop, fs, *[, fft_mode, ...])Instantiate a ShortTimeFFT by only providing a dual window.
from_window
(win_param, fs, nperseg, noverlap, *)Instantiate ShortTimeFFT by using get_window.
istft
(S[, k0, k1, f_axis, t_axis])Inverse short-time Fourier transform.
k_max
(n)First sample index after signal end not touched by a time slice.
nearest_k_p
(k[, left])Return nearest sample index k_p for which t[k_p] == t[p] holds.
p_max
(n)Index of first non-overlapping upper time slice for n sample input.
p_num
(n)Number of time slices for an input signal with n samples.
p_range
(n[, p0, p1])Determine and validate slice index range.
scale_to
(scaling)Scale window to obtain 'magnitude' or 'psd' scaling for the STFT.
spectrogram
(x[, y, detr, p0, p1, k_offset, ...])Calculate spectrogram or cross-spectrogram.
stft
(x[, p0, p1, k_offset, padding, axis])Perform the short-time Fourier transform.
stft_detrend
(x, detr[, p0, p1, k_offset, ...])Short-time Fourier transform with a trend being subtracted from each segment beforehand.
t
(n[, p0, p1, k_offset])Times of STFT for an input signal with n samples.
First signal index and first slice index affected by post-padding.
Examples
The following example shows the magnitude of the STFT of a sine with varying frequency \(f_i(t)\) (marked by a red dashed line in the plot):
>>> import numpy as np >>> import matplotlib.pyplot as plt >>> from scipy.signal import ShortTimeFFT >>> from scipy.signal.windows import gaussian ... >>> T_x, N = 1 / 20, 1000 # 20 Hz sampling rate for 50 s signal >>> t_x = np.arange(N) * T_x # time indexes for signal >>> f_i = 1 * np.arctan((t_x - t_x[N // 2]) / 2) + 5 # varying frequency >>> x = np.sin(2*np.pi*np.cumsum(f_i)*T_x) # the signal
The utilized Gaussian window is 50 samples or 2.5 s long. The parameter
mfft=200
in ShortTimeFFT causes the spectrum to be oversampled by a factor of 4:>>> g_std = 8 # standard deviation for Gaussian window in samples >>> w = gaussian(50, std=g_std, sym=True) # symmetric Gaussian window >>> SFT = ShortTimeFFT(w, hop=10, fs=1/T_x, mfft=200, scale_to='magnitude') >>> Sx = SFT.stft(x) # perform the STFT
In the plot, the time extent of the signal x is marked by vertical dashed lines. Note that the SFT produces values outside the time range of x. The shaded areas on the left and the right indicate border effects caused by the window slices in that area not fully being inside time range of x:
>>> fig1, ax1 = plt.subplots(figsize=(6., 4.)) # enlarge plot a bit >>> t_lo, t_hi = SFT.extent(N)[:2] # time range of plot >>> ax1.set_title(rf"STFT ({SFT.m_num*SFT.T:g}$\,s$ Gaussian window, " + ... rf"$\sigma_t={g_std*SFT.T}\,$s)") >>> ax1.set(xlabel=f"Time $t$ in seconds ({SFT.p_num(N)} slices, " + ... rf"$\Delta t = {SFT.delta_t:g}\,$s)", ... ylabel=f"Freq. $f$ in Hz ({SFT.f_pts} bins, " + ... rf"$\Delta f = {SFT.delta_f:g}\,$Hz)", ... xlim=(t_lo, t_hi)) ... >>> im1 = ax1.imshow(abs(Sx), origin='lower', aspect='auto', ... extent=SFT.extent(N), cmap='viridis') >>> ax1.plot(t_x, f_i, 'r--', alpha=.5, label='$f_i(t)$') >>> fig1.colorbar(im1, label="Magnitude $|S_x(t, f)|$") ... >>> # Shade areas where window slices stick out to the side: >>> for t0_, t1_ in [(t_lo, SFT.lower_border_end[0] * SFT.T), ... (SFT.upper_border_begin(N)[0] * SFT.T, t_hi)]: ... ax1.axvspan(t0_, t1_, color='w', linewidth=0, alpha=.2) >>> for t_ in [0, N * SFT.T]: # mark signal borders with vertical line: ... ax1.axvline(t_, color='y', linestyle='--', alpha=0.5) >>> ax1.legend() >>> fig1.tight_layout() >>> plt.show()
Reconstructing the signal with the istft is straightforward, but note that the length of x1 should be specified, since the SFT length increases in
hop
steps:>>> SFT.invertible # check if invertible True >>> x1 = SFT.istft(Sx, k1=N) >>> np.allclose(x, x1) True
It is possible to calculate the SFT of signal parts:
>>> p_q = SFT.nearest_k_p(N // 2) >>> Sx0 = SFT.stft(x[:p_q]) >>> Sx1 = SFT.stft(x[p_q:])
When assembling sequential STFT parts together, the overlap needs to be considered:
>>> p0_ub = SFT.upper_border_begin(p_q)[1] - SFT.p_min >>> p1_le = SFT.lower_border_end[1] - SFT.p_min >>> Sx01 = np.hstack((Sx0[:, :p0_ub], ... Sx0[:, p0_ub:] + Sx1[:, :p1_le], ... Sx1[:, p1_le:])) >>> np.allclose(Sx01, Sx) # Compare with SFT of complete signal True
It is also possible to calculate the itsft for signal parts:
>>> y_p = SFT.istft(Sx, N//3, N//2) >>> np.allclose(y_p, x[N//3:N//2]) True