Source code for vitalDSP.transforms.fourier_transform
"""
Fourier Transform Module for Physiological Signal Processing
This module provides comprehensive Fourier Transform capabilities for analyzing
the frequency content of physiological signals such as ECG, EEG, and PPG. It
implements both Discrete Fourier Transform (DFT) and Inverse Discrete Fourier
Transform (IDFT) with signal validation and error handling.
Author: vitalDSP Team
Date: 2025-01-27
Version: 1.0.0
Key Features:
- Discrete Fourier Transform (DFT) computation
- Inverse Discrete Fourier Transform (IDFT) reconstruction
- Signal validation and error handling
- Frequency domain analysis capabilities
- Signal reconstruction from frequency components
Examples:
--------
Basic frequency analysis:
>>> import numpy as np
>>> from vitalDSP.transforms.fourier_transform import FourierTransform
>>> signal = np.sin(np.linspace(0, 10, 100)) + np.random.normal(0, 0.1, 100)
>>> ft = FourierTransform(signal)
>>> frequency_spectrum = ft.compute_dft()
>>> print(f"Spectrum shape: {frequency_spectrum.shape}")
Signal reconstruction:
>>> reconstructed_signal = ft.compute_idft(frequency_spectrum)
>>> print(f"Reconstruction error: {np.mean((signal - reconstructed_signal)**2):.6f}")
Frequency domain filtering:
>>> # Zero out high frequencies
>>> filtered_spectrum = frequency_spectrum.copy()
>>> filtered_spectrum[20:] = 0
>>> filtered_signal = ft.compute_idft(filtered_spectrum)
"""
import numpy as np
from vitalDSP.utils.data_processing.validation import SignalValidator
[docs]
class FourierTransform:
"""
A class to perform Fourier Transform for analyzing the frequency content in signals such as ECG/EEG.
The Fourier Transform is a mathematical technique that transforms a time-domain signal into its constituent
frequencies, providing insights into the signal's frequency content. This class allows for both the computation
of the Discrete Fourier Transform (DFT) and the Inverse Discrete Fourier Transform (IDFT), making it possible
to analyze and reconstruct signals.
Methods
-------
compute_dft : method
Computes the Discrete Fourier Transform (DFT) of the signal.
compute_idft : method
Computes the Inverse Discrete Fourier Transform (IDFT) to reconstruct the signal.
"""
def __init__(self, signal):
"""
Initialize the FourierTransform class with the input signal.
Parameters
----------
signal : numpy.ndarray
The input signal to be transformed. The signal should be a 1D array representing time-domain data,
such as an ECG or EEG signal.
Raises
------
ValueError
If the signal is empty or invalid.
Examples
--------
>>> signal = np.sin(np.linspace(0, 10, 100)) + np.random.normal(0, 0.1, 100)
>>> ft = FourierTransform(signal)
>>> print(ft.signal)
"""
# Validate signal - require min_length=2, don't allow empty signals
SignalValidator.validate_signal(
signal,
min_length=2,
allow_empty=False,
allow_nan=True,
allow_inf=True,
signal_name="signal",
)
self.signal = signal
[docs]
def compute_dft(self):
"""
Compute the Discrete Fourier Transform (DFT) of the input signal.
The DFT converts the time-domain signal into the frequency domain, allowing for the analysis of its frequency
components. This is particularly useful in identifying periodicities, filtering, and spectral analysis of
biomedical signals like ECG and EEG.
Returns
-------
numpy.ndarray
The frequency domain representation of the signal, where each element corresponds to a specific
frequency component.
Examples
--------
>>> signal = np.sin(np.linspace(0, 10, 100)) + np.random.normal(0, 0.1, 100)
>>> ft = FourierTransform(signal)
>>> frequency_content = ft.compute_dft()
>>> print(frequency_content)
"""
dft = np.fft.fft(self.signal)
return dft
[docs]
def compute_idft(self, frequency_content):
"""
Compute the Inverse Discrete Fourier Transform (IDFT) to reconstruct the time-domain signal.
The IDFT converts the frequency-domain data back into the time domain, reconstructing the original signal
from its frequency components. This is useful for understanding how different frequency components contribute
to the overall signal and for signal reconstruction after processing in the frequency domain.
Parameters
----------
frequency_content : numpy.ndarray
The frequency domain representation of the signal, as obtained from the DFT.
Returns
-------
numpy.ndarray
The time-domain signal reconstructed from its frequency components.
Examples
--------
>>> signal = np.sin(np.linspace(0, 10, 100)) + np.random.normal(0, 0.1, 100)
>>> ft = FourierTransform(signal)
>>> frequency_content = ft.compute_dft()
>>> reconstructed_signal = ft.compute_idft(frequency_content)
>>> print(reconstructed_signal)
"""
idft = np.fft.ifft(frequency_content)
return (
idft.real
) # Return the real part, as the imaginary part should be negligible
[docs]
def filter_frequencies(self, low_cutoff=None, high_cutoff=None, fs=1.0):
"""
Apply a bandpass filter in the frequency domain.
Parameters
----------
low_cutoff : float, optional
The low cutoff frequency. Frequencies below this will be zeroed out.
high_cutoff : float, optional
The high cutoff frequency. Frequencies above this will be zeroed out.
fs : float, optional
The sampling frequency of the signal. Default is 1.0.
Returns
-------
numpy.ndarray
The filtered signal in the time domain.
Examples
--------
>>> signal = np.sin(np.linspace(0, 10, 100)) + np.random.normal(0, 0.1, 100)
>>> ft = FourierTransform(signal)
>>> filtered_signal = ft.filter_frequencies(low_cutoff=0.5, high_cutoff=2.0, fs=10)
>>> print(filtered_signal)
"""
dft = self.compute_dft()
freqs = np.fft.fftfreq(len(self.signal), d=1 / fs)
if low_cutoff is not None:
dft[np.abs(freqs) < low_cutoff] = 0
if high_cutoff is not None:
dft[np.abs(freqs) > high_cutoff] = 0
filtered_signal = self.compute_idft(dft)
return filtered_signal