MSF-decomp provides an efficient and intuitive way to analyze and decompose complex signals by identifying and isolating its most prominent frequency components in space and time. This library is ideal for researchers, engineers, and data scientists working with signal processing, audio analysis, sensor data, and more, enabling deeper insights into the underlying periodicities and structures within their data.
MSF-decomp is a Python module. You can install it by cloning the repository and installing the necessary dependencies.
- Python 3.7+
numpyscipynumba(for parallel functionalities)matplotlib(recommended for visualization features)
-
Clone the repository
git clone https://github.com/jjordene/MSF-decomp.git cd MSF-decomp -
Install dependencies
pip install numpy scipy numba matplotlib
There are two ways this module can be used. The easiest is to only pass the calc_msf function, and all stages are calculated at once. For large data this can take some time.
Here's a basic example of how to use calc_msf to decompose a synthetic signal and visualize the results.
import numpy as np
import matplotlib.pyplot as plt
import MSF # Assuming MSF.py is in your Python path or current directory
# 1. Generate a synthetic signal with multiple frequencies
mx = 20
mt = 1024
my = 100
dt = dx = dy = 1
x,t,y = np.arange(mx),np.arange(mt),np.arange(my)
data=np.zeros((mx,mt,my))
kt = sci.fft.rfftfreq(mt,dt)
for yy in y:
for tt in t:
for xx in x:
data[xx,tt,yy]= 0.5*np.cos(-2 * np.pi *kt[9]*tt*dt)+np.cos(-2 * np.pi *kt[2]*tt*dt)
# 2. Perform Most Significant Frequency (MSF) decomposition
# Decide order and number of MSF components
## We choose 3 components. The order of these components must be (mt,mx,my) therefore the order of the data (mx,mt,my) must change.
axes_order = (1,0,2)
#Initialise class
a = msf.msf(data,axes_order,dt,dy,dx)
#Calculate first iteration of MSF in the time direction (outputs: MSF frequency index, difference to original signal, MSF signal)
f_max = 0.02 #Maximum individual frequency calculated
msf_index,msf_diff,msf_wave = a.calc_msf(sec=False,parallel=True,f_max=f_max,save_bins=True,dir='kt')
#To iterate the MSF decomposition
##Calculate remainder of signal
new_sig = a.data_remainder(data,msf_wave)
## Calculate remainder of signal in each frequency bin
new_bins = a.bins_remainder(a.ifft_bins,msf_index)
#Iterate MSF calculation
msf2_index,msf2_diff,msf2_wave = a.calc_msf(data=new_sig,ifft_bins=new_bins,sec=True,parallel=True,save_bins=False)
# 3. Visualise the significant frequencies
fig,ax=plt.subplots(1,2)
ax[0].scatter(t,msf_index[0,:,0],s=1,color='lightblue',label="first_it")
ax[0].scatter(t,msf2_index[0,:,0],s=1,color='k',label="second_it")
ax[0].set_yticks(np.arange(0,len(a.kt[a.kt<f_max])+1),labels= [f"{a.kt[j]*1000:.2f}" if a.kt[j] < f_max else f"$>$ {a.kt[j]*1000:.2f}" for j in range(len(a.kt[a.kt<f_max])+1)])
ax[0].set_ylabel("MSF (mHz)")
ax[0].set_xlabel("t")
ax[0].legend()
# Visualise the MSF signal against the original
ax[1].plot(t, data[0,:,0], label='Original Signal', color='gray', alpha=0.7)
ax[1].plot(t, msf_wave[0,:,0], label='First_it', color='lightblue', alpha=1)
ax[1].plot(t, msf2_wave[0,:,0]+msf_wave[0,:,0], label='Second_it', color='lightcoral', alpha=1)
ax[1].set_ylabel('Amplitude')
ax[1].set_xlabel('t')
ax[1].legend()It is also possible to do the MSF algorithm step by step, as follows:
psd- calculates PSD throughscipy.fftfiltered_ifft- frequency filtering of the PSD, returning signals for each induvidual frequencypoint_diff- Calculates the difference between each filtered frequency signal and the original signal at each grid point.calc_msf- Returns the MSF at each point, the point diff for the MSF signal, and the MSF signal.
The core functionality of MSF.py is exposed through the MSF class, primarily through the calc_msf function.
Parameters
-
file(np.ndarray):
Data for the MSF decomposition to be performed on. Can be 1, 2 or 3 dimensional. -
axes(tupleorint):
Dimensions for Fourier transform to be performed on. The first dimension will always relate toself.kt, and will usescipy.fft.rfft. Therefore, the number of outputs will be half the size of the original data in this direction. The second dimension relates toself.kxand the final toself.ky. The parameterdyis therefore only needed if the fft is performed in 3 dimensions, and similarly,dxfor two or more dimensions. -
dt, (float):
Step size of first dimension,$t$ . -
dx,dy(float, optional):
Step sizes for second and third dimensions. Only needed ifaxesis a tuple of length 2 or 3, respectively.
Performs Most Significant Frequency (MSF) decomposition on a given signal in the direction dir. In the case dir='kperp', will calculate MSF bins which contain any kx and ky value between kx**2+ky**2 steps.
Parameters:
-
sec(logical):
Treats as a first iteration ifFalse. IfTrue, requires thedataandifft_binsparameters, recalculated after first iteration. -
parallel(logical):
If True, uses parallelised version of MSF calculation. NOTE: Does not apply to the fft functionality. If no parallelisation is wanted in the fourier transforms, must setworkerstoNone. -
workers(int, optional):
Maximum number of workers to use for parallel computation in fft. Seescipy.fftdocumentation for more details. -
dir(str,optional):
Direction in which MSF is to be calculated. Possible directions:'kt','kx','ky'and'kperp', depending on dimensions ofaxes. By default will calculate the temporal frequency (kt). Not needed ififft_binsis given. -
f_max(float,optional):
Maximum individual frequency which will be calculated. By default all frequencies are treated individually (which can be computationally expensive). -
save_bins(logical, optional):
If true, saves the attributeifft_bins. This is needed for the second iteration ofcalc_msf. -
ifft_bins(sequence of numpy.ndarrays, optional):
List filtered bins from the inverse fourier transform for each frequency. If passed,calc_msfwill skip the filtering process. -
data(numpy.ndarray,optional):
Raw data for filtered signals to be compared to. Only necessary in additional iterations. -
point_diff(sequence of numpy.ndarrays, optional):
Sequence of arrays containing the difference between each filtered frequency signal and the original data at each grid point. If passed,calc_msfwill skip the difference calculation. -
psd(numpy.ndarray, optional):
Normal DFT PSD of the data usingscipyfunctionality. If passed,calc_msfwill skip calculating the FFT.
Returns:
msf_index(numpy.ndarray):
The MSF index, relating to the index of the frequency arraykwhich correlates to the MSF at each grid point.msf_diff(numpy.ndarray):
The difference between the MSF signal,msf_waveand the original signal.msf_wave(numpy.ndarray):
The signal created by the MSF at each grid point.
Raises
ValueError
Ififft_binsordataare not given whensec=True
Example:
import numpy as np
import MSF
# 1. Generate a synthetic signal with multiple frequencies
mx = 20
mt = 1024
my = 100
dt = dx = dy = 1
x,t,y = np.arange(mx),np.arange(mt),np.arange(my)
data=np.zeros((mx,mt,my))
kt = sci.fft.rfftfreq(mt,dt)
for yy in y:
for tt in t:
for xx in x:
data[xx,tt,yy]= 0.5*np.cos(-2 * np.pi *kt[9]*tt*dt)+np.cos(-2 * np.pi *kt[2]*tt*dt)
# 2. Perform Most Significant Frequency (MSF) decomposition
#Initialise class
a = msf.msf(data,(1,0,2),dt,dy,dx)
#Calculate first iteration of MSF in the kt direction
f_max = 0.02
msf_index,msf_diff,msf_wave = a.calc_msf(sec=False,parallel=True,f_max=f_max,save_bins=True,dir='kt')
#msf_index gives the index of kt which relates to the MSF at each point.MSF-decomp/
βββ .gitignore # Specifies intentionally untracked files to ignore
βββ LICENSE # Project's license (BSD 3-Clause)
βββ MSF.py # The core MSF decomposition module
βββ README.md # This documentation file
We welcome contributions to MSF-decomp! If you'd like to contribute, please consider the following:
- Fork the repository.
- Clone your forked repository:
git clone https://github.com/YOUR_USERNAME/MSF-decomp.git cd MSF-decomp - Install development dependencies (same as runtime dependencies for this project):
pip install numpy scipy matplotlib
- Make your changes, add new features, or fix bugs.
- Ensure your code adheres to Python best practices (e.g., PEP 8).
As of now, explicit test files are not present. Contributions including unit tests using a framework like pytest would be highly appreciated.
- Commit your changes with clear and concise commit messages.
- Push your changes to your fork.
- Open a pull request to the
mainbranch of the originalMSF-decomprepository.
This project is licensed under the BSD 3-Clause "New" or "Revised" License - see the LICENSE file for details.
- π Issues: GitHub Issues - For bug reports, feature requests, or questions.
β Star this repo if you find it helpful for your signal processing needs!
Made by George Cherry (jjordene)