Skip to content

Commit 312fed7

Browse files
authored
Merge pull request #293 from fooof-tools/checkmodes
[MNT] - Fix management of check modes
2 parents 209c57d + d49edb3 commit 312fed7

File tree

10 files changed

+124
-23
lines changed

10 files changed

+124
-23
lines changed

doc/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ Objects to store settings, metadata and results for power spectrum models.
9191
:template: data_object.rst
9292

9393
FOOOFSettings
94+
FOOOFRunModes
9495
FOOOFMetaData
9596
FOOOFResults
9697

fooof/core/info.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def get_description():
1717
1818
- results : parameters for and measures of the model
1919
- settings : model settings
20+
- run_modes: checks performed and errors raised
2021
- data : input data
2122
- meta_data : meta data of the inputs
2223
- arrays : data stored in arrays
@@ -29,6 +30,7 @@ def get_description():
2930
'settings' : ['peak_width_limits', 'max_n_peaks',
3031
'min_peak_height', 'peak_threshold',
3132
'aperiodic_mode'],
33+
'run_modes': ['_debug', '_check_freqs', '_check_data'],
3234
'data' : ['power_spectrum', 'freq_range', 'freq_res'],
3335
'meta_data' : ['freq_range', 'freq_res'],
3436
'arrays' : ['freqs', 'power_spectrum', 'aperiodic_params_',

fooof/data/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Data sub-module for FOOOF."""
22

3-
from .data import FOOOFSettings, FOOOFMetaData, FOOOFResults, SimParams
3+
from .data import FOOOFSettings, FOOOFRunModes, FOOOFMetaData, FOOOFResults, SimParams

fooof/data/data.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ class FOOOFSettings(namedtuple('FOOOFSettings', ['peak_width_limits', 'max_n_pea
3838
__slots__ = ()
3939

4040

41+
class FOOOFRunModes(namedtuple('FOOOFRunModes', ['debug', 'check_freqs', 'check_data'])):
42+
"""Checks performed and errors raised by the model.
43+
44+
Parameters
45+
----------
46+
debug : bool
47+
Whether to run in debug mode.
48+
check_freqs : bool
49+
Whether to run in check freqs mode.
50+
check_data : bool
51+
Whether to run in check data mode.
52+
53+
Notes
54+
-----
55+
This object is a data object, based on a NamedTuple, with immutable data attributes.
56+
"""
57+
__slots__ = ()
58+
59+
4160
class FOOOFMetaData(namedtuple('FOOOFMetaData', ['freq_range', 'freq_res'])):
4261
"""Metadata information about a power spectrum.
4362

fooof/objs/fit.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@
4545
_debug : bool
4646
Whether the object is set in debug mode.
4747
This should be controlled by using the `set_debug_mode` method.
48-
_check_data : bool
49-
Whether to check added data for NaN or Inf values, and fail out if present.
50-
This should be controlled by using the `set_check_data_mode` method.
48+
_check_data, _check_freqs : bool
49+
Whether to check added inputs for incorrect inputs, failing if present.
50+
Frequency data is checked for linear spacing.
51+
Power values are checked for data for NaN or Inf values.
52+
These modes default to True, and can be controlled with the `set_check_modes` method.
5153
5254
Code Notes
5355
----------
@@ -76,7 +78,7 @@
7678
from fooof.plts.fm import plot_fm
7779
from fooof.utils.data import trim_spectrum
7880
from fooof.utils.params import compute_gauss_std
79-
from fooof.data import FOOOFResults, FOOOFSettings, FOOOFMetaData
81+
from fooof.data import FOOOFSettings, FOOOFRunModes, FOOOFMetaData, FOOOFResults
8082
from fooof.data.conversions import model_to_dataframe
8183
from fooof.sim.gen import gen_freqs, gen_aperiodic, gen_periodic, gen_model
8284

@@ -199,7 +201,7 @@ def __init__(self, peak_width_limits=(0.5, 12.0), max_n_peaks=np.inf, min_peak_h
199201
# Set default debug mode - controls if an error is raised if model fitting is unsuccessful
200202
self._debug = False
201203
# Set default data checking modes - controls which checks get run on input data
202-
# check_freqs: check the frequency values, and raises an error for uneven spacing
204+
# check_freqs: checks the frequency values, and raises an error for uneven spacing
203205
self._check_freqs = True
204206
# check_data: checks the power values and raises an error for any NaN / Inf values
205207
self._check_data = True
@@ -568,6 +570,19 @@ def get_settings(self):
568570
for key in OBJ_DESC['settings']})
569571

570572

573+
def get_run_modes(self):
574+
"""Return run modes of the current object.
575+
576+
Returns
577+
-------
578+
FOOOFRunModes
579+
Object containing the run modes from the current object.
580+
"""
581+
582+
return FOOOFRunModes(**{key.strip('_') : getattr(self, key) \
583+
for key in OBJ_DESC['run_modes']})
584+
585+
571586
def get_meta_data(self):
572587
"""Return data information from the current object.
573588
@@ -723,6 +738,24 @@ def set_debug_mode(self, debug):
723738
self._debug = debug
724739

725740

741+
def set_check_modes(self, check_freqs=None, check_data=None):
742+
"""Set check modes, which controls if an error is raised based on check on the inputs.
743+
744+
Parameters
745+
----------
746+
check_freqs : bool, optional
747+
Whether to run in check freqs mode, which checks the frequency data.
748+
check_data : bool, optional
749+
Whether to run in check data mode, which checks the power spectrum values data.
750+
"""
751+
752+
if check_freqs is not None:
753+
self._check_freqs = check_freqs
754+
if check_data is not None:
755+
self._check_data = check_data
756+
757+
758+
# This kept for backwards compatibility, but to be removed in 2.0 in favor of `set_check_modes`
726759
def set_check_data_mode(self, check_data):
727760
"""Set check data mode, which controls if an error is raised if NaN or Inf data are added.
728761
@@ -732,7 +765,24 @@ def set_check_data_mode(self, check_data):
732765
Whether to run in check data mode.
733766
"""
734767

735-
self._check_data = check_data
768+
self.set_check_modes(check_data=check_data)
769+
770+
771+
def set_run_modes(self, debug, check_freqs, check_data):
772+
"""Simultaneously set all run modes.
773+
774+
Parameters
775+
----------
776+
debug : bool
777+
Whether to run in debug mode.
778+
check_freqs : bool
779+
Whether to run in check freqs mode.
780+
check_data : bool
781+
Whether to run in check data mode.
782+
"""
783+
784+
self.set_debug_mode(debug)
785+
self.set_check_modes(check_freqs, check_data)
736786

737787

738788
def to_df(self, peak_org):

fooof/objs/group.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,9 @@ def get_fooof(self, ind, regenerate=True):
456456
The FOOOFResults data loaded into a FOOOF object.
457457
"""
458458

459-
# Initialize a FOOOF object, with same settings & check data mode as current FOOOFGroup
459+
# Initialize a FOOOF object, with same settings & run modes as current FOOOFGroup
460460
fm = FOOOF(*self.get_settings(), verbose=self.verbose)
461-
fm.set_check_data_mode(self._check_data)
461+
fm.set_run_modes(*self.get_run_modes())
462462

463463
# Add data for specified single power spectrum, if available
464464
# The power spectrum is inverted back to linear, as it is re-logged when added to FOOOF
@@ -494,8 +494,9 @@ def get_group(self, inds):
494494
# Check and convert indices encoding to list of int
495495
inds = check_inds(inds)
496496

497-
# Initialize a new FOOOFGroup object, with same settings as current FOOOFGroup
497+
# Initialize a new FOOOFGroup object, with same settings and run modes as current FOOOFGroup
498498
fg = FOOOFGroup(*self.get_settings(), verbose=self.verbose)
499+
fg.set_run_modes(*self.get_run_modes())
499500

500501
# Add data for specified power spectra, if available
501502
# The power spectra are inverted back to linear, as they are re-logged when added to FOOOF

fooof/sim/params.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,16 @@ def _check_values(start, stop, step):
146146
If the given values for defining the iteration range are inconsistent.
147147
"""
148148

149-
if any(ii < 0 for ii in [start, stop, step]):
150-
raise ValueError("Inputs 'start', 'stop', and 'step' should all be positive values.")
149+
if any(ii < 0 for ii in [start, stop]):
150+
raise ValueError("Inputs 'start' and 'stop' should be positive values.")
151151

152-
if not start < stop:
153-
raise ValueError("Input 'start' should be less than 'stop'.")
152+
if (stop - start) * step < 0:
153+
raise ValueError("The sign of input 'step' does not align with 'start' / 'stop' values.")
154154

155-
if not step < (stop - start):
155+
if start == stop:
156+
raise ValueError("Input 'start' and 'stop' must be different values.")
157+
158+
if not abs(step) < abs(stop - start):
156159
raise ValueError("Input 'step' is too large given values for 'start' and 'stop'.")
157160

158161

fooof/tests/core/test_info.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ def test_get_description(tfm):
1515
for it in va:
1616
assert it in objs
1717

18-
1918
def test_get_peak_indices():
2019

2120
indices = get_peak_indices()
@@ -33,7 +32,6 @@ def test_get_ap_indices():
3332
for ind, val in enumerate(['offset', 'exponent']):
3433
assert indices_fixed[val] == ind
3534

36-
3735
indices_knee = get_indices('knee')
3836

3937
assert indices_knee

fooof/tests/data/test_data.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ def test_fooof_settings():
1919
for field in OBJ_DESC['settings']:
2020
assert getattr(settings, field)
2121

22+
def test_fooof_run_modes():
23+
24+
run_modes = FOOOFRunModes(True, True, True)
25+
assert run_modes
26+
27+
for field in OBJ_DESC['run_modes']:
28+
assert getattr(run_modes, field.strip('_'))
29+
2230
def test_fooof_meta_data():
2331

2432
meta_data = FOOOFMetaData([1, 50], 0.5)

fooof/tests/objs/test_fit.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -414,17 +414,24 @@ def test_fooof_debug():
414414
with raises(FitError):
415415
tfm.fit(*gen_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4]))
416416

417-
def test_fooof_check_data():
418-
"""Test FOOOF in with check data mode turned off, including with NaN data."""
417+
def test_fooof_set_check_modes(tfm):
418+
"""Test changing check_modes using set_check_modes, and that checks get turned off.
419+
Note that testing for checks raising errors happens in test_fooof_checks.`"""
419420

420421
tfm = FOOOF(verbose=False)
421422

422-
tfm.set_check_data_mode(False)
423+
tfm.set_check_modes(False, False)
424+
assert tfm._check_freqs is False
423425
assert tfm._check_data is False
424426

425-
# Add data, with check data turned off
426-
# In check data mode, adding data with NaN should run
427-
freqs = gen_freqs([3, 50], 0.5)
427+
# Add bad frequency data, with check freqs turned off
428+
freqs = np.array([1, 2, 4])
429+
powers = np.array([1, 2, 3])
430+
tfm.add_data(freqs, powers)
431+
assert tfm.has_data
432+
433+
# Add bad power values data, with check data turned off
434+
freqs = gen_freqs([3, 30], 1)
428435
powers = np.ones_like(freqs) * np.nan
429436
tfm.add_data(freqs, powers)
430437
assert tfm.has_data
@@ -433,6 +440,18 @@ def test_fooof_check_data():
433440
tfm.fit()
434441
assert not tfm.has_model
435442

443+
# Reset check modes to true
444+
tfm.set_check_modes(True, True)
445+
assert tfm._check_freqs is True
446+
assert tfm._check_data is True
447+
448+
def test_set_run_modes():
449+
450+
tfm = FOOOF(verbose=False)
451+
tfm.set_run_modes(False, False, False)
452+
for field in OBJ_DESC['run_modes']:
453+
assert getattr(tfm, field) is False
454+
436455
def test_fooof_to_df(tfm, tbands, skip_if_no_pandas):
437456

438457
df1 = tfm.to_df(2)

0 commit comments

Comments
 (0)