Skip to content

Commit

Permalink
review updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanhammonds committed Jan 21, 2021
1 parent fdd109c commit 91eaf44
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 99 deletions.
35 changes: 18 additions & 17 deletions neurodsp/filt/checks.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
"""Checker functions for filtering."""

from warnings import warn
import os
import json

import numpy as np

Expand Down Expand Up @@ -92,7 +90,7 @@ def check_filter_definition(pass_type, f_range):


def check_filter_properties(filter_coefs, a_vals, fs, pass_type, f_range, transitions=(-20, -3),
filt_type=None, verbose=True, save_properties=None):
return_properties=False, verbose=True):
"""Check a filters properties, including pass band and transition band.
Parameters
Expand All @@ -119,17 +117,18 @@ def check_filter_properties(filter_coefs, a_vals, fs, pass_type, f_range, transi
a tuple and is assumed to be (None, f_hi) for 'lowpass', and (f_lo, None) for 'highpass'.
transitions : tuple of (float, float), optional, default: (-20, -3)
Cutoffs, in dB, that define the transition band.
filt_type : str, optional, {'FIR', 'IIR'}
The type of filter being applied.
return_properties : bool, optional, default: False
Returns the frequency response, pass band and transition band.
verbose : bool, optional, default: True
Whether to print out filter properties.
save_properties : str
Path, including file name, to save filter properites to as a json.
Whether to print out transition and pass bands.
Returns
-------
passes : bool
Whether all the checks pass. False if one or more checks fail.
properties : dict
The frequency response, pass band and transition band.
Only returned if return_properties is True.
Examples
--------
Expand All @@ -145,7 +144,7 @@ def check_filter_properties(filter_coefs, a_vals, fs, pass_type, f_range, transi

# Import utility functions inside function to avoid circular imports
from neurodsp.filt.utils import (compute_frequency_response, compute_pass_band,
compute_transition_band, gen_filt_report, save_filt_report)
compute_transition_band)

# Initialize variable to keep track if all checks pass
passes = True
Expand Down Expand Up @@ -179,16 +178,18 @@ def check_filter_properties(filter_coefs, a_vals, fs, pass_type, f_range, transi
warn('Transition bandwidth is {:.1f} Hz. This is greater than the desired'\
'pass/stop bandwidth of {:.1f} Hz'.format(transition_bw, pass_bw))

# Report filter properties
if verbose or save_properties:
filt_report = gen_filt_report(pass_type, filt_type, fs, f_db, db, pass_bw,
transition_bw, f_range, f_range_trans)

# Print out transition bandwidth and pass bandwidth to the user
if verbose:
print('\n'.join('{} : {}'.format(key, value) for key, value in filt_report.items()))
print('Transition bandwidth is {:.1f} Hz.'.format(transition_bw))
print('Pass/stop bandwidth is {:.1f} Hz.'.format(pass_bw))

# Return the filter properties
if return_properties:

properties = {'f_db': f_db, 'db': db, 'pass_bw': pass_bw, 'transition_bw': transition_bw,
'f_range_trans': f_range_trans}

if save_properties is not None:
save_filt_report(save_properties, filt_report)
return passes, properties

return passes

Expand Down
14 changes: 6 additions & 8 deletions neurodsp/filt/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

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, save_properties=None, return_filter=False, verbose=False):
plot_properties=False, return_filter=False, save_report=None):
"""Apply a bandpass, bandstop, highpass, or lowpass filter to a neural signal.
Parameters
Expand Down Expand Up @@ -50,12 +50,10 @@ def filter_signal(sig, fs, pass_type, f_range, filter_type='fir', n_cycles=3, n_
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.
save_properties : str, optional, default: None
Path, including file name, to save filter properites to as a json.
return_filter : bool, optional, default: False
If True, return the filter coefficients.
verbose : bool, optional, default: False
If True, print out detailed filter information.
save_report : str, optional, default: None
Path, including file name, to save a filter report to as a pdf.
Returns
-------
Expand All @@ -78,12 +76,12 @@ def filter_signal(sig, fs, pass_type, f_range, filter_type='fir', n_cycles=3, n_
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,
save_properties, return_filter, verbose)
return_filter, save_report)
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, save_properties,
return_filter, verbose)
print_transitions, plot_properties, return_filter,
save_report)
else:
raise ValueError('Filter type not understood.')

Expand Down
33 changes: 22 additions & 11 deletions neurodsp/filt/fir.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
from neurodsp.utils import remove_nans, restore_nans
from neurodsp.utils.decorators import multidim
from neurodsp.plts.filt import plot_filter_properties
from neurodsp.filt.utils import compute_frequency_response, remove_filter_edges
from neurodsp.filt.utils import compute_frequency_response, remove_filter_edges, save_filt_report
from neurodsp.filt.checks import (check_filter_definition, check_filter_properties,
check_filter_length)

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

def filter_signal_fir(sig, fs, pass_type, f_range, n_cycles=3, n_seconds=None, remove_edges=True,
print_transitions=False, plot_properties=False, save_properties=None,
return_filter=False, verbose=False, file_path=None, file_name=None):
print_transitions=False, plot_properties=False, return_filter=False,
save_report=None):
"""Apply an FIR filter to a signal.
Parameters
Expand Down Expand Up @@ -47,12 +47,10 @@ def filter_signal_fir(sig, fs, pass_type, f_range, n_cycles=3, n_seconds=None, r
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.
save_properties : str
Path, including file name, to save filter properites to as a json.
return_filter : bool, optional, default: False
If True, return the filter coefficients of the FIR filter.
verbose : bool, optional, default: False
If True, print out detailed filter information.
save_report : str, optional, default: None
Path, including file name, to save a filter report to as a pdf.
Returns
-------
Expand Down Expand Up @@ -83,9 +81,8 @@ def filter_signal_fir(sig, fs, pass_type, f_range, n_cycles=3, n_seconds=None, r
check_filter_length(sig.shape[-1], len(filter_coefs))

# Check filter properties: compute transition bandwidth & run checks
check_filter_properties(filter_coefs, 1, fs, pass_type, f_range, filt_type="FIR",
verbose=np.any([print_transitions, verbose]),
save_properties=save_properties)
_, properties = check_filter_properties(filter_coefs, 1, fs, pass_type, f_range,
return_properties=True, verbose=print_transitions)

# Remove any NaN on the edges of 'sig'
sig, sig_nans = remove_nans(sig)
Expand All @@ -100,11 +97,25 @@ def filter_signal_fir(sig, fs, pass_type, f_range, n_cycles=3, n_seconds=None, r
# Add NaN back on the edges of 'sig', if there were any at the beginning
sig_filt = restore_nans(sig_filt, sig_nans)

# Unpack filter properties
if plot_properties or save_report is not None:
f_db = properties['f_db']
db = properties['db']

if save_report is not None:
pass_bw = properties['pass_bw']
transition_bw = properties['transition_bw']
f_range_trans = properties['f_range_trans']

# Plot filter properties, if specified
if plot_properties:
f_db, db = compute_frequency_response(filter_coefs, 1, fs)
plot_filter_properties(f_db, db, fs, filter_coefs)

# Save a pdf filter report containing plots and parameters
if save_report is not None:
save_filt_report(save_report, pass_type, 'IIR', fs, f_db, db, pass_bw, transition_bw,
f_range, f_range_trans, len(f_db)-1, filter_coefs=filter_coefs)

if return_filter:
return sig_filt, filter_coefs
else:
Expand Down
34 changes: 22 additions & 12 deletions neurodsp/filt/iir.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
"""Filter signals with IIR filters."""

import numpy as np
from scipy.signal import butter, sosfiltfilt

from neurodsp.utils import remove_nans, restore_nans
from neurodsp.filt.utils import compute_nyquist, compute_frequency_response
from neurodsp.filt.utils import compute_nyquist, compute_frequency_response, save_filt_report
from neurodsp.filt.checks import check_filter_definition, check_filter_properties
from neurodsp.plts.filt import plot_frequency_response

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

def filter_signal_iir(sig, fs, pass_type, f_range, butterworth_order, print_transitions=False,
plot_properties=False, save_properties=None, return_filter=False,
verbose=False):
plot_properties=False, return_filter=False, save_report=None):
"""Apply an IIR filter to a signal.
Parameters
Expand Down Expand Up @@ -41,12 +39,10 @@ def filter_signal_iir(sig, fs, pass_type, f_range, butterworth_order, print_tran
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.
save_properties : str
Path, including file name, to save filter properites to as a json.
return_filter : bool, optional, default: False
If True, return the second order series coefficients of the IIR filter.
verbose : bool, optional, default: False
If True, print out detailed filter information.
save_report : str, optional, default: None
Path, including file name, to save a filter report to as a pdf.
Returns
-------
Expand All @@ -71,9 +67,8 @@ def filter_signal_iir(sig, fs, pass_type, f_range, butterworth_order, print_tran
sos = design_iir_filter(fs, pass_type, f_range, butterworth_order)

# Check filter properties: compute transition bandwidth & run checks
check_filter_properties(sos, None, fs, pass_type, f_range, filt_type="IIR",
verbose=np.any([print_transitions, verbose]),
save_properties=save_properties)
_, properties = check_filter_properties(sos, None, fs, pass_type, f_range,
return_properties=True, verbose=print_transitions)

# Remove any NaN on the edges of 'sig'
sig, sig_nans = remove_nans(sig)
Expand All @@ -84,11 +79,26 @@ def filter_signal_iir(sig, fs, pass_type, f_range, butterworth_order, print_tran
# Add NaN back on the edges of 'sig', if there were any at the beginning
sig_filt = restore_nans(sig_filt, sig_nans)

# Unpack filter properties
if plot_properties or save_report is not None:
f_db = properties['f_db']
db = properties['db']

if save_report is not None:
pass_bw = properties['pass_bw']
transition_bw = properties['transition_bw']
f_range_trans = properties['f_range_trans']

# Plot frequency response, if desired
if plot_properties:
f_db, db = compute_frequency_response(sos, None, fs)
plot_frequency_response(f_db, db)

# Save a pdf filter report containing plots and parameters
if save_report is not None:

save_filt_report(save_report, pass_type, 'IIR', fs, f_db, db, pass_bw,
transition_bw, f_range, f_range_trans, butterworth_order)

if return_filter:
return sig_filt, sos
else:
Expand Down
Loading

0 comments on commit 91eaf44

Please sign in to comment.