Skip to content

Commit

Permalink
Merge branch 'main' into zeropad
Browse files Browse the repository at this point in the history
  • Loading branch information
TomDonoghue authored May 14, 2024
2 parents 5fc7f96 + aef60f3 commit 2df033d
Show file tree
Hide file tree
Showing 35 changed files with 848 additions and 163 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
MODULE_NAME: neurodsp
strategy:
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ tests:
# Run test coverage
coverage:
@printf "\n\nRUN TESTS: \n"
@coverage run --source $(MODULE) -m py.test
@coverage run --source $(MODULE) -m pytest
@printf "\n\nCHECK COVERAGE: \n"
@coverage report --omit="*/tests*"

Expand Down
23 changes: 15 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,35 @@
Neuro Digital Signal Processing Toolbox
========================================

|ProjectStatus|_ |Version|_ |BuildStatus|_ |Coverage|_ |License|_ |PythonVersions|_ |Publication|_
|ProjectStatus| |Version| |BuildStatus| |Coverage| |License| |PythonVersions| |Publication|

.. |ProjectStatus| image:: https://www.repostatus.org/badges/latest/active.svg
.. _ProjectStatus: https://www.repostatus.org/#active
:target: https://www.repostatus.org/#active
:alt: build status

.. |Version| image:: https://img.shields.io/pypi/v/neurodsp.svg
.. _Version: https://pypi.python.org/pypi/neurodsp/
:target: https://pypi.python.org/pypi/neurodsp/
:alt: version

.. |BuildStatus| image:: https://github.com/neurodsp-tools/neurodsp/actions/workflows/build.yml/badge.svg
.. _BuildStatus: https://github.com/neurodsp-tools/neurodsp/actions/workflows/build.yml
:target: https://github.com/neurodsp-tools/neurodsp/actions/workflows/build.yml
:alt: build status

.. |Coverage| image:: https://codecov.io/gh/neurodsp-tools/neurodsp/branch/main/graph/badge.svg
.. _Coverage: https://codecov.io/gh/neurodsp-tools/neurodsp
:target: https://codecov.io/gh/neurodsp-tools/neurodsp
:alt: coverage

.. |License| image:: https://img.shields.io/pypi/l/neurodsp.svg
.. _License: https://opensource.org/licenses/Apache-2.0
:target: https://opensource.org/licenses/Apache-2.0
:alt: license

.. |PythonVersions| image:: https://img.shields.io/pypi/pyversions/neurodsp.svg
.. _PythonVersions: https://pypi.python.org/pypi/neurodsp/
:target: https://pypi.python.org/pypi/neurodsp/
:alt: python versions

.. |Publication| image:: https://joss.theoj.org/papers/10.21105/joss.01272/status.svg
.. _Publication: https://doi.org/10.21105/joss.01272
:target: https://doi.org/10.21105/joss.01272
:alt: publication

Tools to analyze and simulate neural time series, using digital signal processing.

Expand Down
12 changes: 12 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Hilbert Methods
.. autosummary::
:toctree: generated/

compute_instantaneous_measure
robust_hilbert
phase_by_time
amp_by_time
Expand Down Expand Up @@ -113,6 +114,7 @@ Spectral Power
compute_spectrum_welch
compute_spectrum_wavelet
compute_spectrum_medfilt
compute_spectrum_multitaper

Spectral Measures
~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -347,6 +349,7 @@ Time Series
plot_time_series
plot_instantaneous_measure
plot_bursts
plot_multi_time_series

Spectral
~~~~~~~~
Expand Down Expand Up @@ -391,6 +394,15 @@ Time Frequency

plot_timefrequency

Combined
~~~~~~~~

.. currentmodule:: neurodsp.plts.combined
.. autosummary::
:toctree: generated/

plot_timeseries_and_spectra

Utilities
---------

Expand Down
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
("Glossary", "glossary"),
("Tutorials", "auto_tutorials/index"),
("Examples", "auto_examples/index"),
("Reference", "reference"),
("GitHub", "https://github.com/neurodsp-tools/neurodsp", True)
],

Expand Down
31 changes: 31 additions & 0 deletions doc/reference.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Reference
=========

This page describes how to reference and report on using this module.

Table of Contents
-----------------
.. contents::
:local:
:backlinks: none

Referencing the Module
~~~~~~~~~~~~~~~~~~~~~~

If you use this code in your project, please cite:

.. code-block:: text
Cole, S., Donoghue, T., Gao, R., & Voytek, B. (2019). NeuroDSP: A package for
neural digital signal processing. Journal of Open Source Software, 4(36), 1272.
DOI: 10.21105/joss.01272
Direct Link: https://doi.org/10.21105/joss.01272

Referencing Specific Methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In some cases, a method or process available in `neurodsp` may be a re-implementation
that has a specific source and reference point, in which case the original should
also be cited. In such cases, there will be a 'Reference' section in the function
documentation, providing a reference to cite.
21 changes: 12 additions & 9 deletions examples/plot_mne_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@
###################################################################################################

# Get the data path for the MNE example data
raw_fname = sample.data_path() + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'
sample_data = sample.data_path()
raw_fname = str(sample_data) + '/MEG/sample/sample_audvis_filt-0-40_raw.fif'

###################################################################################################

# Load the file of example MNE data
raw = io.read_raw_fif(raw_fname, preload=True, verbose=False)
Expand Down Expand Up @@ -226,26 +229,26 @@
###################################################################################################

# Plot locations with highest lagged coherence rhythmicity score
vmin, vmax = np.min(max_score), np.max(max_score)
plot_topomap(max_score, raw.info, cmap=cm.viridis, vmin=vmin, vmax=vmax, contours=0)
vlim = [np.min(max_score), np.max(max_score)]
plot_topomap(max_score, raw.info, cmap=cm.viridis, vlim=vlim, contours=0)

# Add colorbar
plt.figure(figsize=[2, 4])
sm = cm.ScalarMappable(cmap='viridis', norm=colors.Normalize(vmin=vmin, vmax=vmax))
sm.set_array(np.linspace(vmin, vmax))
sm = cm.ScalarMappable(cmap='viridis', norm=colors.Normalize(vmin=vlim[0], vmax=vlim[1]))
sm.set_array(np.linspace(*vlim))
cbar = plt.colorbar(sm, orientation='vertical', label='Score')
plt.gca().set_visible(False); plt.gcf().subplots_adjust(right=0.5)

###################################################################################################

# Plot frequency with most dominant rhythmicity
vmin, vmax = np.min(max_freq)-2, np.max(max_freq)+2
plot_topomap(max_freq, raw.info, cmap=cm.viridis, vmin=vmin, vmax=vmax, contours=0)
vlim = [np.min(max_freq) - 2, np.max(max_freq) + 2]
plot_topomap(max_freq, raw.info, cmap=cm.viridis, vlim=vlim, contours=0)

# Add colorbar
plt.figure(figsize=[2, 4])
sm = cm.ScalarMappable(cmap='viridis', norm=colors.Normalize(vmin=vmin, vmax=vmax))
sm.set_array(np.linspace(vmin, vmax))
sm = cm.ScalarMappable(cmap='viridis', norm=colors.Normalize(vmin=vlim[0], vmax=vlim[1]))
sm.set_array(np.linspace(*vlim))
cbar = plt.colorbar(sm, orientation='vertical', label='Frequency')
plt.gca().set_visible(False); plt.gcf().subplots_adjust(right=0.5)

Expand Down
87 changes: 50 additions & 37 deletions neurodsp/filt/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
###################################################################################################
###################################################################################################

def filter_signal(sig, fs, pass_type, f_range, filter_type='fir',
n_cycles=3, n_seconds=None, remove_edges=True, butterworth_order=None,
print_transitions=False, plot_properties=False, return_filter=False):
def filter_signal(sig, fs, pass_type, f_range, filter_type=None,
print_transitions=False, plot_properties=False, return_filter=False,
**filter_kwargs):
"""Apply a bandpass, bandstop, highpass, or lowpass filter to a neural signal.
Parameters
Expand All @@ -32,27 +32,31 @@ def filter_signal(sig, fs, pass_type, f_range, filter_type='fir',
For 'bandpass' & 'bandstop', must be a tuple.
For 'lowpass' or 'highpass', can be a float that specifies pass frequency, or can be
a tuple and is assumed to be (None, f_hi) for 'lowpass', and (f_lo, None) for 'highpass'.
n_cycles : float, optional, default: 3
Length of filter, in number of cycles, at the 'f_lo' frequency, if using an FIR filter.
This parameter is overwritten by `n_seconds`, if provided.
n_seconds : float, optional
Length of filter, in seconds, if using an FIR filter.
This parameter overwrites `n_cycles`.
filter_type : {'fir', 'iir'}, optional
Whether to use an FIR or IIR filter.
The only IIR filter offered is a butterworth filter.
remove_edges : bool, optional, default: True
If True, replace samples within half the kernel length to be np.nan.
Only used for FIR filters.
butterworth_order : int, optional
Order of the butterworth filter, if using an IIR filter.
See input 'N' in scipy.signal.butter.
Whether to use an FIR or IIR filter. IIR option is a butterworth filter.
If None, type is inferred from input parameters, and/or defaults to FIR.
print_transitions : bool, optional, default: True
If True, print out the transition and pass bandwidths.
plot_properties : bool, optional, default: False
If True, plot the properties of the filter, including frequency response and/or kernel.
return_filter : bool, optional, default: False
If True, return the filter coefficients.
**filter_kwargs
Additional parameters for the filtering function, specific to filtering type.
| For FIR filters, can include:
| n_cycles : float, optional
| Filter length, in number of cycles, defined at 'f_lo' frequency.
| Either `n_cycles` or `n_seconds` can be set for the filter length, but not both.
| If not provided, and `n_seconds` is also not defined, defaults to 3.
| n_seconds : float, optional
| Filter length, in seconds.
| Either `n_cycles` or `n_seconds` can be set for the filter length, but not both.
| remove_edges : bool, optional, default: True
| If True, replace samples within half the kernel length to be np.nan.
| For IIR filters, can include:
| butterworth_order : int, optional
| Order of the butterworth filter. See input 'N' in scipy.signal.butter.
Returns
-------
Expand All @@ -72,27 +76,36 @@ def filter_signal(sig, fs, pass_type, f_range, filter_type='fir',
... filter_type='fir', f_range=(1, 25))
"""

check_param_options(filter_type, 'filter_type', ['fir', 'iir'])
if filter_type is not None:
check_param_options(filter_type, 'filter_type', ['fir', 'iir'])
else:
# Infer IIR if relevant parameter set, otherwise, assume FIR
filter_type = 'iir' if 'butterworth_order' in filter_kwargs else 'fir'

_filter_input_checks(filter_type, filter_kwargs)

if filter_type.lower() == 'fir':
return filter_signal_fir(sig, fs, pass_type, f_range, n_cycles, n_seconds,
remove_edges, print_transitions,
plot_properties, return_filter)
return filter_signal_fir(sig, fs, pass_type, f_range, **filter_kwargs,
print_transitions=print_transitions,
plot_properties=plot_properties,
return_filter=return_filter)

elif filter_type.lower() == 'iir':
_iir_checks(n_seconds, butterworth_order, remove_edges)
return filter_signal_iir(sig, fs, pass_type, f_range, butterworth_order,
print_transitions, plot_properties,
return_filter)


def _iir_checks(n_seconds, butterworth_order, remove_edges):
"""Checks for using an IIR filter if called from the general filter function."""

# Check inputs for IIR filters
if n_seconds is not None:
raise ValueError('n_seconds should not be defined for an IIR filter.')
if butterworth_order is None:
raise ValueError('butterworth_order must be defined when using an IIR filter.')
if remove_edges:
warn('Edge artifacts are not removed when using an IIR filter.')
return filter_signal_iir(sig, fs, pass_type, f_range, **filter_kwargs,
print_transitions=print_transitions,
plot_properties=plot_properties,
return_filter=return_filter)


FILTER_INPUTS = {
'fir' : ['n_cycles', 'n_seconds', 'remove_edges'],
'iir' : ['butterworth_order'],
}


def _filter_input_checks(filter_type, filter_kwargs):
"""Check inputs to `filter_signal` match filter type."""

for param in filter_kwargs.keys():
assert param in FILTER_INPUTS[filter_type], \
'Parameter {} not expected for {} filter'.format(param, filter_type)
Loading

0 comments on commit 2df033d

Please sign in to comment.