Package for decomposing EMG signals into motor unit firings, created for Formento et al 2021. Based heavily on Negro et al, 2016. Supports GPU via CUDA and distributed computation via Dask.
pip install emgdecomp
For those that want to either use Dask and/or CUDA, you can alternatively run:
pip install emgdecomp[dask]
pip install emgdecomp[cuda]
# data should be a numpy array of n_channels x n_samples
sampling_rate, data = fetch_data(...)
decomp = EmgDecomposition(
params=EmgDecompositionParams(
sampling_rate=sampling_rate
))
firings = decomp.decompose(data)
print(firings)
The resulting firings
object is a NumPy structured array containing the columns source_idx
, discharge_samples
, and discharge_seconds
. source_idx
is a 0-indexed ID for each "source" learned from the data; each source is a putative motor unit. discharge_samples
indicates the sample at which the source was detected as "firing"; note that the algorithm can only detect sources up to a delay. discharge_seconds
is the conversion of discharge_samples
into seconds via the passed-in sampling rate.
As a structured NumPy array, the resulting firings
object is suitable for conversion into a Pandas DataFrame:
import pandas as pd
print(pd.DataFrame(firings))
And the "sources" (i.e. components corresponding to motor units) can be interrogated as needed via the decomp.model
property:
model = decomp.model
print(model.components)
Given an already-fit EmgDecomposition
object, you can then decompose a new batch of EMG data with its existing sources via transform
:
# Assumes decomp is already fit
new_data = fetch_more_data(...)
new_firings = decomp.transform(new_data)
print(new_firings)
Alternatively, you can add new sources (i.e. new putative motor units) while retaining the existing sources with decompose_batch
:
# Assumes decomp is already fit
more_data = fetch_even_more_data(...)
# Firings corresponding to sources that were both existing and newly added
firings2 = decomp.decompose_batch(more_data)
# Should have at least as many components as before decompose_batch()
print(decomp.model.components)
Finally, basic plotting capabilities are included as well:
from emgdecomp.plots import plot_firings, plot_muaps
plot_muaps(decomp, data, firings)
plot_firings(decomp, data, firings)
The EmgDecomposition
class is equipped with load
and save
methods that can save/load parameters to disk as needed; for example:
with open('/path/to/decomp.pkl', 'wb') as f:
decomp.save(f)
with open('/path/to/decomp.pkl', 'rb') as f:
decomp_reloaded = EmgDecomposition.load(f)
Both Dask and CUDA are supported within EmgDecomposition for support for distributed computation across workers and/or use of GPU acceleration. Each are controlled via the use_dask
and use_cuda
boolean flags in the EmgDecomposition
constructor.
See the list of parameters in EmgDecompositionParameters. The defaults on master
are set as they were used for Formento et. al, 2021 and should be reasonable defaults for others.
See documentation on classes EmgDecomposition
and EmgDecompositionParameters
for more details.
If you enjoy this package and use it for your research, you can:
Feel free to open issues in this project if there are questions or feature requests. Pull requests for feature requests are very much encouraged, but feel free to create an issue first before implementation to ensure the desired change sounds appropriate.