diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e7cec45..4164b2e1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: # When ready to drop 3.6, can revert from 'ubuntu-20.04' -> 'ubuntu-latest' runs-on: ubuntu-20.04 env: - MODULE_NAME: fooof + MODULE_NAME: specparam strategy: matrix: python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9792b800..067aca4a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing Guidelines -Thank you for your interest in contributing to `fooof`! +Thank you for your interest in contributing to `specparam`! We welcome all contributions to the project that extend or improve code and/or documentation! @@ -55,7 +55,7 @@ and see if there is anything you would be interested in helping with. If so, joi All contributions must be within the scope of the module. -`fooof` is a module for parameterizing neural power spectra. This includes model fitting, management and analysis of resulting parameters, and utilities to visualize power spectra and model results. This module also includes functionality to simulate power spectra based on the model. +`specparam` is a module for parameterizing neural power spectra. This includes model fitting, management and analysis of resulting parameters, and utilities to visualize power spectra and model results. This module also includes functionality to simulate power spectra based on the model. Procedures and utilities that do not deal with operating upon power spectra or on model outputs will most likely be considered out of scope. Notably, this model does not include doing spectral estimation or time-domain analysis. For approaches such as these, the [neurodsp](https://github.com/neurodsp-tools/neurodsp/) module may be a more appropriate target. @@ -120,8 +120,8 @@ All code contributed to the module should follow these conventions: * If a new approach is added, a new tutorial or example may be appropriate * To build and check the documentation locally: * Install the requirements for the docsite (`pip install -r requirements-doc.txt`) - * Move to the `fooof/doc` directory (`cd doc`) + * Move to the `specparam/doc` directory (`cd doc`) * Run `make html` to create a local copy of the documentation website - * The documentation can then be opened in a web browser by opening the file `fooof/doc/_build/html/index.html` + * The documentation can then be opened in a web browser by opening the file `specparam/doc/_build/html/index.html` For more guidelines on how to write well formated and organized code, check out the [Python API Checklist](http://python.apichecklist.com). diff --git a/Makefile b/Makefile index bc19ef9f..5649e8fd 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ ########################################################################## ## VARIABLES -MODULE = fooof +MODULE = specparam LINT_FILE = _lint.txt ########################################################################## diff --git a/README.rst b/README.rst index f771b99e..e0ff6c6c 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ -========================================= -FOOOF - fitting oscillations & one over f -========================================= +========================= +Spectral Parameterization +========================= |ProjectStatus|_ |Version|_ |BuildStatus|_ |Coverage|_ |License|_ |PythonVersions|_ |Paper|_ @@ -26,7 +26,7 @@ FOOOF - fitting oscillations & one over f .. _Paper: https://doi.org/10.1038/s41593-020-00744-x -FOOOF is a fast, efficient, and physiologically-informed tool to parameterize neural power spectra. +Spectral parameterization is a fast, efficient, and physiologically-informed tool to parameterize neural power spectra. Overview -------- @@ -71,7 +71,7 @@ This documentation includes: Dependencies ------------ -FOOOF is written in Python, and requires Python >= 3.6 to run. +SpecParam is written in Python, and requires Python >= 3.6 to run. It has the following required dependencies: @@ -82,7 +82,7 @@ There are also optional dependencies, which are not required for model fitting i - `matplotlib `_ is needed to visualize data and model fits - `tqdm `_ is needed to print progress bars when fitting many models -- `pandas `_ is needed to for exporting model fit results to dataframes +- `pandas `_ is needed for exporting model fit results to dataframes - `pytest `_ is needed to run the test suite locally We recommend using the `Anaconda `_ distribution to manage these requirements. @@ -133,12 +133,12 @@ To install an editable version, download the development version as above, and r Other Language Support ---------------------- -The original implementation of FOOOF, available in this repository, is implemented in Python. +The original implementation of `specparam`, available in this repository, is implemented in Python. -If you wish to run FOOOF from another language, there are a couple potential options: +If you wish to run specparam from another language, there are a couple potential options: - a `wrapper`, which allows for running the Python code from another language -- a `reimplementation`, which reflects a new implementation of the fooof algorithm in another language +- a `reimplementation`, which reflects a new implementation of the specparam algorithm in another language Below are listed some examples of wrappers and/or reimplementations in other languages (non-exhaustive). @@ -146,9 +146,8 @@ Matlab ~~~~~~ In Matlab, there is a reimplementation available in common toolboxes: - -- The `Brainstorm `_ toolbox has a reimplementation of fooof (see the `Brainstorm fooof tutorial `_) -- The `Fieldtrip `_ also uses the same reimplementation (see the `Fieldtrip fooof tutorial `_) +- The `Brainstorm `_ toolbox has a reimplementation of specparam (see the `Brainstorm fooof tutorial `_) +- The `Fieldtrip `_ toolbox also uses the same reimplementation (see the `Fieldtrip fooof tutorial `_) There is also a Matlab wrapper in the `fooof_mat `_ repository. @@ -209,27 +208,27 @@ The algorithm works on frequency representations, that is power spectra in linea **Fitting a Single Power Spectrum** With a power spectrum loaded (with 'freqs' storing frequency values, and 'spectrum' storing -the power spectrum, both as 1D arrays in linear space) FOOOF can be used as follows: +the power spectrum, both as 1D arrays in linear space) parameterization can be done as follows: .. code-block:: python - # Import the FOOOF object - from fooof import FOOOF + # Import the model object + from specparam import SpectralModel - # Initialize FOOOF object - fm = FOOOF() + # Initialize model object + fm = SpectralModel() # Define frequency range across which to model the spectrum freq_range = [3, 40] - # Model the power spectrum with FOOOF, and print out a report + # Parameterize the power spectrum, and print out a report fm.report(freqs, spectrum, freq_range) -FOOOF.report() fits the model, plots the original power spectrum with the associated FOOOF model fit, +SpectralModel.report() fits the model, plots the original power spectrum with the associated model fit, and prints out the parameters of the model fit for both the aperiodic component, and parameters for any identified peaks, reflecting periodic components. -Example output for the report of a FOOOF fit on an individual power spectrum: +Example output for the report of a parameterized fit on an individual power spectrum: .. image:: https://raw.githubusercontent.com/fooof-tools/fooof/main/doc/img/FOOOF_report.png @@ -247,9 +246,9 @@ These settings can be defined when initializing the model, for example: .. code-block:: python - # Initialize a FOOOF model object with defined settings - fm = FOOOF(peak_width_limits=[1.0, 8.0], max_n_peaks=6, min_peak_height=0.1, - peak_threshold=2.0, aperiodic_mode='fixed') + # Initialize a model object with defined settings + fm = SpectralModel(peak_width_limits=[1.0, 8.0], max_n_peaks=6, min_peak_height=0.1, + peak_threshold=2.0, aperiodic_mode='fixed') **Fitting a Group of Power Spectra** @@ -259,19 +258,19 @@ We can fit the group of power spectra by doing: .. code-block:: python - # Initialize a FOOOFGroup object, specifying some parameters - fg = FOOOFGroup(peak_width_limits=[1.0, 8.0], max_n_peaks=8) + # Initialize a SpectralGroupModel object, specifying some parameters + fg = SpectralGroupModel(peak_width_limits=[1.0, 8.0], max_n_peaks=8) - # Fit FOOOF model across the matrix of power spectra + # Fit models across the matrix of power spectra fg.fit(freqs, spectra) # Create and save out a report summarizing the results across the group of power spectra fg.save_report() - # Save out FOOOF results for further analysis later - fg.save(file_name='fooof_group_results', save_results=True) + # Save out results for further analysis later + fg.save(file_name='group_results', save_results=True) -Example output from using FOOOFGroup across a group of power spectra: +Example output from using SpectralGroupModel across a group of power spectra: .. image:: https://raw.githubusercontent.com/fooof-tools/fooof/main/doc/img/FOOOFGroup_report.png diff --git a/data/README.txt b/data/README.txt index ca92c031..ff23d201 100644 --- a/data/README.txt +++ b/data/README.txt @@ -1,4 +1,4 @@ Data ==== -Example data files for the FOOOF module. \ No newline at end of file +Example data files for the module. diff --git a/doc/Makefile b/doc/Makefile index 4f9a5cff..f04cd3a4 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -7,7 +7,7 @@ # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build -SPHINXPROJ = fooof +SPHINXPROJ = specparam SOURCEDIR = . BUILDDIR = _build @@ -44,7 +44,7 @@ check: make SPHINXOPTS="-n" html make linkcheck -# Create the plots used in the FOOOF documentation +# Create the plots used in the documentation plots: python make_doc_plots.py diff --git a/doc/api.rst b/doc/api.rst index f57f0ab4..54b9f1af 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -4,7 +4,7 @@ API Documentation ================= -API reference for the FOOOF module. +API reference for the module. Table of Contents ================= @@ -13,54 +13,54 @@ Table of Contents :local: :depth: 2 -.. currentmodule:: fooof +.. currentmodule:: specparam Model Objects ------------- Objects that manage data and fit the model to parameterize neural power spectra. -FOOOF Object -~~~~~~~~~~~~ +Base Object +~~~~~~~~~~~ -The FOOOF object is the base object for the model, and can be used to fit individual power spectra. +The SpectralModel object is the base object for the model, and can be used to fit individual power spectra. .. autosummary:: :toctree: generated/ - FOOOF + SpectralModel -FOOOFGroup Object -~~~~~~~~~~~~~~~~~ +Group Object +~~~~~~~~~~~~ -The FOOOFGroup object allows for parameterizing groups of power spectra. +The SpectralGroupModel object allows for parameterizing groups of power spectra. .. autosummary:: :toctree: generated/ - FOOOFGroup + SpectralGroupModel Object Utilities ~~~~~~~~~~~~~~~~ -Functions to manipulate, examine and analyze FOOOF objects, and related utilities. +Functions to manipulate, examine, and analyze model objects. -.. currentmodule:: fooof.objs +.. currentmodule:: specparam.objs .. autosummary:: :toctree: generated/ - compare_info - average_fg + compare_model_objs + combine_model_objs + average_group average_reconstructions - combine_fooofs -.. currentmodule:: fooof +.. currentmodule:: specparam .. autosummary:: :toctree: generated/ - fit_fooof_3d + fit_models_3d Data Objects ------------ @@ -72,7 +72,7 @@ Bands Object An object to handle frequency band definitions. -.. currentmodule:: fooof +.. currentmodule:: specparam .. autosummary:: :toctree: generated/ @@ -84,16 +84,16 @@ Model Information Objects to store settings, metadata and results for power spectrum models. -.. currentmodule:: fooof.data +.. currentmodule:: specparam.data .. autosummary:: :toctree: generated/ :template: data_object.rst - FOOOFSettings - FOOOFRunModes - FOOOFMetaData - FOOOFResults + SpectrumMetaData + ModelSettings + ModelRunModes + FitResults Simulation Parameters ~~~~~~~~~~~~~~~~~~~~~ @@ -118,26 +118,26 @@ Functions for analyzing the error of model fits. **Object Inputs** -The following functions take in FOOOF objects directly, which is the recommended approach. +The following functions take in model objects directly. -.. currentmodule:: fooof.analysis +.. currentmodule:: specparam.analysis .. autosummary:: :toctree: generated/ - compute_pointwise_error_fm - compute_pointwise_error_fg + compute_pointwise_error + compute_pointwise_error_group **Array Inputs** The following functions operate on arrays of models and data, which may be useful for more custom work-flows. -.. currentmodule:: fooof.analysis.error +.. currentmodule:: specparam.analysis.error .. autosummary:: :toctree: generated/ - compute_pointwise_error + compute_pointwise_error_arr Analyze Periodic Components ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -146,27 +146,27 @@ Functions for analyzing the periodic components of model fits. **Object Inputs** -The following functions take in FOOOF objects directly, which is the typical use case. +The following functions take in model objects directly, which is the typical use case. -.. currentmodule:: fooof.analysis +.. currentmodule:: specparam.analysis .. autosummary:: :toctree: generated/ - get_band_peak_fm - get_band_peak_fg + get_band_peak + get_band_peak_group **Array Inputs** The following functions operate on arrays of peak parameters, which may be useful for more custom work-flows. -.. currentmodule:: fooof.analysis.periodic +.. currentmodule:: specparam.analysis.periodic .. autosummary:: :toctree: generated/ - get_band_peak - get_band_peak_group + get_band_peak_arr + get_band_peak_group_arr get_highest_peak threshold_peaks @@ -180,21 +180,20 @@ Generate Power Spectra Functions for simulating neural power spectra. -.. currentmodule:: fooof.sim +.. currentmodule:: specparam.sim .. autosummary:: :toctree: generated/ - gen_freqs - gen_power_spectrum - gen_group_power_spectra + sim_power_spectrum + sim_group_power_spectra Manage Parameters ~~~~~~~~~~~~~~~~~ Functions and objects for managing parameters for simulated power spectra. -.. currentmodule:: fooof.sim.params +.. currentmodule:: specparam.sim.params .. autosummary:: :toctree: generated/ @@ -210,7 +209,7 @@ Transform Power Spectra Functions for transforming power spectra. -.. currentmodule:: fooof.sim.transform +.. currentmodule:: specparam.sim.transform .. autosummary:: :toctree: generated/ @@ -227,11 +226,12 @@ Simulation Utilities Utilities for simulating power spectra. -.. currentmodule:: fooof.sim.utils +.. currentmodule:: specparam.sim.utils .. autosummary:: :toctree: generated/ + create_freqs set_random_seed Plotting Functions @@ -244,7 +244,7 @@ Plot Power Spectra Plots for visualizing power spectra. -.. currentmodule:: fooof.plts +.. currentmodule:: specparam.plts .. autosummary:: :toctree: generated/ @@ -253,7 +253,7 @@ Plots for visualizing power spectra. Plots for plotting power spectra with shaded regions. -.. currentmodule:: fooof.plts.spectra +.. currentmodule:: specparam.plts.spectra .. autosummary:: :toctree: generated/ @@ -266,7 +266,7 @@ Plot Model Properties & Parameters Plots for visualizing **periodic** parameters and model components. -.. currentmodule:: fooof.plts.periodic +.. currentmodule:: specparam.plts.periodic .. autosummary:: :toctree: generated/ @@ -276,7 +276,7 @@ Plots for visualizing **periodic** parameters and model components. Plots for visualizing **aperiodic** parameters and model components. -.. currentmodule:: fooof.plts.aperiodic +.. currentmodule:: specparam.plts.aperiodic .. autosummary:: :toctree: generated/ @@ -286,39 +286,39 @@ Plots for visualizing **aperiodic** parameters and model components. Plots for visualizing model error. -.. currentmodule:: fooof.plts.error +.. currentmodule:: specparam.plts.error .. autosummary:: :toctree: generated/ plot_spectral_error -Plot FOOOF Objects +Plot Model Objects ~~~~~~~~~~~~~~~~~~ -Plots for visualizing models from FOOOF objects. -Note that these are the same plotting functions that can be called from FOOOF objects directly. +Plots for visualizing from model objects. +Note that these are the same plotting functions that can be called from the model objects directly. -.. currentmodule:: fooof.plts.fm +.. currentmodule:: specparam.plts.model .. autosummary:: :toctree: generated/ - plot_fm + plot_model -.. currentmodule:: fooof.plts.fg +.. currentmodule:: specparam.plts.group .. autosummary:: :toctree: generated/ - plot_fg + plot_group Annotated Plots ~~~~~~~~~~~~~~~ Annotated plots that describe the model and fitting process. -.. currentmodule:: fooof.plts.annotate +.. currentmodule:: specparam.plts.annotate .. autosummary:: :toctree: generated/ @@ -357,7 +357,7 @@ Data Utilities Utilities for working with data. -.. currentmodule:: fooof.utils +.. currentmodule:: specparam.utils .. autosummary:: :toctree: generated/ @@ -372,7 +372,7 @@ Parameter Utilities Utilities for working with parameters -.. currentmodule:: fooof.utils.params +.. currentmodule:: specparam.utils.params .. autosummary:: :toctree: generated/ @@ -382,20 +382,20 @@ Utilities for working with parameters Input / Output (IO) ~~~~~~~~~~~~~~~~~~~ -.. currentmodule:: fooof.utils.io +.. currentmodule:: specparam.utils.io .. autosummary:: :toctree: generated/ - load_fooof - load_fooofgroup + load_model + load_group Methods Reports ~~~~~~~~~~~~~~~ Utilities to creating methods reports. -.. currentmodule:: fooof.utils.reports +.. currentmodule:: specparam.utils.reports .. autosummary:: :toctree: generated/ diff --git a/doc/changelog.rst b/doc/changelog.rst index f496fd98..aa5d6bab 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -22,11 +22,11 @@ the upcoming `specparam 2.0.0` release. ----- Warning: the 1.X.X release series is an API breaking release from the prior versions, -in the beta series (0.X.X). It is a stable release version of the module. +in the beta release series (0.X.X). It is a stable release version of the module. As compared to the prior series (0.X.X), some names and module organizations have changed. -This means existing code that uses FOOOF may no longer work as currently written with the -new version, and may need updating. You should update to the new version when you are ready to +This means existing code may no longer work as currently written with the new version, +and may need updating. You should update to the new version when you are ready to update your code to reflect the new changes. Note that the main changes are in code organization, some names, and the addition of @@ -75,10 +75,10 @@ Code Updates The 1.X.X series adds a large number of code updates & additions, including: - A big extension of the plotting sub-module -- Adding new functions to manipulate, manage, organize and manage FOOOF objects +- Adding new functions to manipulate, manage, organize and manage model objects - Add new analysis functions, including more utilities for checking model errors - Add a new 'Bands' object for managing frequency band definitions -- Extra methods on FOOOF & FOOOFGroup objects for managing data & results +- Extra methods on model objects for managing data & results - Miscellaneous bug fixes & other additions The full history of changes is available in the Github commit and pull request history. @@ -86,7 +86,7 @@ The full history of changes is available in the Github commit and pull request h The bulk of the updates for 1.X.X went through in the following pull requests: - #152: broad updates, including lots of issue fixes, and code & documentation extensions -- #139: add 'Bands' object and more functions for managing FOOOF objects +- #139: add 'Bands' object and more functions for managing model objects - #130: updates data objects and internal data management - #121 & #129: code reorganizations & cleanups - #122: Updating terminology and names @@ -110,7 +110,7 @@ Note that if you have data saved out from the 0.X.X release series of the module code update to the 1.X.X series won't be able to properly load this data out of the box. This is due to the naming changes, and in particular the change from 'background' to -'aperiodic'. Note that saved FOOOF files are plain-text JSON files, and so if you find & replace +'aperiodic'. Note that saved files are plain-text JSON files, and so if you find & replace the word 'background' to 'aperiodic', this should update the files so that they can be loaded by the 1.X.X version. Note that if you also saved out the algorithm settings, you may need to update the name of `min_peak_amplitude` to `min_peak_height` as well. @@ -118,7 +118,7 @@ the name of `min_peak_amplitude` to `min_peak_height` as well. 0.1.X ----- -The 0.1.X series was the initial release series of beta versions of the FOOOF module. +The 0.1.X series was the initial release series of beta versions of the module. The old series of releases has a different naming scheme and module organization to the current 1.X.X series, and is now deprecated, with no plans to update or maintain this version. diff --git a/doc/conf.py b/doc/conf.py index 253382ed..b0724d82 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -21,12 +21,12 @@ # -- Project information ----------------------------------------------------- # Set project information -project = 'fooof' +project = 'specparam' copyright = '2018-{}, VoytekLab'.format(date.today().year) author = 'Thomas Donoghue' # Get and set the current version number -from fooof import __version__ +from specparam import __version__ version = __version__ release = version @@ -137,8 +137,8 @@ 'within_subsection_order': FileNameSortKey, 'default_thumb_file': 'img/spectrum.png', 'backreferences_dir': 'generated', # Where to drop linking files between examples & API - 'doc_module': ('fooof',), - 'reference_url': {'fooof': None}, + 'doc_module': ('specparam',), + 'reference_url': {'specparam': None}, 'remove_config_comments': True, } diff --git a/doc/faq.rst b/doc/faq.rst index a1eb10ae..052e9ccc 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -1,7 +1,7 @@ Frequently Asked Questions ========================== -The following are a collection of frequently asked questions and answers about FOOOF. +The following is a collection of frequently asked questions and answers about spectral parameterization. These answers focus on the ideas and concepts relating to parameterizing neural power spectra. @@ -13,23 +13,23 @@ Table of Contents :local: :backlinks: none -What is FOOOF? -~~~~~~~~~~~~~~ +What is spectral parameterization? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -FOOOF is an open-source Python module for parameterizing neural power spectra. +Spectral parameterization means fitting a model to describe power spectra. +A particular algorithm and implementation for doing this is available in `specparam`, +an open-source Python module for parameterizing neural power spectra. -The parameterization uses a model-driven approach that assumes that neurophysiological time +Spectral parameterization uses a model-driven approach that assumes that neurophysiological time series are comprised of two separable components, reflecting periodic (or oscillatory) and -aperiodic activity. - -This approach therefore does rely on the assumption that these two components are indeed separable +aperiodic activity. This approach relies on the assumption that these two components are indeed separable components of the underlying data, though it is agnostic to their physiological origin and putative functional roles. -The parameterization approach operates on frequency representations of neurophysiological times +Spectral parameterization operates on frequency representations of neurophysiological times series (power spectra). At it's core, the module contains an algorithm to measure these two components - the periodic and aperiodic components - in power spectra. The final model -of the neural power spectrum, consists of quantifications of each of the two components, as well as +of the neural power spectrum consists of quantifications of each of the two components, as well as a combined model fit of the whole power spectrum. The full mathematical description of the model is described in the tutorials. @@ -38,10 +38,10 @@ What is meant by 'aperiodic' activity? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By 'aperiodic' activity we mean non-periodic (or arrhythmic) activity, meaning activity that -has no characteristic frequency. For a general example, white noise, would be considered to -be an aperiodic signal. +has no characteristic frequency. For example, white noise, would be considered to be an +aperiodic signal. -In neural data, the aperiodic component of the signal, typically follows a 1/f-like distribution, +In neural data, the aperiodic component of the signal typically follows a 1/f-like distribution, whereby power systematically decreases with increasing frequencies. Due to the aperiodic component, in a neural power spectrum, there is power at all frequencies, though this does not imply there is rhythmic power. @@ -51,12 +51,12 @@ What is meant by 'periodic' activity? By 'periodic' activity we mean features of the signal that are rhythmic, with activity at a characteristic frequency. This kind of activity is typically referred to as -neural oscillations. +'oscillatory' or reflecting neural oscillations. In practice, putative oscillations, reflecting periodic activity, are operationally defined and detectable by the fitting algorithm if they exhibit as band-limited power over and above -the aperiodic component of the signal in the power spectrum. This 'peak' of power over the -aperiodic is taken as evidence of frequency specific power, distinct from the aperiodic component. +the aperiodic component in the power spectrum. This 'peak' of power over the aperiodic is taken as +evidence of frequency specific power, distinct from the aperiodic component. Note that this periodic activity need not be continuous, as oscillatory activity often exhibits as 'bursts' in the time series, nor sinusoidal, as rhythmic neural activity is @@ -94,17 +94,16 @@ Why is it important to measure aperiodic activity? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Aperiodic activity has long known to be present in neural data, but has been less of a -research focus, as compared to periodic activity. Recent work has demonstrated +research focus (as compared to periodic activity). Recent work has demonstrated that aperiodic activity is dynamic, and systematically varies both within [1_] and between [2_] subjects, and has suggested potential physiological interpretations of aperiodic activity [3_] (see also below for more on this). We consider measuring aperiodic activity to be important for two reasons: -- Aperiodic activity is always there, and it is dynamic. Even if periodic activity - is the focus of the analysis, quantification of such data must explicitly account - for aperiodic activity to appropriately measure which components of the data are actually - changing. +- Aperiodic activity is always there, and it is dynamic. Even if periodic activity is the + focus of the analysis, quantification of such data must explicitly account for aperiodic + activity to accurately measure which components of the data are actually changing. - Aperiodic components of neural signals may be important and interesting in their own right as an interesting signal to investigate. This is motivated by findings that aperiodic activity is dynamic, correlates with other features of interest, and is of theoretical interest [1_, 2_, 3_]. @@ -144,12 +143,12 @@ that the aperiodic component may be a signal of interest, and not merely 'noise' Overall, we have moved to using the term 'aperiodic' to relate to any activity that is, descriptively, non-periodic. We prefer this term, as a neutral descriptor, to avoid implying particular theoretical interpretations, and/or what aspects of the signal -or of interest for any particular investigation. +are of interest for any particular investigation. Why are spectral peak used as evidence of periodic activity? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Due to neural signals containing aperiodic activity, there will always be power within +Since neural activity contains aperiodic activity, there will always be power within any given frequency range. If this aperiodic activity changes, the measured power within a predefined frequency range can also change. All this can occur without any truly periodic activity being present in the data. Even if there is periodic activity, quantifications of it @@ -168,8 +167,7 @@ If, for a given frequency band, no peak is detected in the power spectrum, this consistent with there being no periodic activity at that frequency. Without a detected peak, we argue that there is not evidence of periodic activity, at that frequency, over and above the power as expected by the aperiodic activity. In this situation, one should be very -wary of interpreting activity at this frequency, as it is most likely reflects aperiodic -activity. +wary of interpreting activity at this frequency, as it is likely reflects aperiodic activity. However, one cannot prove a negative, of course, and so the absence of a detected peak does not imply that there is definitively no periodic activity at that particular frequency. There could @@ -183,9 +181,9 @@ Peaks, defined as regions of power over and above the aperiodic component, are c to be putative periodic activity. However, there is not necessarily a one-to-one mapping between power spectrum peaks, and oscillations in the data. -One reason for this is that sometimes overlapping peaks can be fit to what is may -be a single oscillatory component in the data. This can happen if the peak in the power -spectrum is asymmetric. Since peaks are fit with gaussians, the model sometimes fits +One reason for this is that sometimes overlapping peaks can be fit to what may be a +single oscillatory component in the data. This can happen if the peak in the power +spectrum is asymmetric. Since peaks are fit with Gaussians, the model sometimes fits partially overlapping peaks to fit what may be a single asymmetric peak in the data. Because of this, it is often useful to focus on the dominant (highest power) peak within a @@ -213,7 +211,7 @@ There are many existing methods for analyzing periodic activity, and also other analyzing aperiodic activity. Most existing methods are designed to measure one or the other signal component. Few methods attempt to explicitly separate and quantify both the periodic and aperiodic components of the signal. This combined approach is a key factor that we -consider to be important for getting the measurements to work well. By jointly learning +consider to be important for getting the measurements to work well. By jointly measuring both components, the method is more capable of quantifying which aspects of the data are changing and in what ways. @@ -237,7 +235,7 @@ the model can be fit. The fitting algorithm is otherwise broadly agnostic to det Note that data from different modalities, or across different frequency ranges, may require different algorithm settings. -More information for checking for if the model fit seems to be appropriate, and for picking +More information on checking if model fits are appropriate, and for picking settings and tuning them to different datasets are all available in the Tutorials. Are there settings for the fitting algorithm? @@ -253,20 +251,19 @@ is covered in the tutorials. How should algorithm settings be chosen? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For any given dataset, there is often some tuning of the algorithm settings needed to -get models to fit well. For any given dataset, settings should therefore be checked, and -tuned if necessary, though, overall, model fits tend not to be overly sensitive to small -changes in the settings. +For any given dataset, there is often some tuning of the algorithm settings needed to get +models to fit well. For any given dataset, settings should therefore be checked, and tuned +if necessary. Model fits tend not to be overly sensitive to small changes in the settings. -One strategy for choosing settings, is to select a subset of power spectra from the +One strategy for choosing settings is to select a subset of power spectra from the dataset to use as something analogous to a 'training set'. This group of spectra can be -used to fit power spectrum models, check model fit properties, visually inspect fits, and -choose the best settings for the data. Once settings have been chosen for the subset, -they can applied to the dataset to be analyzed. Note that in order to be able to systematically +used to fit power spectrum models, check model fit properties, and visually inspect fits, +in order to choose the best settings for the data. Once settings have been chosen for the subset, +they can applied to the full dataset to be analyzed. Note that in order to be able to systematically compare model fits between conditions / tasks / subjects, etc, we recommend using the same algorithm settings across the whole dataset. -Details of what the algorithm settings are, and how to set them are available in the code Tutorials. +Details of what the algorithm settings are, and how to set them are available in the Tutorials. What frequency range should the model be fit on? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -282,9 +279,9 @@ By comparison, an analysis in ECoG that wants to include high frequency activity use a range of [1, 150], or perhaps [50, 150] if the goal is to focus specifically on high frequency activity. -Picking a frequency range should be considered in the context of choosing the -aperiodic mode, as whether or not a 'knee' should be fit depends in part on the frequency -range that is being examined. For more information on choosing the aperiodic mode, see the Tutorials. +Picking a frequency range should be considered in the context of choosing the aperiodic +mode, as whether or not a 'knee' should be fit depends in part on the frequency range that +is being examined. For more information on choosing the aperiodic mode, see the Tutorials. If I am interested in a particular oscillation band, should I fit a small range? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -322,27 +319,27 @@ potential arise from balanced activity of excitatory (E) and inhibitory (I) syna currents. In this model, changes in aperiodic properties of the data relate to changes in EI balance [3_]. -Does it matter how power spectra are calculated? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Does it matter how power spectra are computed? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For the most part, it does not matter exactly how power spectra to be parameterized -are calculated. The algorithm is agnostic to precise details of calculating power +are computed. The algorithm is agnostic to precise details of calculating power spectra, and so different estimation methods should all be fine. Regardless of how power spectra are computed, certain properties of the power spectra do influence how the parameterization goes. For example, the better the frequency resolution, the more precisely the algorithm will be able to estimate center frequencies and bandwidths -of detected peaks. However, as a trade off, using longer time segments to end up with 'smoother' -spectra can also help with getting the algorithm to fit better. +of detected peaks. However, as a trade off, averaging over a greater number of shorter windows +may help to end up with 'smoother' spectra, which may help with getting better fits. -Can this be applied to task or trial based analyses? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Can spectral parameterization be applied to task data? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Yes, power spectra can be fit in task based analyses. However, one thing to keep in mind is the resolution of the data. The shorter the -time segments of data used, and/or the fewer data segments averaged over, can lead to -'messy' power spectra, which may not be fit very well by the model. +time segments of data used, and/or the fewer data segments averaged over, the 'messier' +the power spectra may be. Noisy power spectra may not be fit very well by the model. With these considerations in mind, there are broadly two approaches for task related analyses: @@ -350,17 +347,17 @@ With these considerations in mind, there are broadly two approaches for task rel fitting one power spectrum model per condition - This doesn't allow for measurements per trial, but averaging across trials allows - for smoother spectra, and better model fits, per condition. This approach may be - for short trials, as the trial averaging allows want to use FOOOF to characterize - short time segments in a task design. + for smoother spectra, and better model fits, per condition. This approach may be better + for short trials, as the trial averaging allows for getting better estimates of trial + activity, per condition, in a way that may be difficult to estimate per trial. - Calculate power spectra and fit power spectrum models per trial, analyzing the distribution of model parameters outputs per condition - This approach can be used with longer trials, when there are relatively long time segments to fit. Model fits of individual trials are likely to be somewhat messy, but - as long as there is not a systematic bias in the fits, then the distributions of fit values - can be interpreted and compared. + as long as there is not a systematic bias in the fits, then the distributions of fit + values across and between trials can be interpreted and compared. - Exactly how much long segments need to to be analyzed in this way is somewhat dependent on the cleanliness of the data. As a rule of thumb, we currently recommend using segments of at least about 500 ms for this approach. @@ -382,15 +379,15 @@ spectral features across time, somewhat analogous to a spectrogram. This functionality is not currently available or described in the current module, but is a focus off current work. We hope to add information, guidelines, and tooling to do this once this soon. -Why is it called FOOOF? -~~~~~~~~~~~~~~~~~~~~~~~ +What is the 'FOOOF' name? +~~~~~~~~~~~~~~~~~~~~~~~~~ -FOOOF stands for "fitting oscillations & one-over f". +The original name of the module was 'FOOOF', which stood for "fitting oscillations & one-over f". -This was a working title for the project that stuck as the name of the code and the tool. -We have moved away from referring to the components that FOOOF fits in this way, preferring -'periodic' and 'aperiodic' activity, but the name 'FOOOF' stuck around as the name of the tool -itself. +This was a working title for the project that stuck as the name of the code and the tool. Since +we have moved away from using these terms in the module and algorithm, now preferring terms such +as 'periodic' and 'aperiodic' activity, the module has been renamed to the more general name +of 'spectral parameterization'. How do I cite this method? ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -415,7 +412,7 @@ References .. _3 : https://doi.org/10.1016/j.neuroimage.2017.06.078 -- [4_] Buzsaki, Logothetis & Singer (2013). Scaling Brain Size, Keeping Timing: Evolutionary Preservation +- [4_] Buzsáki, Logothetis & Singer (2013). Scaling Brain Size, Keeping Timing: Evolutionary Preservation of Brain Rhythms. DOI: 10.1016/j.neuron.2013.10.002 .. _4 : https://doi.org/10.1016/j.neuron.2013.10.002 diff --git a/doc/glossary.rst b/doc/glossary.rst index f972e831..4db3948e 100644 --- a/doc/glossary.rst +++ b/doc/glossary.rst @@ -5,10 +5,6 @@ A glossary of terms used in the module, with a description of how they are used, .. glossary:: - FOOOF - FOOOF stands for 'fitting oscillations & one-over f'. - We use `FOOOF` to refer to either the current module, or to `FOOOF` objects. - Field Data 'Field data' is used as a catch-all for the relevant types of data that the power spectrum model can be applied to. This includes recordings of electrophysiological or magnetophysiological 'fields', meaning recording modalities such as electroencephalography (EEG), magnetoencephalography (MEG), electrocorticography (ECoG), and local field potential (LFP) data. @@ -19,8 +15,8 @@ A glossary of terms used in the module, with a description of how they are used, A power spectrum is a model that describes neural power spectra. This module implements a particular power spectrum model, whereby we conceptualize and describe mathematically a model that considers power spectra as a combination of periodic and aperiodic components, each of which can be described by a set of model parameters. - FOOOF Objects - This module is object-oriented, whereby the algorithm to parameterize neural power spectra is implemented in Python objects. We refer to those objects as 'FOOOF objects'. + Model Objects + This module is object-oriented, whereby the algorithm to parameterize neural power spectra is implemented in Python objects. We refer to those objects as 'model objects'. Component By 'component' we mean a part, or aspect, of the data. @@ -70,5 +66,5 @@ A glossary of terms used in the module, with a description of how they are used, The KNE is an aperiodic parameter, as part of the aperiodic component of the data. Exponent (EXP) - The exponent of the aperiodic fit, which is :math:`\chi` in the :math:`1/f^\chi` formulation. FOOOF uses and fits exponential functions for the aperiodic fit, whereby :math:`\chi` is equivalent to the slope of a linear fit in log-log space (with a sign flip). + The exponent of the aperiodic fit, which is :math:`\chi` in the :math:`1/f^\chi` formulation. Exponential functions are used for the aperiodic fit, whereby :math:`\chi` is equivalent to the slope of a linear fit in log-log space (with a sign flip). The EXP is an aperiodic parameter, as part of the aperiodic component of the data. diff --git a/doc/make_doc_plots.py b/doc/make_doc_plots.py index 9973f4cf..f7faaf33 100644 --- a/doc/make_doc_plots.py +++ b/doc/make_doc_plots.py @@ -1,15 +1,15 @@ -"""Create the images for the FOOOF documentation.""" +"""Create the images for the documentation site.""" import shutil import numpy as np import matplotlib.pyplot as plt -from fooof import FOOOF, FOOOFGroup -from fooof.sim.gen import gen_power_spectrum -from fooof.plts.utils import check_ax -from fooof.plts.spectra import plot_spectrum -from fooof.utils.download import load_fooof_data +from specparam import SpectralModel, SpectralGroupModel +from specparam.sim.gen import gen_power_spectrum +from specparam.plts.utils import check_ax +from specparam.plts.spectra import plot_spectrum +from specparam.utils.download import load_example_data ################################################################################################### ################################################################################################### @@ -19,28 +19,30 @@ def main(): ## Individual Model Plot # Download examples data files needed for this example - freqs = load_fooof_data('freqs.npy', folder='data') - spectrum = load_fooof_data('spectrum.npy', folder='data') + freqs = load_example_data('freqs.npy', folder='data') + spectrum = load_example_data('spectrum.npy', folder='data') # Initialize and fit an example power spectrum model - fm = FOOOF(peak_width_limits=[1, 6], max_n_peaks=6, min_peak_height=0.2, verbose=False) + fm = SpectralModel(peak_width_limits=[1, 6], max_n_peaks=6, + min_peak_height=0.2, verbose=False) fm.fit(freqs, spectrum, [3, 40]) # Save out the report - fm.save_report('FOOOF_report.png', 'img') + fm.save_report('report.png', 'img') ## Group Plot # Download examples data files needed for this example - freqs = load_fooof_data('group_freqs.npy', folder='data') - spectra = load_fooof_data('group_powers.npy', folder='data') + freqs = load_example_data('group_freqs.npy', folder='data') + spectra = load_example_data('group_powers.npy', folder='data') # Initialize and fit a group of example power spectrum models - fg = FOOOFGroup(peak_width_limits=[1, 6], max_n_peaks=6, min_peak_height=0.2, verbose=False) + fg = SpectralGroupModel(peak_width_limits=[1, 6], max_n_peaks=6, + min_peak_height=0.2, verbose=False) fg.fit(freqs, spectra, [3, 30]) # Save out the report - fg.save_report('FOOOFGroup_report.png', 'img') + fg.save_report('group_report.png', 'img') ## Make the icon plot diff --git a/doc/reference.rst b/doc/reference.rst index 24725d7e..657cec1d 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -54,16 +54,16 @@ In addition, we recommend that reports should include information on: Reporting Template & Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To assist in reporting on using FOOOF, we have created some templates for reporting on spectral parameterization methods. There are also some utilities included in the code to collect the required information. +To assist in reporting on using spectral parameterization, we have created some templates for reporting on spectral parameterization methods. There are also some utilities included in the code to collect the required information. The following box is an example of what a methods report might look like (where all of the *X*'s should be filled in with the relevant information). .. topic:: Methods Report Template - The FOOOF algorithm (version *X.X.X*) was used to parameterize neural power spectra. Settings for the - algorithm were set as: peak width limits : *XX*; max number of peaks : *XX*; minimum peak height : *XX*; - peak threshold : *XX*; and aperiodic mode : *XX*. Power spectra were parameterized across - the frequency range *XX* to *XX* Hz. + Spectral parameterization, using the specparam Python tool (version *X.X.X*) was used to + parameterize neural power spectra. Settings for the algorithm were: peak width limits : *XX*; + max number of peaks : *XX*; minimum peak height : *XX*; peak threshold : *XX*; and aperiodic + mode : *XX*. Power spectra were parameterized across the frequency range *XX* to *XX* Hz. Checking module version ~~~~~~~~~~~~~~~~~~~~~~~ @@ -74,32 +74,32 @@ check the `__version__` from Python, using the following code: .. code-block:: python # Check the version of the tool - from fooof import __version__ as fooof_version - print('Current fooof version:', fooof_version) + from specparam import __version__ as specparam_version + print('Current specparam version:', specparam_version) Generating Methods Reports ~~~~~~~~~~~~~~~~~~~~~~~~~~ -As of FOOOF version 1.0.0 there are code utilities to extract all required information for reporting, and for generating methods reports. +As of version 1.0.0 there are code utilities to extract all required information for reporting, and for generating methods reports. -These utilities require a defined FOOOF object, such as `FOOOF` or `FOOOFGroup`, assumed to be called 'fooof_obj' in the following examples. This object will be used to extract all the relevant settings and any available meta-data for reporting. +These utilities require a defined model object, such as `SpectralModel` or `SpectralGroupModel`, assumed to be called 'model_obj' in the following examples. This object will be used to extract all the relevant settings and any available meta-data for reporting. -The :func:`~fooof.utils.reports.methods_report_info` function can be used to print out the information you need for reporting: +The :func:`~specparam.utils.reports.methods_report_info` function can be used to print out the information you need for reporting: .. code-block:: python # Import the utility to print out information for reporting - from fooof.utils.reports import methods_report_info + from specparam.utils.reports import methods_report_info # Print out all the methods information for reporting - methods_report_info(fooof_obj) + methods_report_info(model_obj) -The :func:`~fooof.utils.reports.methods_report_text` function can be used to print out an auto-generated methods report, like the one demonstrated above, with all available information filled: +The :func:`~specparam.utils.reports.methods_report_text` function can be used to print out an auto-generated methods report, like the one demonstrated above, with all available information filled: .. code-block:: python # Import the utility to print out information for reporting - from fooof.utils.reports import methods_report_text + from specparam.utils.reports import methods_report_text # Generate methods text, with methods information inserted - methods_report_text(fooof_obj) + methods_report_text(model_obj) diff --git a/examples/analyses/plot_dev_demo.py b/examples/analyses/plot_dev_demo.py index 99749e9d..5880549f 100644 --- a/examples/analyses/plot_dev_demo.py +++ b/examples/analyses/plot_dev_demo.py @@ -31,25 +31,25 @@ import matplotlib.pyplot as plt # Import the parameterization model objects -from fooof import FOOOF, FOOOFGroup +from specparam import SpectralModel, SpectralGroupModel # Import useful parameterization related utilities and plot functions -from fooof.bands import Bands -from fooof.analysis import get_band_peak_fg -from fooof.utils import trim_spectrum -from fooof.utils.data import subsample_spectra -from fooof.sim.gen import gen_aperiodic -from fooof.data import FOOOFSettings -from fooof.plts.templates import plot_hist -from fooof.plts.spectra import plot_spectra -from fooof.plts.periodic import plot_peak_fits, plot_peak_params -from fooof.plts.aperiodic import plot_aperiodic_params, plot_aperiodic_fits +from specparam.bands import Bands +from specparam.analysis import get_band_peak_group +from specparam.utils import trim_spectrum +from specparam.utils.data import subsample_spectra +from specparam.sim.gen import gen_aperiodic +from specparam.data import ModelSettings +from specparam.plts.templates import plot_hist +from specparam.plts.spectra import plot_spectra +from specparam.plts.periodic import plot_peak_fits, plot_peak_params +from specparam.plts.aperiodic import plot_aperiodic_params, plot_aperiodic_fits # Import functions to examine frequency-by-frequency error of model fits -from fooof.analysis.error import compute_pointwise_error_fm, compute_pointwise_error_fg +from specparam.analysis.error import compute_pointwise_error, compute_pointwise_error_group # Import helper utility to access data -from fooof.utils.download import fetch_fooof_data +from specparam.utils.download import fetch_example_data ################################################################################################### # Access Example Data @@ -67,8 +67,8 @@ data_path = Path('data') # Collect the example data -fetch_fooof_data('freqs.csv', data_path, data_url) -fetch_fooof_data('indv.csv', data_path, data_url) +fetch_example_data('freqs.csv', data_path, data_url) +fetch_example_data('indv.csv', data_path, data_url) ################################################################################################### # Fitting an Individual Power Spectrum @@ -106,8 +106,8 @@ ################################################################################################### # Initialize a model object for spectral parameterization, with some settings -fm = FOOOF(peak_width_limits=peak_width, max_n_peaks=n_peaks, - min_peak_height=peak_height, verbose=False) +fm = SpectralModel(peak_width_limits=peak_width, max_n_peaks=n_peaks, + min_peak_height=peak_height, verbose=False) # Fit individual PSD over 3-40 Hz range fm.report(freqs, spectrum, PSD_range) @@ -226,7 +226,7 @@ # It can be useful to plot frequency-by-frequency error of the model fit, # to identify where in frequency space the spectrum is (or is not) being fit well. # When fitting individual spectrum, this can be accomplished using the -# `compute_pointwise_error_fm` function. +# `compute_pointwise_error` function. # # In this case, we can see that error fluctuates around 0.05, which is the same as # the mean absolute error for the model (MAE). There are points in the spectrum where @@ -237,18 +237,18 @@ ################################################################################################### # Plot frequency-by-frequency error -compute_pointwise_error_fm(fm, plot_errors=True) +compute_pointwise_error(fm, plot_errors=True) ################################################################################################### # Compute the frequency-by-frequency errors -errs_fm = compute_pointwise_error_fm(fm, plot_errors=False, return_errors=True) +errs_fm = compute_pointwise_error(fm, plot_errors=False, return_errors=True) ################################################################################################### # Note that the average of this error is the same as the global error stored print('Average freq-by-freq error:\t {:1.3f}'.format(np.mean(errs_fm))) -print('FOOOF model fit error: \t\t {:1.3f}'.format(fm.error_)) +print('Total model fit error: \t\t {:1.3f}'.format(fm.error_)) ################################################################################################### # Fitting a Group of Power Spectra @@ -265,8 +265,8 @@ ################################################################################################### # Collect the example data -fetch_fooof_data('freqs.csv', data_path, data_url) -fetch_fooof_data('eop.csv', data_path, data_url) +fetch_example_data('freqs.csv', data_path, data_url) +fetch_example_data('eop.csv', data_path, data_url) ################################################################################################### @@ -288,8 +288,8 @@ ################################################################################################### # Initialize a model object for spectral parameterization, with some settings -fg = FOOOFGroup(peak_width_limits=peak_width, max_n_peaks=n_peaks, - min_peak_height=peak_height, verbose=False) +fg = SpectralGroupModel(peak_width_limits=peak_width, max_n_peaks=n_peaks, + min_peak_height=peak_height, verbose=False) # Fit group PSDs over the 3-40 Hz range fg.fit(freqs, spectra, PSD_range) @@ -311,7 +311,7 @@ # As with the individual model object, the `get_params` method can be # used to access model fit attributes. # -# In addition, here we will use a `Bands` object and the `get_band_peak_fg` +# In addition, here we will use a `Bands` object and the `get_band_peak_group` # function to organize fit peaks into canonical band ranges. # @@ -342,9 +342,9 @@ ################################################################################################### # Extract band-limited peaks information -thetas = get_band_peak_fg(fg, bands.theta) -alphas = get_band_peak_fg(fg, bands.alpha) -betas = get_band_peak_fg(fg, bands.beta) +thetas = get_band_peak_group(fg, bands.theta) +alphas = get_band_peak_group(fg, bands.alpha) +betas = get_band_peak_group(fg, bands.beta) ################################################################################################### # @@ -451,13 +451,13 @@ ################################################################################################### # Fit model object with model 1 settings -fg1 = FOOOFGroup(peak_width_limits=m1_peak_width, max_n_peaks=m1_n_peaks, - min_peak_height=m1_peak_height) +fg1 = SpectralGroupModel(peak_width_limits=m1_peak_width, max_n_peaks=m1_n_peaks, + min_peak_height=m1_peak_height) fg1.fit(m1_freq, m1_spectra) # Create individual reports for model 1 settings (these could be saved and checked) for ind in range(len(fg1)): - temp_model = fg1.get_fooof(ind, regenerate=True) + temp_model = fg1.get_model(ind, regenerate=True) ################################################################################################### # @@ -467,39 +467,39 @@ ################################################################################################### # Fit model object with model 2 settings -fg2 = FOOOFGroup(peak_width_limits=m2_peak_width, max_n_peaks=m2_n_peaks, - min_peak_height=m2_peak_height) +fg2 = SpectralGroupModel(peak_width_limits=m2_peak_width, max_n_peaks=m2_n_peaks, + min_peak_height=m2_peak_height) fg2.fit(m2_freq, m2_spectra) # Create individual reports for model 2 settings (these could be saved and checked) for ind in range(len(fg2)): - temp_model = fg2.get_fooof(ind, regenerate=True) + temp_model = fg2.get_model(ind, regenerate=True) ################################################################################################### # -# There are also other ways to manage settings, for example, using the `FOOOFSettings` object. +# There are also other ways to manage settings, for example, using the `ModelSettings` object. # -# Here we will redefine group model objects (`FOOOFGroup`), +# Here we will redefine group model objects (`SpectralGroupModel`), # again using different settings for each one. # ################################################################################################### # Define settings for model 1 -settings1 = FOOOFSettings(peak_width_limits=m1_peak_width, max_n_peaks=m1_n_peaks, +settings1 = ModelSettings(peak_width_limits=m1_peak_width, max_n_peaks=m1_n_peaks, min_peak_height=m1_peak_height, peak_threshold=2., aperiodic_mode='fixed') # Define settings for model 2 -settings2 = FOOOFSettings(peak_width_limits=m2_peak_width, max_n_peaks=m2_n_peaks, +settings2 = ModelSettings(peak_width_limits=m2_peak_width, max_n_peaks=m2_n_peaks, min_peak_height=m2_peak_height, peak_threshold=2., aperiodic_mode='fixed') ################################################################################################### # Initialize model objects for spectral parameterization, with some settings -fg1 = FOOOFGroup(*settings1) -fg2 = FOOOFGroup(*settings2) +fg1 = SpectralGroupModel(*settings1) +fg2 = SpectralGroupModel(*settings2) ################################################################################################### # @@ -545,7 +545,7 @@ worst_fit_ind = np.argmax(fg.get_params('error')) # Extract this model fit from the group -fm = fg.get_fooof(worst_fit_ind, regenerate=True) +fm = fg.get_model(worst_fit_ind, regenerate=True) ################################################################################################### @@ -565,7 +565,7 @@ underfit_check = [] for ind, res in enumerate(fg): if res.error > underfit_error_threshold: - underfit_check.append(fg.get_fooof(ind, regenerate=True)) + underfit_check.append(fg.get_model(ind, regenerate=True)) ################################################################################################### @@ -585,7 +585,7 @@ overfit_check = [] for ind, res in enumerate(fg): if res.error < overfit_error_threshold: - overfit_check.append(fg.get_fooof(ind, regenerate=True)) + overfit_check.append(fg.get_model(ind, regenerate=True)) ################################################################################################### @@ -619,7 +619,7 @@ # It can be useful to plot frequency-by-frequency error of the model fit, # to identify where in frequency space the spectrum is (or is not) being fit well. # When fitting individual spectrum, this can be accomplished using the -# `compute_pointwise_error_fg` function. When plotting the error, the plot line is +# `compute_pointwise_error_group` function. When plotting the error, the plot line is # the mean error per frequency, across fits, and the shading indicates the standard deviation # of the error, also per frequency. # @@ -634,12 +634,12 @@ ################################################################################################### # Plot frequency-by-frequency error -compute_pointwise_error_fg(fg, plot_errors=True) +compute_pointwise_error_group(fg, plot_errors=True) ################################################################################################### # Return the errors - this returns a 2D matrix of errors for all fits -errs_fg = compute_pointwise_error_fg(fg, plot_errors=False, return_errors=True) +errs_fg = compute_pointwise_error_group(fg, plot_errors=False, return_errors=True) # Check which frequency has the highest error f_max_err = fg.freqs[np.argmax(np.mean(errs_fg, 0))] diff --git a/examples/analyses/plot_mne_example.py b/examples/analyses/plot_mne_example.py index deef1f7a..6b93f7d4 100644 --- a/examples/analyses/plot_mne_example.py +++ b/examples/analyses/plot_mne_example.py @@ -25,11 +25,11 @@ import mne from mne.datasets import sample -# FOOOF imports -from fooof import FOOOFGroup -from fooof.bands import Bands -from fooof.analysis import get_band_peak_fg -from fooof.plts.spectra import plot_spectra +# Spectral parameterization imports +from specparam import SpectralGroupModel +from specparam.bands import Bands +from specparam.analysis import get_band_peak_group +from specparam.plts.spectra import plot_spectra ################################################################################################### # Load & Check MNE Data @@ -123,13 +123,13 @@ def check_nans(data, nan_policy='zero'): # # Now that we have power spectra, we can fit some power spectrum models. # -# Since we have multiple power spectra, we will use the :class:`~fooof.FOOOFGroup` object. +# Since we have multiple power spectra, we will use the :class:`~specparam.SpectralGroupModel` object. # ################################################################################################### -# Initialize a FOOOFGroup object, with desired settings -fg = FOOOFGroup(peak_width_limits=[1, 6], min_peak_height=0.15, +# Initialize a SpectralGroupModel object, with desired settings +fg = SpectralGroupModel(peak_width_limits=[1, 6], min_peak_height=0.15, peak_threshold=2., max_n_peaks=6, verbose=False) # Define the frequency range to fit @@ -152,9 +152,9 @@ def check_nans(data, nan_policy='zero'): # Now that we have our power spectrum models calculated across all channels, # let's start by plotting topographies of some of the resulting model parameters. # -# To do so, we can leverage the fact that both MNE and FOOOF objects preserve data order. +# To do so, we can leverage the fact that both MNE and specparam objects preserve data order. # So, when we calculated power spectra, our output spectra kept the channel order -# that is described in the MNE data object, and so did our :class:`~fooof.FOOOFGroup` +# that is described in the MNE data object, and so did our :class:`~specparam.SpectralGroupModel` # object. # # That means that to plot our topography, we can use the MNE ``plot_topomap`` @@ -182,7 +182,7 @@ def check_nans(data, nan_policy='zero'): ################################################################################################### # Extract alpha peaks -alphas = get_band_peak_fg(fg, bands.alpha) +alphas = get_band_peak_group(fg, bands.alpha) # Extract the power values from the detected peaks alpha_pw = alphas[:, 1] @@ -208,7 +208,7 @@ def check_nans(data, nan_policy='zero'): for ind, (label, band_def) in enumerate(bands): # Get the power values across channels for the current band - band_power = check_nans(get_band_peak_fg(fg, band_def)[:, 1]) + band_power = check_nans(get_band_peak_group(fg, band_def)[:, 1]) # Create a topomap for the current oscillation band mne.viz.plot_topomap(band_power, raw.info, cmap=cm.viridis, contours=0, axes=axes[ind]) @@ -237,10 +237,10 @@ def check_nans(data, nan_policy='zero'): for ind, (label, band_def) in enumerate(bands): # Get the power values across channels for the current band - band_power = check_nans(get_band_peak_fg(fg, band_def)[:, 1]) + band_power = check_nans(get_band_peak_group(fg, band_def)[:, 1]) # Extracted and plot the power spectrum model with the most band power - fg.get_fooof(np.argmax(band_power)).plot(ax=axes[ind], add_legend=False) + fg.get_model(np.argmax(band_power)).plot(ax=axes[ind], add_legend=False) # Set some plot aesthetics & plot title axes[ind].yaxis.set_ticklabels([]) @@ -281,8 +281,8 @@ def check_nans(data, nan_policy='zero'): # Compare the power spectra between low and high exponent channels fig, ax = plt.subplots(figsize=(8, 6)) -spectra = [fg.get_fooof(np.argmin(exps)).power_spectrum, - fg.get_fooof(np.argmax(exps)).power_spectrum] +spectra = [fg.get_model(np.argmin(exps)).power_spectrum, + fg.get_model(np.argmax(exps)).power_spectrum] plot_spectra(fg.freqs, spectra, ax=ax, labels=['Low Exponent', 'High Exponent']) diff --git a/examples/manage/README.txt b/examples/manage/README.txt index c37c2f1e..b15e0300 100644 --- a/examples/manage/README.txt +++ b/examples/manage/README.txt @@ -1,4 +1,4 @@ Managing Objects ---------------- -Examples of how to use, organize, work with, and check FOOOF objects. \ No newline at end of file +Examples of how to use, organize, work with, and check model objects. \ No newline at end of file diff --git a/examples/manage/plot_data_exporting.py b/examples/manage/plot_data_exporting.py index 9a3125a2..e473197c 100644 --- a/examples/manage/plot_data_exporting.py +++ b/examples/manage/plot_data_exporting.py @@ -11,10 +11,10 @@ ################################################################################################### # Import model objects, and Bands object to define bands of interest -from fooof import FOOOF, FOOOFGroup, Bands +from specparam import SpectralModel, SpectralGroupModel, Bands # Import simulation functions to create some example data -from fooof.sim import gen_power_spectrum, gen_group_power_spectra +from specparam.sim import sim_power_spectrum, sim_group_power_spectra ################################################################################################### # Exporting Results @@ -25,8 +25,8 @@ # # Note that the main use case of exporting models to pandas DataFrames is for # analysis across models. If you are just trying to access the model fit results from -# a fit model, you may want the :meth:`~fooof.FOOOF.get_results` and/or -# :meth:`~fooof.FOOOF.get_params` methods. +# a fit model, you may want the :meth:`~specparam.SpectralModel.get_results` and/or +# :meth:`~specparam.SpectralModel.get_params` methods. # # Defining Oscillation Bands # ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -36,7 +36,7 @@ # # This means that we need to define some kind of strategy to organize the peak # parameters across different models. Across these examples, this will include using the -# :class:`~fooof.Bands` object to define oscillations bands of interest. +# :class:`~specparam.Bands` object to define oscillations bands of interest. # ################################################################################################### @@ -45,19 +45,20 @@ bands1 = Bands({'alpha' : [7, 14]}) # Initialize model object -fm = FOOOF() +fm = SpectralModel() ################################################################################################### # Simulate example power spectrum -freqs, powers = gen_power_spectrum([1, 50], [0, 10, 1], [10, 0.25, 2], freq_res=0.25) +freqs, powers = sim_power_spectrum([1, 50], [0, 10, 1], [10, 0.25, 2], freq_res=0.25) # Fit model to power spectrum fm.fit(freqs, powers) ################################################################################################### # -# The :meth:`~fooof.FOOOF.to_df` method supports exporting model fit results to pandas objects. +# The :meth:`~specparam.SpectralModel.to_df` method supports exporting model +# fit results to pandas objects. # ################################################################################################### @@ -116,21 +117,21 @@ # In the above, we used the model object to show the basic exporting functionalities. # # This functionality is more useful when considering multiple model fits together, such -# as can be done using the :meth:`~fooof.FOOOFGroup.to_df` method from the Group object, +# as can be done using the :meth:`~specparam.SpectralGroupModel.to_df` method from the Group object, # which allows for exporting DataFrames of organized model fit parameters across power spectra. # # As with the above, keep in mind that for some cases you may want the -# :meth:`~fooof.FOOOFGroup.get_results` and/or :meth:`~fooof.FOOOFGroup.get_params` methods -# instead of doing a DataFrame export. +# :meth:`~specparam.SpectralGroupModel.get_results` and/or +# :meth:`~specparam.SpectralGroupModel.get_params` methods instead of doing a DataFrame export. # ################################################################################################### # Simulate an example group of power spectra -freqs, powers = gen_group_power_spectra(5, [1, 50], [0, 1], [10, 0.25, 2]) +freqs, powers = sim_group_power_spectra(5, [1, 50], [0, 1], [10, 0.25, 2]) # Initialize a group model object and fit power spectra -fg = FOOOFGroup(verbose=False) +fg = SpectralGroupModel(verbose=False) fg.fit(freqs, powers) ################################################################################################### diff --git a/examples/manage/plot_failed_fits.py b/examples/manage/plot_failed_fits.py index 65f87a82..94277024 100644 --- a/examples/manage/plot_failed_fits.py +++ b/examples/manage/plot_failed_fits.py @@ -7,14 +7,14 @@ ################################################################################################### -# Import the FOOOFGroup object -from fooof import FOOOFGroup +# Import the SpectralGroupModel object +from specparam import SpectralGroupModel # Import simulation code to create test power spectra -from fooof.sim.gen import gen_group_power_spectra +from specparam.sim import sim_group_power_spectra # Import FitError, which we will use to help debug model fit errors -from fooof.core.errors import FitError +from specparam.core.errors import FitError ################################################################################################### # Model Fit Failures @@ -36,13 +36,13 @@ ################################################################################################### # Simulate some example power spectra to use for the example -freqs, powers = gen_group_power_spectra(25, [1, 50], [1, 1], [10, 0.25, 3], +freqs, powers = sim_group_power_spectra(25, [1, 50], [1, 1], [10, 0.25, 3], nlvs=0.1, freq_res=0.25) ################################################################################################### -# Initialize a FOOOFGroup object, with some desired settings -fg = FOOOFGroup(min_peak_height=0.1, max_n_peaks=6) +# Initialize a SpectralGroupModel object, with some desired settings +fg = SpectralGroupModel(min_peak_height=0.1, max_n_peaks=6) ################################################################################################### @@ -54,7 +54,7 @@ # If there are failed fits, these are stored as null models. # # Let's check if there were any null models, from model failures, in the models -# that we have fit so far. To do so, the :class:`~fooof.FOOOFGroup` object has some +# that we have fit so far. To do so, the :class:`~specparam.SpectralGroupModel` object has some # attributes that provide information on any null model fits. # # These attributes are: @@ -118,7 +118,7 @@ # model is failing, you can use the debug mode to get a bit more information about # where the model is failing. # -# The debug mode will stop the FOOOF object catching and continuing any model +# The debug mode will stop the model object catching and continuing any model # fit errors, allowing you to see where the error is happening, and get more # information about where it is failing. # @@ -129,7 +129,7 @@ ################################################################################################### -# Set FOOOFGroup into debug mode +# Set SpectralGroupModel into debug mode fg.set_debug_mode(True) ################################################################################################### @@ -137,8 +137,8 @@ # Refit in debug mode, in which failed fits will raise an error try: fg.fit(freqs, powers) -except FitError as fooof_error: - print(fooof_error) +except FitError as error: + print(error) ################################################################################################### # Debugging Model Fit Errors diff --git a/examples/manage/plot_fit_fooof_3d.py b/examples/manage/plot_fit_models_3d.py similarity index 77% rename from examples/manage/plot_fit_fooof_3d.py rename to examples/manage/plot_fit_models_3d.py index 86a6a308..29c3b616 100644 --- a/examples/manage/plot_fit_fooof_3d.py +++ b/examples/manage/plot_fit_models_3d.py @@ -9,8 +9,8 @@ # Running Across 3D # ----------------- # -# Most of the materials so far have explored using the :class:`~fooof.FOOOF` object to fit -# individual power spectra, and the :class:`~fooof.FOOOFGroup` object for fitting groups of +# Most of the materials so far have explored using the :class:`~specparam.SpectralModel` object to fit +# individual power spectra, and the :class:`~specparam.SpectralGroupModel` object for fitting groups of # power spectra, where a group of spectra is organized as a 2D array of power spectra. # # In this example, we'll go one step further, and step through how to analyze data @@ -38,11 +38,10 @@ # explore how to fit, manage, and organize this data. # # A reminder that no matter how the data is organized, it's always the exact same model -# that is fit, that is the one defined in the FOOOF object. All other objects or organizations -# use this same code to do the fitting. For example, the FOOOFGroup object inherits from the -# FOOOF, and calls the same underlying fit function. +# that is fit. All other objects or organizations use the same code to do the fitting. +# For example, the SpectralGroupModel object inherits from SpectralModel, and calls the same underlying fit function. # -# As we'll see, we can fit 3D arrays of spectra by distributing FOOOFGroup objects +# As we'll see, we can fit 3D arrays of spectra by distributing SpectralGroupModel objects # across the data, which also uses the same underlying code. # @@ -52,16 +51,17 @@ import os import numpy as np -# Import the FOOOFGroup object -from fooof import FOOOFGroup +# Import the SpectralGroupModel object +from specparam import SpectralGroupModel -# Import utilities for working with FOOOF objects -from fooof.objs import fit_fooof_3d, combine_fooofs +# Import utilities for working with model objects +from specparam.objs import fit_models_3d, combine_model_objs # Import simulation & IO utilities to help with the example -from fooof.sim.gen import gen_freqs, gen_group_power_spectra -from fooof.sim.params import param_sampler -from fooof.utils.io import load_fooofgroup +from specparam.sim import sim_group_power_spectra +from specparam.sim.utils import create_freqs +from specparam.sim.params import param_sampler +from specparam.utils.io import load_group ################################################################################################### # Example Set-Up @@ -90,7 +90,7 @@ # Set up the shape of the data n_conditions = 3 n_channels = 10 -n_freqs = len(gen_freqs(freq_range, freq_res)) +n_freqs = len(create_freqs(freq_range, freq_res)) # Define parameters for the simulated power spectra ap_opts = param_sampler([[0, 1.0], [0, 1.5], [0, 2]]) @@ -98,10 +98,10 @@ ################################################################################################### -# Generate some simulated power spectra, and organize into a 3D array +# Simulate power spectra, and organize into a 3D array spectra = [] for ind in range(n_conditions): - freqs, powers = gen_group_power_spectra(n_channels, freq_range, ap_opts, + freqs, powers = sim_group_power_spectra(n_channels, freq_range, ap_opts, pe_opts, freq_res=freq_res) spectra.append(powers) @@ -124,44 +124,41 @@ # efficiently across all power spectra, while keeping our data and results organized # in a way that we keep track of which model results reflect which data. # -# The strategy we will take to do so is by systematically applying FOOOF objects across -# the data. -# -# For working with 3D arrays of power spectra, we have the :func:`~.fit_fooof_3d` +# For working with 3D arrays of power spectra, we have the :func:`~.fit_models_3d` # function which takes in data and a pre-initialized model object, and uses it to fit # power spectrum models across all the data, while maintaining the organization of # the input data. # ################################################################################################### -# fit_fooof_3d -# ~~~~~~~~~~~~ +# fit_models_3d +# ~~~~~~~~~~~~~ # -# More specifically, :func:`~.fit_fooof_3d` takes in: +# More specifically, :func:`~.fit_models_3d` takes in: # -# - a FOOOFGroup object, pre-initialized with the desired settings +# - a SpectralGroupModel object, pre-initialized with the desired settings # - an array of frequency values and a 3D array of power spectra # -# Internally, this function uses the :class:`~fooof.FOOOFGroup` object to +# Internally, this function uses the :class:`~specparam.SpectralGroupModel` object to # fit models across the power spectra. # -# This function then returns a list of :class:`~fooof.FOOOFGroup` objects, which +# This function then returns a list of :class:`~specparam.SpectralGroupModel` objects, which # collectively store all the model fit results. # ################################################################################################### -# Initialize a FOOOFGroup object, with desired settings -fg = FOOOFGroup(peak_width_limits=[1, 6], min_peak_height=0.1) +# Initialize a SpectralGroupModel object, with desired settings +fg = SpectralGroupModel(peak_width_limits=[1, 6], min_peak_height=0.1) ################################################################################################### # Fit the 3D array of power spectra -fgs = fit_fooof_3d(fg, freqs, spectra) +fgs = fit_models_3d(fg, freqs, spectra) ################################################################################################### -# This returns a list of FOOOFGroup objects +# This returns a list of SpectralGroupModel objects print(fgs) ################################################################################################### @@ -169,26 +166,26 @@ # Note that the length of the returned list of objects should be equivalent to # the outermost dimensionality of the input data. # -# In our example setup, this corresponds to `n_conditions` :class:`~fooof.FOOOFGroup` objects. +# In our example setup, this corresponds to `n_conditions` :class:`~specparam.SpectralGroupModel` objects. # ################################################################################################### -print('Number of FOOOFGroups: \t{}'.format(len(fgs))) +print('Number of SpectralGroupModels: \t{}'.format(len(fgs))) print('Number of conditions: \t{}'.format(n_conditions)) ################################################################################################### -# Analyzing FOOOF Objects +# Analyzing Model Objects # ~~~~~~~~~~~~~~~~~~~~~~~ # # Once you have fit the power spectrum models, you want to analyze the results in some way! # -# Since you have a collection of :class:`~fooof.FOOOF` objects, you can analyze these the same -# way as you would look into any other FOOOF objects. You can check out the other examples +# Since you have a collection of :class:`~specparam.SpectralModel` objects, you can analyze these the same +# way as you would look into any other model objects. You can check out the other examples # and tutorials for more information on how to do this. # # A general strategy for analyzing model fit results as they get returned from -# :func:`~.fit_fooof_3d` is to loop across all the objects in the +# :func:`~.fit_models_3d` is to loop across all the objects in the # returned list, and then within the loop you can collect and/or analyze and/or plot # any data of interest. # @@ -204,18 +201,18 @@ ind, np.mean(fg.get_params('aperiodic_params', 'exponent')))) ################################################################################################### -# Managing FOOOF Objects +# Managing Model Objects # ~~~~~~~~~~~~~~~~~~~~~~ # -# When running analyses like this, you may start to have many :class:`~fooof.FOOOF` objects. +# When running analyses like this, you may start to have many :class:`~specparam.SpectralModel` objects. # # For example, you may want to save them out, reload them as needed, and analyze -# results from each :class:`~fooof.FOOOF` or :class:`~fooof.FOOOFGroup` object. +# results from each :class:`~specparam.SpectralModel` or :class:`~specparam.SpectralGroupModel` object. # You may also manipulate the objects by, for example, combining model results # across objects to check overall model fit properties. # # Here, we will continue with a quick example of saving, loading and then combining -# FOOOF objects. Note that a broader exploration of managing different FOOOF objects +# model objects. Note that a broader exploration of managing different model objects # and these object utility functions is available in other examples. # @@ -231,14 +228,14 @@ ################################################################################################### -# Reload our list of FOOOFGroups -fgs = [load_fooofgroup(file_name, file_path='results') \ +# Reload our list of SpectralGroupModels +fgs = [load_group(file_name, file_path='results') \ for file_name in os.listdir('results')] ################################################################################################### -# Combine a list of FOOOF objects into a single FOOOFGroup object -all_fg = combine_fooofs(fgs) +# Combine a list of model objects into a single SpectralGroupModel object +all_fg = combine_model_objs(fgs) # Explore the results from across all model fits all_fg.print_results() diff --git a/examples/manage/plot_manipulating_fooofs.py b/examples/manage/plot_manipulating_models.py similarity index 60% rename from examples/manage/plot_manipulating_fooofs.py rename to examples/manage/plot_manipulating_models.py index eb2285b2..f89c9000 100644 --- a/examples/manage/plot_manipulating_fooofs.py +++ b/examples/manage/plot_manipulating_models.py @@ -1,39 +1,38 @@ """ -Manipulating FOOOF Objects -========================== +Manipulating Objects +==================== Examples with combining, sub-selecting, dropping, and averaging power spectrum models. """ ################################################################################################### # -# As you fit power spectrum models, you may end up with multiple FOOOF objects, as you fit +# As you fit power spectrum models, you may end up with multiple model objects, as you fit # models within and across subjects, conditions, trials, etc. To help manage and organize -# the potentially multiple FOOOF objects that can arise in these cases, here we will -# explore the utilities offered for managing and organizing within and between FOOOF -# objects. +# the potentially multiple objects that can arise in these cases, here we will explore the +# utilities offered for managing and organizing within and between model objects. # # Using simulated data, in this example we will cover: # -# - combining results across FOOOF objects -# - sub-selecting fits from FOOOFGroup objects -# - dropping specified model fits from FOOOFGroup objects -# - average across groups of FOOOF fits +# - combining results across model objects +# - sub-selecting fits from SpectralGroupModel objects +# - dropping specified model fits from SpectralGroupModel objects +# - average across groups of model fits # ################################################################################################### -# Import FOOOF & FOOOFGroup objects -from fooof import FOOOF +# Import model object +from specparam import SpectralModel # Import Bands object, to manage frequency band definitions -from fooof.bands import Bands +from specparam.bands import Bands -# Import utility functions that manage & manipulate FOOOF objects -from fooof.objs.utils import average_fg, combine_fooofs, compare_info +# Import utility functions for working with model objects +from specparam.objs.utils import average_group, combine_model_objs, compare_model_objs # Import simulation functions to create our example data -from fooof.sim.gen import gen_power_spectrum +from specparam.sim import sim_power_spectrum ################################################################################################### # @@ -47,17 +46,17 @@ freq_res = 0.25 # Create some example power spectra -freqs, powers_1 = gen_power_spectrum(freq_range, [0, 1.0], [10, 0.25, 2], +freqs, powers_1 = sim_power_spectrum(freq_range, [0, 1.0], [10, 0.25, 2], nlv=0.00, freq_res=freq_res) -freqs, powers_2 = gen_power_spectrum(freq_range, [0, 1.2], [9, 0.20, 1.5], +freqs, powers_2 = sim_power_spectrum(freq_range, [0, 1.2], [9, 0.20, 1.5], nlv=0.01, freq_res=freq_res) -freqs, powers_3 = gen_power_spectrum(freq_range, [0, 1.5], [11, 0.3, 2.5], +freqs, powers_3 = sim_power_spectrum(freq_range, [0, 1.5], [11, 0.3, 2.5], nlv=0.02, freq_res=freq_res) ################################################################################################### -# Initialize a set of FOOOF objects -fm1, fm2, fm3 = FOOOF(max_n_peaks=4), FOOOF(max_n_peaks=4), FOOOF(max_n_peaks=4) +# Initialize a set of model objects +fm1, fm2, fm3 = SpectralModel(max_n_peaks=4), SpectralModel(max_n_peaks=4), SpectralModel(max_n_peaks=4) # Fit power spectrum models fm1.fit(freqs, powers_1) @@ -65,53 +64,53 @@ fm3.fit(freqs, powers_3) ################################################################################################### -# Combining FOOOF Objects +# Combining Model Objects # ----------------------- # -# Sometimes, when working with models in :class:`~fooof.FOOOF` or :class:`~fooof.FOOOFGroup` +# Sometimes, when working with models in :class:`~specparam.SpectralModel` or :class:`~specparam.SpectralGroupModel` # objects, you may want to combine them together, to check some group properties. # -# The :func:`~.combine_fooofs` function takes a list of FOOOF and/or -# FOOOFGroup objects, and combines all available fits together into a FOOOFGroup object. +# The :func:`~.combine_model_objs` function takes a list of SpectralModel and/or +# SpectralGroupModel objects, and combines all available fits together into a SpectralGroupModel object. # -# Let's now combine our individual model fits into a FOOOFGroup object. +# Let's now combine our individual model fits into a SpectralGroupModel object. # ################################################################################################### -# Combine a list of FOOOF objects into a FOOOFGroup object -fg = combine_fooofs([fm1, fm2, fm3]) +# Combine a list of model objects into a SpectralGroupModel object +fg = combine_model_objs([fm1, fm2, fm3]) # Check the number of models in the object -# Note that the length of a FOOOFGroup object is defined as the number of model fits +# Note that the length of a SpectralGroupModel object is defined as the number of model fits print('Number of model fits: ', len(fg)) ################################################################################################### -# Note on Manipulating FOOOF Objects +# Note on Manipulating Model Objects # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# Note that these functions that manipulate FOOOF objects typically do more than just +# Note that these functions that manipulate model objects typically do more than just # copy results data - they also check and manage settings and meta-data of objects. # -# For example, combining FOOOF objects returns a new FOOOFGroup object with the same settings. +# For example, combining SpectralModel objects returns a new SpectralGroupModel object with the same settings. # -# We can see this by using the :func:`~.compare_info` function to compare -# the settings between FOOOF objects. +# We can see this by using the :func:`~.compare_model_objs` function to compare +# the settings between SpectralModel objects. # -# You can also use this function if you wish to compare FOOOF objects to ensure that +# You can also use this function if you wish to compare SpectralModel objects to ensure that # you are comparing model results that were fit with equivalent settings. # ################################################################################################### -# Compare defined settings across FOOOF objects -compare_info([fm1, fg], 'settings') +# Compare defined settings across model objects +compare_model_objs([fm1, fg], 'settings') ################################################################################################### -# Sub-Select from FOOOFGroup -# -------------------------- +# Sub-Select from SpectralGroupModel +# ---------------------------------- # -# When you have a :class:`~fooof.FOOOFGroup` object, you may also want to sub-select +# When you have a :class:`~specparam.SpectralGroupModel` object, you may also want to sub-select # a group of models. # # Example use cases for this could be: @@ -119,12 +118,12 @@ # - you want to sub-select models that meet some kind of goodness-of-fit criterion # - you want to examine a subset of model reflect, for example, particular channels or trials # -# To do so, we can use the :func:`~fooof.FOOOFGroup.get_group` method of the FOOOFGroup object. +# To do so, we can use the :func:`~specparam.SpectralGroupModel.get_group` method of the SpectralGroupModel object. # This method takes in an input specifying which indices to sub-select, and returns a -# new FOOOFGroup object, containing only the requested model fits. +# new SpectralGroupModel object, containing only the requested model fits. # -# Note that if you want to sub-select a single FOOOF model you can -# use the :meth:`~fooof.FOOOFGroup.get_fooof` method. +# Note that if you want to sub-select a single model you can +# use the :meth:`~specparam.SpectralGroupModel.get_model` method. # ################################################################################################### @@ -133,24 +132,24 @@ # This could be a the indices for a 'region of interest', for example inds = [0, 1] -# Sub-select our selection of models from the FOOOFGroup object +# Sub-select our selection of models from the SpectralGroupModel object nfg = fg.get_group(inds) -# Check how many models our new FOOOFGroup object contains +# Check how many models our new SpectralGroupModel object contains print('Number of model fits: ', len(nfg)) ################################################################################################### # # From here, we could continue to do any analyses of interest on our new -# FOOOFGroup object, which contains only our models of interest. +# SpectralGroupModel object, which contains only our models of interest. # ################################################################################################### -# Dropping Fits from FOOOFGroup -# ----------------------------- +# Dropping Fits from SpectralGroupModel +# ------------------------------------- # -# Another option is to 'drop' model fits from a FOOOFGroup object. You can do this with -# the :meth:`~fooof.FOOOFGroup.drop` method from a :class:`~fooof.FOOOFGroup` object. +# Another option is to 'drop' model fits from a SpectralGroupModel object. You can do this with +# the :meth:`~specparam.SpectralGroupModel.drop` method from a :class:`~specparam.SpectralGroupModel` object. # # This can be used, for example, for a quality control step. If you have checked through # the object, and noticed some outlier model fits, you may want to exclude them from @@ -169,12 +168,12 @@ # Note on Dropped or Failed Fits # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# When models are dropped from :class:`~fooof.FOOOFGroup` objects, they are set as null models. +# When models are dropped from :class:`~specparam.SpectralGroupModel` objects, they are set as null models. # They are therefore cleared of results, but not literally dropped, which -# is done to preserve the ordering of the FOOOFGroup, so that the `n-th` model +# is done to preserve the ordering of the SpectralGroupModel, so that the `n-th` model # doesn't change if some models are dropped. # -# Note that there may in some cases be Null models in a FOOOFGroup without +# Note that there may in some cases be Null models in a SpectralGroupModel without # explicitly dropping them, if any models failed during the fitting process. # @@ -195,11 +194,11 @@ print(fg[ind]) ################################################################################################### -# Note on Selecting From FOOOF Objects -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Note on Selecting from SpectralModel Objects +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# Both the :meth:`~fooof.FOOOFGroup.get_group` and :meth:`~fooof.FOOOFGroup.drop` methods -# take an input of the indices of FOOOF model to select or drop. +# Both the :meth:`~specparam.SpectralGroupModel.get_group` and :meth:`~specparam.SpectralGroupModel.drop` methods +# take an input of the indices of the model(s) to select or drop. # # In both cases, the input can be defined in multiple ways, including directly indicating # the indices as a list of integers, or boolean masks. @@ -209,7 +208,7 @@ # Averaging Across Model Fits # --------------------------- # -# Finally, let's average across the models in our FOOOFGroup object, to examine +# Finally, let's average across the models in our SpectralGroupModel object, to examine # the average model of the data. # # Note that in order to be able to average across individual models, we need to define @@ -224,7 +223,7 @@ bands = Bands({'alpha': [7, 14]}) # Average across individual models fits, specifying bands and an averaging function -afm = average_fg(fg, bands, avg_method='median') +afm = average_group(fg, bands, avg_method='median') # Plot our average model of the data afm.plot() diff --git a/examples/models/plot_aperiodic_params.py b/examples/models/plot_aperiodic_params.py index 96f018e4..7e379a49 100644 --- a/examples/models/plot_aperiodic_params.py +++ b/examples/models/plot_aperiodic_params.py @@ -9,13 +9,13 @@ from scipy.stats import spearmanr -from fooof import FOOOF, FOOOFGroup -from fooof.plts.spectra import plot_spectra -from fooof.plts.annotate import plot_annotated_model -from fooof.plts.aperiodic import plot_aperiodic_params -from fooof.sim.params import Stepper, param_iter -from fooof.sim import gen_power_spectrum, gen_group_power_spectra -from fooof.utils.params import compute_time_constant, compute_knee_frequency +from specparam import SpectralModel, SpectralGroupModel +from specparam.plts.spectra import plot_spectra +from specparam.plts.annotate import plot_annotated_model +from specparam.plts.aperiodic import plot_aperiodic_params +from specparam.sim.params import Stepper, param_iter +from specparam.sim import sim_power_spectrum, sim_group_power_spectra +from specparam.utils.params import compute_time_constant, compute_knee_frequency ################################################################################################### # 'Fixed' Model @@ -28,12 +28,12 @@ ################################################################################################### # Simulate an example power spectrum -freqs, powers = gen_power_spectrum([1, 50], [0, 1], [10, 0.25, 2], freq_res=0.25) +freqs, powers = sim_power_spectrum([1, 50], [0, 1], [10, 0.25, 2], freq_res=0.25) ################################################################################################### # Initialize model object and fit power spectrum -fm = FOOOF(min_peak_height=0.1) +fm = SpectralModel(min_peak_height=0.1) fm.fit(freqs, powers) ################################################################################################### @@ -65,7 +65,7 @@ ################################################################################################### # Simulate a group of power spectra -freqs, powers = gen_group_power_spectra(\ +freqs, powers = sim_group_power_spectra(\ len(exp_steps), [3, 40], ap_params, [10, 0.25, 1], freq_res=0.25, f_rotation=10) ################################################################################################### @@ -76,7 +76,7 @@ ################################################################################################### # Initialize a group mode object and parameterize the power spectra -fg = FOOOFGroup() +fg = SpectralGroupModel() fg.fit(freqs, powers) ################################################################################################### @@ -122,12 +122,12 @@ ################################################################################################### # Generate a power spectrum with a knee -freqs2, powers2 = gen_power_spectrum([1, 50], [0, 15, 1], [8, 0.125, 0.75], freq_res=0.25) +freqs2, powers2 = sim_power_spectrum([1, 50], [0, 15, 1], [8, 0.125, 0.75], freq_res=0.25) ################################################################################################### # Initialize model object and fit power spectrum -fm = FOOOF(min_peak_height=0.05, aperiodic_mode='knee') +fm = SpectralModel(min_peak_height=0.05, aperiodic_mode='knee') fm.fit(freqs2, powers2) ################################################################################################### diff --git a/examples/models/plot_data_components.py b/examples/models/plot_data_components.py index ba0e72c4..96ae8127 100644 --- a/examples/models/plot_data_components.py +++ b/examples/models/plot_data_components.py @@ -10,22 +10,22 @@ # sphinx_gallery_thumbnail_number = 3 -# Import FOOOF model objects -from fooof import FOOOF, FOOOFGroup +# Import model objects +from specparam import SpectralModel, SpectralGroupModel # Import function to plot power spectra -from fooof.plts.spectra import plot_spectra +from specparam.plts.spectra import plot_spectra # Import simulation functions to create some example data -from fooof.sim import gen_power_spectrum, gen_group_power_spectra +from specparam.sim import sim_power_spectrum, sim_group_power_spectra ################################################################################################### # Simulate example power spectrum -freqs, powers = gen_power_spectrum([1, 50], [0, 10, 1], [10, 0.25, 2], freq_res=0.25) +freqs, powers = sim_power_spectrum([1, 50], [0, 10, 1], [10, 0.25, 2], freq_res=0.25) # Initialize model object and fit power spectrum -fm = FOOOF() +fm = SpectralModel() fm.fit(freqs, powers) ################################################################################################### @@ -44,8 +44,8 @@ # Full Data & Model Components # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# Before diving into the isolated data components, let's check 'full' components, including -# the data (`power_spectrum`) and full model fit of a model object (`fooofed_spectrum`). +# Before diving into the isolated data components, let's check the data (`power_spectrum`) +# and full model fit of a model object (`modeled_spectrum`). # ################################################################################################### @@ -56,7 +56,7 @@ ################################################################################################### # Plot the power spectrum model from the object -plot_spectra(fm.freqs, fm.fooofed_spectrum_, color='red') +plot_spectra(fm.freqs, fm.modeled_spectrum_, color='red') ################################################################################################### # Isolated Components @@ -68,8 +68,8 @@ # # To access these components, we can use the following `getter` methods: # -# - :meth:`~fooof.FOOOF.get_data`: allows for accessing data components -# - :meth:`~fooof.FOOOF.get_model`: allows for accessing model components +# - :meth:`~specparam.SpectralModel.get_data`: allows for accessing data components +# - :meth:`~specparam.SpectralModel.get_model`: allows for accessing model components # ################################################################################################### @@ -141,8 +141,7 @@ # # Both the `get_data` and `get_model` methods accept a 'space' argument, whereby the user # can specify whether the return the components in log10 or linear spacing. - - +# ################################################################################################### # Aperiodic Components in Linear Space diff --git a/examples/models/plot_freq_by_freq_error.py b/examples/models/plot_freq_by_freq_error.py index df3cc254..eec76479 100644 --- a/examples/models/plot_freq_by_freq_error.py +++ b/examples/models/plot_freq_by_freq_error.py @@ -12,14 +12,14 @@ # Import numpy for some utility functions import numpy as np -# Import the FOOOF and FOOOFGroup objects -from fooof import FOOOF, FOOOFGroup +# Import model objects +from specparam import SpectralModel, SpectralGroupModel # Import simulation utilities to create some test data -from fooof.sim.gen import gen_power_spectrum, gen_group_power_spectra +from specparam.sim import sim_power_spectrum, sim_group_power_spectra # Import functions to examine frequency-by-frequency error of model fits -from fooof.analysis.error import compute_pointwise_error_fm, compute_pointwise_error_fg +from specparam.analysis.error import compute_pointwise_error, compute_pointwise_error_group ################################################################################################### # Frequency-by-Frequency Error @@ -44,8 +44,8 @@ # First we will start by examining frequency-by-frequency error of an individual model fit, # using simulated data. # -# The function for analyzing error from a FOOOF object is -# :func:`~.compute_pointwise_error_fm`. +# The function for analyzing error from a model object is +# :func:`~.compute_pointwise_error`. # To start with, we will indicate to this function to plot the frequency-by-frequency # error of our model fit. # @@ -53,12 +53,12 @@ ################################################################################################### # Simulate an example power spectrum -freqs, powers = gen_power_spectrum([3, 50], [1, 1], [10, 0.25, 0.5]) +freqs, powers = sim_power_spectrum([3, 50], [1, 1], [10, 0.25, 0.5]) ################################################################################################### -# Initialize a FOOOF object to fit with -fm = FOOOF(verbose=False) +# Initialize a model object +fm = SpectralModel(verbose=False) # Parameterize our power spectrum fm.fit(freqs, powers) @@ -66,7 +66,7 @@ ################################################################################################### # Calculate the error per frequency of the model -compute_pointwise_error_fm(fm, plot_errors=True) +compute_pointwise_error(fm, plot_errors=True) ################################################################################################### # @@ -79,13 +79,13 @@ ################################################################################################### # We can also use this function to return the frequency-by-frequency error -errs_fm = compute_pointwise_error_fm(fm, plot_errors=False, return_errors=True) +errs_fm = compute_pointwise_error(fm, plot_errors=False, return_errors=True) ################################################################################################### # Note that the average of this error is the same as the global error stored print('Average freq-by-freq error:\t {:1.3f}'.format(np.mean(errs_fm))) -print('FOOOF model fit error: \t\t {:1.3f}'.format(fm.error_)) +print('Model fit error: \t\t {:1.3f}'.format(fm.error_)) ################################################################################################### # Checking the Error Across Groups of Model Fits @@ -94,18 +94,18 @@ # Next, lets move on to calculating frequency-by-frequency error across groups of fits, # again using some simulated data. # -# To analyze error from a FOOOFGroup object, use :func:`~.compute_pointwise_error_fg`. +# To analyze error from a SpectralGroupModel object, use :func:`~.compute_pointwise_error_group`. # ################################################################################################### # Simulate a group of power spectra -freqs, powers = gen_group_power_spectra(10, [3, 50], [1, 1], [10, 0.3, 1], nlvs=0.1) +freqs, powers = sim_group_power_spectra(10, [3, 50], [1, 1], [10, 0.3, 1], nlvs=0.1) ################################################################################################### -# Initialize a FOOOFGroup object to fit -fg = FOOOFGroup(min_peak_height=0.25, verbose=False) +# Initialize a SpectralGroupModel object to fit +fg = SpectralGroupModel(min_peak_height=0.25, verbose=False) ################################################################################################### @@ -123,12 +123,12 @@ ################################################################################################### # Plot the group frequency-by-frequency error -compute_pointwise_error_fg(fg, plot_errors=True) +compute_pointwise_error_group(fg, plot_errors=True) ################################################################################################### # Return the errors - this returns a 2D matrix of errors for all fits -errs_fg = compute_pointwise_error_fg(fg, False, True) +errs_fg = compute_pointwise_error_group(fg, False, True) ################################################################################################### @@ -155,7 +155,7 @@ # # As a final example, let's examine a case in which the model is not working well, # and see how the errors look. In particular, we will simulate some new power spectra, -# with a knee parameter, and refit with the same FOOOFGroup object, in 'fixed' aperiodic +# with a knee parameter, and refit with the same SpectralGroupModel object, in 'fixed' aperiodic # mode, and then analyze the frequency-by-frequency errors, as before. In this scenario, # we are fitting data with the wrong model form, and so we expect there to be some issues # with the fit, and we can use the frequency-by-frequency error to investigate if and how @@ -165,7 +165,7 @@ ################################################################################################### # Simulate a group of power spectra, with a knee -freqs, powers = gen_group_power_spectra(10, [1, 50], [0, 10, 2], +freqs, powers = sim_group_power_spectra(10, [1, 50], [0, 10, 2], [10, 0.3, 1], nlvs=0.01) # Parameterize our new group of power spectra @@ -174,7 +174,7 @@ ################################################################################################### # Plot the group frequency-by-frequency error -compute_pointwise_error_fg(fg, plot_errors=True) +compute_pointwise_error_group(fg, plot_errors=True) ################################################################################################### # diff --git a/examples/models/plot_peak_params.py b/examples/models/plot_peak_params.py index 5498e0bb..744a5cff 100644 --- a/examples/models/plot_peak_params.py +++ b/examples/models/plot_peak_params.py @@ -7,14 +7,14 @@ ################################################################################################### -from fooof import FOOOF, FOOOFGroup -from fooof.plts.spectra import plot_spectra -from fooof.plts.periodic import plot_peak_params -from fooof.sim.utils import set_random_seed -from fooof.sim.params import Stepper, param_iter -from fooof.sim import gen_power_spectrum, gen_group_power_spectra -from fooof.plts.annotate import plot_annotated_model -from fooof.utils.params import compute_time_constant, compute_knee_frequency +from specparam import SpectralModel +from specparam.plts.spectra import plot_spectra +from specparam.plts.periodic import plot_peak_params +from specparam.sim.utils import set_random_seed +from specparam.sim.params import Stepper, param_iter +from specparam.sim import sim_power_spectrum, sim_group_power_spectra +from specparam.plts.annotate import plot_annotated_model +from specparam.utils.params import compute_time_constant, compute_knee_frequency ################################################################################################### # Gaussian Peak Model @@ -33,12 +33,12 @@ ################################################################################################### # Simulate an example power spectrum -freqs, powers = gen_power_spectrum([3, 40], [0, 1], [10, 0.3, 1.], freq_res=0.25) +freqs, powers = sim_power_spectrum([3, 40], [0, 1], [10, 0.3, 1.], freq_res=0.25) ################################################################################################### # Initialize model object and fit power spectrum -fm = FOOOF(min_peak_height=0.1) +fm = SpectralModel(min_peak_height=0.1) fm.fit(freqs, powers) ################################################################################################### @@ -74,12 +74,12 @@ ################################################################################################### # Simulate an example power spectrum created with an asymmetric peak -freqs, powers = gen_power_spectrum([3, 40], [0, 1], [[10, 0.3, 1.], [11.25, 0.175, 0.5]], freq_res=0.25) +freqs, powers = sim_power_spectrum([3, 40], [0, 1], [[10, 0.3, 1.], [11.25, 0.175, 0.5]], freq_res=0.25) ################################################################################################### # Initialize model object and fit power spectrum -fm = FOOOF(min_peak_height=0.1) +fm = SpectralModel(min_peak_height=0.1) fm.report(freqs, powers) ################################################################################################### diff --git a/examples/plots/plot_model_components.py b/examples/plots/plot_model_components.py index 4bff7ac6..41627718 100644 --- a/examples/plots/plot_model_components.py +++ b/examples/plots/plot_model_components.py @@ -7,20 +7,20 @@ ################################################################################################### -# Import the FOOOFGroup object -from fooof import FOOOFGroup +# Import the SpectralGroupModel object +from specparam import SpectralGroupModel # Import utilities to manage frequency band definitions -from fooof.bands import Bands -from fooof.analysis import get_band_peak_fg +from specparam.bands import Bands +from specparam.analysis import get_band_peak_group # Import simulation utilities for making example data -from fooof.sim.gen import gen_group_power_spectra -from fooof.sim.params import param_jitter +from specparam.sim import sim_group_power_spectra +from specparam.sim.params import param_jitter # Import plotting function for model parameters and components -from fooof.plts.periodic import plot_peak_fits, plot_peak_params -from fooof.plts.aperiodic import plot_aperiodic_params, plot_aperiodic_fits +from specparam.plts.periodic import plot_peak_fits, plot_peak_params +from specparam.plts.aperiodic import plot_aperiodic_params, plot_aperiodic_fits ################################################################################################### # Experiment Set Up & Simulate Data @@ -60,21 +60,21 @@ ################################################################################################### # Simulate some test data, as two groups of power spectra -freqs, powers1 = gen_group_power_spectra(n_subjs, freq_range, g1_aps, g1_peaks) -freqs, powers2 = gen_group_power_spectra(n_subjs, freq_range, g2_aps, g2_peaks) +freqs, powers1 = sim_group_power_spectra(n_subjs, freq_range, g1_aps, g1_peaks) +freqs, powers2 = sim_group_power_spectra(n_subjs, freq_range, g2_aps, g2_peaks) ################################################################################################### # Fit Power Spectrum Models # ~~~~~~~~~~~~~~~~~~~~~~~~~ # -# Now that we have our simulated data, we can fit our power spectrum models, using FOOOFGroup. +# Now that we have our simulated data, we can fit our power spectrum models, using SpectralGroupModel. # ################################################################################################### -# Initialize a FOOOFGroup object for each group -fg1 = FOOOFGroup(verbose=False) -fg2 = FOOOFGroup(verbose=False) +# Initialize a SpectralGroupModel object for each group +fg1 = SpectralGroupModel(verbose=False) +fg2 = SpectralGroupModel(verbose=False) ################################################################################################### @@ -124,8 +124,8 @@ ################################################################################################### # Extract alpha peaks from each group -g1_alphas = get_band_peak_fg(fg1, bands.alpha) -g2_alphas = get_band_peak_fg(fg2, bands.alpha) +g1_alphas = get_band_peak_group(fg1, bands.alpha) +g2_alphas = get_band_peak_group(fg2, bands.alpha) ################################################################################################### # Plotting Peak Parameters diff --git a/examples/plots/plot_fooof_models.py b/examples/plots/plot_models.py similarity index 80% rename from examples/plots/plot_fooof_models.py rename to examples/plots/plot_models.py index 14a3982b..0b414021 100644 --- a/examples/plots/plot_fooof_models.py +++ b/examples/plots/plot_models.py @@ -2,13 +2,13 @@ Plot Power Spectrum Models ========================== -Plotting power spectrum models, directly from FOOOF objects. +Plotting power spectrum models, directly from specparam objects. In order to the get a qualitative sense of if the model is fitting well, and what the results look like, it can be useful to visualize power spectrum model reconstructions. This example dives deeper into plotting model reconstructions, using the -:meth:`~fooof.FOOOF.plot` method from a :class:`~fooof.FOOOF` object, and explores +:meth:`~specparam.SpectralModel.plot` method from a :class:`~specparam.SpectralModel` object, and explores options for tuning these these visualizations. """ @@ -19,46 +19,46 @@ # Import matplotlib to help manage plotting import matplotlib.pyplot as plt -# Import the FOOOF object -from fooof import FOOOF +# Import the model object +from specparam import SpectralModel # Import simulation functions to create some example data -from fooof.sim.gen import gen_power_spectrum +from specparam.sim import sim_power_spectrum ################################################################################################### -# Generate an example power spectrum -freqs, powers = gen_power_spectrum([3, 50], [1, 1], +# Simulate an example power spectrum +freqs, powers = sim_power_spectrum([3, 50], [1, 1], [[9, 0.25, 0.5], [22, 0.1, 1.5], [25, 0.2, 1.]]) ################################################################################################### -# Plotting From FOOOF Objects +# Plotting from model objects # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# The FOOOF object has a :meth:`~fooof.FOOOF.plot` method that can be used to visualize -# data and models available in the :class:`~fooof.FOOOF` object. +# The model object has a :meth:`~specparam.SpectralModel.plot` method that can be used to visualize +# data and models available in the :class:`~specparam.SpectralModel` object. # ################################################################################################### -# Initialize a FOOOF object, and add some data to it -fm = FOOOF(verbose=False) +# Initialize a model object, and add some data to it +fm = SpectralModel(verbose=False) fm.add_data(freqs, powers) ################################################################################################### # -# Once you have added data to a FOOOF object, you can visualize the data using -# :func:`~fooof.FOOOF.plot`. +# Once you have added data to a model object, you can visualize the data using +# :func:`~specparam.SpectralModel.plot`. # ################################################################################################### -# Plot the data available in the FOOOF object +# Plot the data available in the object fm.plot() ################################################################################################### # -# When the model is available, the :meth:`~fooof.FOOOF.plot` call also displays the +# When the model is available, the :meth:`~specparam.SpectralModel.plot` call also displays the # full model reconstruction, in red. # @@ -72,7 +72,7 @@ # Plotting Aperiodic Components # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# As you can see above, the :meth:`~fooof.FOOOF.plot` call by default also plots the +# As you can see above, the :meth:`~specparam.SpectralModel.plot` call by default also plots the # aperiodic component, in a dashed blue line. # # You can toggle whether to display the aperiodic component with the @@ -139,13 +139,13 @@ ################################################################################################### # Simulate a new power spectrum, over a broader frequency region -freqs, powers = gen_power_spectrum([1, 150], [0, 10, 1.5], +freqs, powers = sim_power_spectrum([1, 150], [0, 10, 1.5], [[4, 0.25, 1], [12, 0.2, 1.5], [25, 0.1, 2]]) ################################################################################################### -# Initialize a new FOOOF object, in 'knee' mode -fm = FOOOF(aperiodic_mode='knee', verbose=False) +# Initialize a new model object, in 'knee' mode +fm = SpectralModel(aperiodic_mode='knee', verbose=False) # Fit the model and visualize the fit, highlighting peaks fm.fit(freqs, powers) @@ -155,7 +155,7 @@ # Other Plotting Options # ~~~~~~~~~~~~~~~~~~~~~~ # -# There are also some other optional inputs to the :meth:`~fooof.FOOOF.plot` call, including: +# There are also some other optional inputs to the :meth:`~specparam.SpectralModel.plot` call, including: # # - `plt_log` : Optional input for plotting the frequency axis in log10 spacing # - `add_legend` : Optional input to toggle whether to add a legend @@ -167,6 +167,6 @@ ################################################################################################### -# Plot from FOOOF, using a custom axes with some optional inputs to tune the plot +# Plot from specparam, using a custom axes with some optional inputs to tune the plot _, ax = plt.subplots(figsize=[10, 10]) fm.plot(plt_log=True, add_legend=False, ax=ax) diff --git a/examples/plots/plot_power_spectra.py b/examples/plots/plot_power_spectra.py index abc8945b..a3d4db8a 100644 --- a/examples/plots/plot_power_spectra.py +++ b/examples/plots/plot_power_spectra.py @@ -11,11 +11,11 @@ import matplotlib.pyplot as plt # Import plotting functions -from fooof.plts.spectra import plot_spectra, plot_spectra_shading +from specparam.plts.spectra import plot_spectra, plot_spectra_shading # Import simulation utilities for creating test data -from fooof.sim.gen import gen_power_spectrum, gen_group_power_spectra -from fooof.sim.params import param_iter, Stepper +from specparam.sim import sim_power_spectrum, sim_group_power_spectra +from specparam.sim.params import param_iter, Stepper ################################################################################################### # Plotting Power Spectra @@ -24,8 +24,8 @@ # The module also includes a plotting sub-module that includes several plotting # options for visualizing power spectra. # -# These plot functions overlap with what is accessible directly through the FOOOF objects, -# as the :meth:`~fooof.FOOOF.plot` method. There are extra functions in the module, and +# These plot functions overlap with what is accessible directly through the model objects, +# as the :meth:`~specparam.SpectralModel.plot` method. There are extra functions in the module, and # extra functionality available in the plotting module. # # Note that the plots in the module are all built using matplotlib. They all allow for @@ -49,8 +49,8 @@ peaks = [[6, 0.2, 1], [10, 0.3, 1], [25, 0.15, 3]] # Simulate two example power spectra -freqs, powers1 = gen_power_spectrum(freq_range, ap_1, peaks) -freqs, powers2 = gen_power_spectrum(freq_range, ap_2, peaks) +freqs, powers1 = sim_power_spectrum(freq_range, ap_1, peaks) +freqs, powers2 = sim_power_spectrum(freq_range, ap_2, peaks) ################################################################################################### # Plotting Individual Power Spectra @@ -86,7 +86,7 @@ ################################################################################################### # Plot multiple spectra on the same plot, in log-log space, specifying some labels -labels = ['PSD-1', 'PSD-2'] +labels = ['SpectralModel-1', 'SpectralModel-2'] plot_spectra(freqs, [powers1, powers2], log_freqs=True, log_powers=True, labels=labels) ################################################################################################### @@ -142,12 +142,12 @@ ap_params = [1, 1] # Simulate a single 10 Hz centered alpha -freqs_al10, powers_al10 = gen_power_spectrum(freq_range, ap_params, +freqs_al10, powers_al10 = sim_power_spectrum(freq_range, ap_params, [10, 0.25, 1], nlv=0) # Simulate spectra stepping across alpha center frequency cf_steps = Stepper(8, 12.5, 0.5) -freqs_al, powers_al = gen_group_power_spectra(len(cf_steps), freq_range, ap_params, +freqs_al, powers_al = sim_group_power_spectra(len(cf_steps), freq_range, ap_params, param_iter([cf_steps, 0.25, 1])) ################################################################################################### diff --git a/examples/processing/plot_line_noise.py b/examples/processing/plot_line_noise.py index 99ab9e8a..20a19b51 100644 --- a/examples/processing/plot_line_noise.py +++ b/examples/processing/plot_line_noise.py @@ -10,12 +10,12 @@ # sphinx_gallery_thumbnail_number = 2 # Import the spectral parameterization object and utilities -from fooof import FOOOF -from fooof.plts import plot_spectra -from fooof.utils import trim_spectrum, interpolate_spectrum +from specparam import SpectralModel +from specparam.plts import plot_spectra +from specparam.utils import trim_spectrum, interpolate_spectrum # Import simulation functions to create some example data -from fooof.sim.gen import gen_power_spectrum +from specparam.sim import sim_power_spectrum # Import NeuroDSP functions for simulating & processing time series from neurodsp.sim import sim_combined @@ -38,18 +38,18 @@ # approach simply gets rid of the peaks, interpolating the data to maintain the 1/f # character of the data, allowing for subsequent fitting. # -# The :func:`~fooof.utils.interpolate_spectrum` function allows for doing simple +# The :func:`~specparam.utils.interpolate_spectrum` function allows for doing simple # interpolation. Given a narrow frequency region, this function interpolates the spectrum, # such that the 'peak' of the line noise is removed. # ################################################################################################### -# Generate an example power spectrum, with line noise -freqs1, powers1 = gen_power_spectrum([3, 75], [1, 1], +# Simulate an example power spectrum, with line noise +freqs1, powers1 = sim_power_spectrum([3, 75], [1, 1], [[10, 0.75, 2], [60, 1, 0.5]]) -# Visualize the generated power spectrum +# Visualize the simulated power spectrum plot_spectra(freqs1, powers1, log_powers=True) ################################################################################################### @@ -81,7 +81,7 @@ ################################################################################################### # Initialize a power spectrum model -fm1 = FOOOF(verbose=False) +fm1 = SpectralModel(verbose=False) fm1.report(freqs_int1, powers_int1) ################################################################################################### @@ -92,13 +92,13 @@ # frequency ranges, there may be multiple peaks that need to be interpolated. # # This can be done by passing in multiple interpolation regions to -# :func:`~fooof.utils.interpolate_spectrum`, which we will do in the next example. +# :func:`~specparam.utils.interpolate_spectrum`, which we will do in the next example. # ################################################################################################### -# Generate an example power spectrum, with line noise & harmonics -freqs2, powers2 = gen_power_spectrum([1, 150], [1, 500, 1.5], +# Simulate an example power spectrum, with line noise & harmonics +freqs2, powers2 = sim_power_spectrum([1, 150], [1, 500, 1.5], [[10, 0.5, 2], [60, 0.75, 0.5], [120, 0.5, 0.5]]) # Interpolate away the line noise region & harmonics @@ -114,7 +114,7 @@ ################################################################################################### # Parameterize the interpolated power spectrum -fm2 = FOOOF(aperiodic_mode='knee', verbose=False) +fm2 = SpectralModel(aperiodic_mode='knee', verbose=False) fm2.report(freqs2, powers_int2) ################################################################################################### @@ -207,7 +207,7 @@ ################################################################################################### # Initialize and fit a power spectrum model -fm = FOOOF() +fm = SpectralModel() fm.report(freqs, powers_post) ################################################################################################### diff --git a/examples/sims/plot_sim_params.py b/examples/sims/plot_sim_params.py index b6ce5fed..1612dd8c 100644 --- a/examples/sims/plot_sim_params.py +++ b/examples/sims/plot_sim_params.py @@ -8,13 +8,13 @@ ################################################################################################### # Import simulation functions for creating spectra -from fooof.sim.gen import gen_power_spectrum, gen_group_power_spectra +from specparam.sim import sim_power_spectrum, sim_group_power_spectra # Import simulation utilities for managing parameters -from fooof.sim.params import param_sampler, param_iter, param_jitter, Stepper +from specparam.sim.params import param_sampler, param_iter, param_jitter, Stepper # Import plotting functions to visualize spectra -from fooof.plts.spectra import plot_spectra +from specparam.plts.spectra import plot_spectra ################################################################################################### # Simulation Parameters @@ -49,7 +49,7 @@ ################################################################################################### # Simulate a group of power spectra -freqs, powers, sim_params = gen_group_power_spectra(n_spectra, freq_range, ap_params, +freqs, powers, sim_params = sim_group_power_spectra(n_spectra, freq_range, ap_params, pe_params, nlv, return_params=True) ################################################################################################### @@ -62,7 +62,7 @@ # You can also use a SimParams object to regenerate a particular power spectrum cur_params = sim_params[0] -freqs, powers = gen_power_spectrum(freq_range, *cur_params) +freqs, powers = sim_power_spectrum(freq_range, *cur_params) ################################################################################################### # Managing Parameters @@ -98,12 +98,12 @@ ################################################################################################### -# Generate some power spectra, using param_sampler -freqs, powers = gen_group_power_spectra(10, freq_range, ap_opts, pe_opts) +# Simualte some power spectra, using param_sampler +freqs, powers = sim_group_power_spectra(10, freq_range, ap_opts, pe_opts) ################################################################################################### -# Plot some of the spectra that were generated +# Plot some of the spectra that were simulated plot_spectra(freqs, powers[0:4, :], log_powers=True) ################################################################################################### @@ -133,12 +133,12 @@ ################################################################################################### -# Generate some power spectra, using param_iter -freqs, powers = gen_group_power_spectra(len(cf_steps), freq_range, ap_params, pe_params) +# Simulate some power spectra, using param_iter +freqs, powers = sim_group_power_spectra(len(cf_steps), freq_range, ap_params, pe_params) ################################################################################################### -# Plot the generated spectra +# Plot the simulated spectra plot_spectra(freqs, powers, log_freqs=True, log_powers=True) ################################################################################################### @@ -161,16 +161,16 @@ ################################################################################################### -# Generate some power spectra, using param_jitter -freqs, powers = gen_group_power_spectra(5, freq_range, ap_params, pe_params) +# Simulate some power spectra, using param_jitter +freqs, powers = sim_group_power_spectra(5, freq_range, ap_params, pe_params) ################################################################################################### -# Plot the generated spectra +# Plot the simulated spectra plot_spectra(freqs, powers, log_freqs=True, log_powers=True) ################################################################################################### # -# We can see that in the generated spectra above, there is some jitter +# We can see that in the simulated spectra above, there is some jitter # to the simulated aperiodic exponent values. # diff --git a/examples/sims/plot_simulated_power_spectra.py b/examples/sims/plot_simulated_power_spectra.py index f955ed8c..aa74c8d8 100644 --- a/examples/sims/plot_simulated_power_spectra.py +++ b/examples/sims/plot_simulated_power_spectra.py @@ -8,10 +8,10 @@ ################################################################################################### # Import functions for creating simulated power spectra -from fooof.sim.gen import gen_power_spectrum, gen_group_power_spectra +from specparam.sim import sim_power_spectrum, sim_group_power_spectra # Import plotting functions -from fooof.plts.spectra import plot_spectra +from specparam.plts.spectra import plot_spectra ################################################################################################### # Creating Simulated Power Spectra @@ -32,7 +32,7 @@ # # - each peak is defined with three parameters, as [center frequency, height, width] # -# The :func:`~.gen_power_spectrum` function takes these parameters as input to +# The :func:`~.sim_power_spectrum` function takes these parameters as input to # create and return a simulated power spectrum. Note that the parameters that define the peaks # are labeled as gaussian parameters, as these parameters define the simulated gaussians # directly, and are not the modified peak parameters that the model outputs. @@ -47,8 +47,8 @@ ################################################################################################### -# Generate a simulated power spectrum -freqs, powers = gen_power_spectrum(freq_range, aperiodic_params, periodic_params) +# Simulate a power spectrum +freqs, powers = sim_power_spectrum(freq_range, aperiodic_params, periodic_params) ################################################################################################### @@ -93,8 +93,8 @@ ################################################################################################### -# Generate the new simulated power spectrum -freqs, powers = gen_power_spectrum(freq_range, aperiodic_params, +# Simulate the new power spectrum +freqs, powers = sim_power_spectrum(freq_range, aperiodic_params, periodic_params, nlv, freq_res) ################################################################################################### @@ -106,16 +106,16 @@ # Simulating a Group of Power Spectra # ----------------------------------- # -# For simulating multiple power spectra, the :func:`~.gen_group_power_spectra` can be used. +# For simulating multiple power spectra, the :func:`~.sim_group_power_spectra` can be used. # # This function takes the same kind of parameter definitions as -# :func:`~.gen_power_spectrum`, and in addition takes a number specifying +# :func:`~.sim_power_spectrum`, and in addition takes a number specifying # how many power spectra to simulate, returning a 2D matrix containing the # desired number of spectra. # -# Parameters that are passed into :func:`~.gen_group_power_spectra` can be: +# Parameters that are passed into :func:`~.sim_group_power_spectra` can be: # -# - a single definition, whereby the same value is used for all generated spectra +# - a single definition, whereby the same value is used for all simulated spectra # - a list of parameters, whereby each successive entry is used for each successive spectrum # - a function or generator that can be called to return parameters for each spectrum # @@ -136,12 +136,12 @@ ################################################################################################### # Simulate a group of power spectra -freqs, powers = gen_group_power_spectra(n_spectra, freq_range, aperiodic_params, +freqs, powers = sim_group_power_spectra(n_spectra, freq_range, aperiodic_params, periodic_params, nlv) ################################################################################################### -# Plot the power spectra that were just generated +# Plot the power spectra that were just simulated plot_spectra(freqs, powers, log_freqs=True, log_powers=True) ################################################################################################### @@ -159,7 +159,7 @@ ################################################################################################### # Simulate a power spectrum, returning the simulation parameter information -freqs, powers, sp = gen_power_spectrum([1, 50], [1, 1], [10, 0.25, 1.5], +freqs, powers, sp = sim_power_spectrum([1, 50], [1, 1], [10, 0.25, 1.5], 0.01, return_params=True) # Check the information stored in the simulation params object @@ -168,7 +168,7 @@ ################################################################################################### # Simulate a group of power spectrum, returning the simulation parameter information -freqs, powers, sps = gen_group_power_spectra(3, [1, 150], [1, 100, 150], +freqs, powers, sps = sim_group_power_spectra(3, [1, 150], [1, 100, 150], [4, 0.2, 2, 22, 0.15, 3], 0.01, return_params=True) diff --git a/examples/sims/plot_transforms.py b/examples/sims/plot_transforms.py index aadaac51..ea077f2e 100644 --- a/examples/sims/plot_transforms.py +++ b/examples/sims/plot_transforms.py @@ -18,24 +18,24 @@ import numpy as np import matplotlib.pyplot as plt -# Import the FOOOF object -from fooof import FOOOF +# Import the model object +from specparam import SpectralModel # Import simulation utilities to create example data -from fooof.sim.gen import gen_power_spectrum +from specparam.sim import sim_power_spectrum # Import functions that can transform power spectra -from fooof.sim.transform import (rotate_spectrum, translate_spectrum, +from specparam.sim.transform import (rotate_spectrum, translate_spectrum, rotate_sim_spectrum, translate_sim_spectrum, compute_rotation_offset, compute_rotation_frequency) # Import plot function to visualize power spectra -from fooof.plts.spectra import plot_spectra +from specparam.plts.spectra import plot_spectra ################################################################################################### -# Generate a simulated power spectrum -freqs, powers, params = gen_power_spectrum([3, 40], [1, 1], [10, 0.5, 1], +# Simulate a power spectrum +freqs, powers, params = sim_power_spectrum([3, 40], [1, 1], [10, 0.5, 1], return_params=True) ################################################################################################### @@ -68,9 +68,9 @@ ################################################################################################### -# Initialize FOOOF objects -fm1 = FOOOF(verbose=False) -fm2 = FOOOF(verbose=False) +# Initialize model objects +fm1 = SpectralModel(verbose=False) +fm2 = SpectralModel(verbose=False) # Fit power spectrum models to the original, and rotated, spectrum fm1.fit(freqs, powers) @@ -171,14 +171,14 @@ ################################################################################################### -# Create a baseline power spectrum -freqs, powers = gen_power_spectrum([3, 50], [0, 1.5], [10, 0.3, 0.5], nlv=0) +# Simulate a baseline power spectrum +freqs, powers = sim_power_spectrum([3, 50], [0, 1.5], [10, 0.3, 0.5], nlv=0) ################################################################################################### # Initialize some power spectrum models for checking our transformations -fm1 = FOOOF(verbose=False) -fm2 = FOOOF(verbose=False) +fm1 = SpectralModel(verbose=False) +fm2 = SpectralModel(verbose=False) ################################################################################################### # Rotate at the Same Rotation Frequencies diff --git a/fooof/__init__.py b/fooof/__init__.py deleted file mode 100644 index 02459e06..00000000 --- a/fooof/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -"""FOOOF - Fitting Oscillations & One-Over F""" - -from .version import __version__ - -# Deprecation of fooof / move to specparam message -# Note: this warning is for fooof v1.1 specifically, and should be removed in specparam 2.0 -from warnings import warn -DEPRECATION_TEXT = ("\nThe `fooof` package is being deprecated and replaced by the " - "`specparam` (spectral parameterization) package." - "\nThis version of `fooof` (1.1) is fully functional, but will not be further updated." - "\nNew projects are recommended to update to using `specparam` (see Changelog for details).") -warn(DEPRECATION_TEXT, DeprecationWarning, stacklevel=2) - -from .bands import Bands -from .objs import FOOOF, FOOOFGroup -from .objs.utils import fit_fooof_3d diff --git a/fooof/analysis/__init__.py b/fooof/analysis/__init__.py deleted file mode 100644 index 947f8cba..00000000 --- a/fooof/analysis/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -"""Analysis sub-module for FOOOF.""" - -from .error import compute_pointwise_error_fm, compute_pointwise_error_fg -from .periodic import get_band_peak_fm, get_band_peak_fg diff --git a/fooof/bands/__init__.py b/fooof/bands/__init__.py deleted file mode 100644 index 2e977649..00000000 --- a/fooof/bands/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Bands sub-module for FOOOF.""" - -from .bands import Bands diff --git a/fooof/core/__init__.py b/fooof/core/__init__.py deleted file mode 100644 index 205c5694..00000000 --- a/fooof/core/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Sub-module for core functions for FOOOF.""" diff --git a/fooof/core/errors.py b/fooof/core/errors.py deleted file mode 100644 index 49e84f5d..00000000 --- a/fooof/core/errors.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Custom error definitions for FOOOF.""" - -class FOOOFError(Exception): - """Base class for errors in the FOOOF module.""" - -class FitError(FOOOFError): - """Error for a failure to fit.""" - -class NoDataError(FOOOFError): - """Error for if data is missing.""" - -class DataError(FOOOFError): - """Error for if there is a problem with the data.""" - -class InconsistentDataError(FOOOFError): - """Error for if the data is inconsistent.""" - -class IncompatibleSettingsError(FOOOFError): - """Error for if settings are incompatible.""" - -class NoModelError(FOOOFError): - """Error for if the model is not fit.""" diff --git a/fooof/data/__init__.py b/fooof/data/__init__.py deleted file mode 100644 index bd6a0d3a..00000000 --- a/fooof/data/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Data sub-module for FOOOF.""" - -from .data import FOOOFSettings, FOOOFRunModes, FOOOFMetaData, FOOOFResults, SimParams diff --git a/fooof/objs/__init__.py b/fooof/objs/__init__.py deleted file mode 100644 index de67e958..00000000 --- a/fooof/objs/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Objects sub-module, for FOOOF objects and functions that operate on FOOOF objects.""" - -from .fit import FOOOF -from .group import FOOOFGroup -from .utils import compare_info, average_fg, average_reconstructions, combine_fooofs, fit_fooof_3d diff --git a/fooof/plts/__init__.py b/fooof/plts/__init__.py deleted file mode 100644 index 981ba12b..00000000 --- a/fooof/plts/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Plots sub-module for FOOOF.""" - -from .spectra import plot_spectrum, plot_spectra diff --git a/fooof/sim/__init__.py b/fooof/sim/__init__.py deleted file mode 100644 index 84e0c45a..00000000 --- a/fooof/sim/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -""""Simulation sub-module for FOOOF.""" - -# Link the Sim Params object into `sim`, so it can be imported from here -from fooof.data import SimParams - -from .gen import gen_freqs, gen_power_spectrum, gen_group_power_spectra diff --git a/fooof/tests/__init__.py b/fooof/tests/__init__.py deleted file mode 100644 index 8f2cd08f..00000000 --- a/fooof/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for FOOOF.""" diff --git a/fooof/tests/analysis/test_error.py b/fooof/tests/analysis/test_error.py deleted file mode 100644 index 2a87e151..00000000 --- a/fooof/tests/analysis/test_error.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Test functions for fooof.analysis.error.""" - -from fooof.analysis.error import * - -################################################################################################### -################################################################################################### - -def test_compute_pointwise_error_fm(tfm): - - errs = compute_pointwise_error_fm(tfm, False, True) - assert np.all(errs) - -def test_compute_pointwise_error_fm_plt(tfm, skip_if_no_mpl): - """Run a separate test to run with plot pass-through.""" - - compute_pointwise_error_fm(tfm, True, False) - -def test_compute_pointwise_error_fg(tfg): - - errs = compute_pointwise_error_fg(tfg, False, True) - assert np.all(errs) - -def test_compute_pointwise_error_fg_plt(tfg, skip_if_no_mpl): - """Run a separate test to run with plot pass-through.""" - - compute_pointwise_error_fg(tfg, True, False) - -def test_compute_pointwise_error(): - - d1 = np.ones(5) * 2 - d2 = np.ones(5) - - errs = compute_pointwise_error(d1, d2) - assert np.array_equal(errs, np.array([1, 1, 1, 1, 1])) diff --git a/fooof/tests/plts/test_fg.py b/fooof/tests/plts/test_fg.py deleted file mode 100644 index bde3a318..00000000 --- a/fooof/tests/plts/test_fg.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Tests for fooof.plts.fg.""" - -from pytest import raises - -from fooof import FOOOFGroup -from fooof.core.errors import NoModelError - -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH - -from fooof.plts.fg import * - -################################################################################################### -################################################################################################### - -@plot_test -def test_plot_fg(tfg, skip_if_no_mpl): - - plot_fg(tfg, save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_fg.png') - - # Test error if no data available to plot - tfg = FOOOFGroup() - with raises(NoModelError): - tfg.plot() - -@plot_test -def test_plot_fg_ap(tfg, skip_if_no_mpl): - - plot_fg_ap(tfg, save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_fg_ap.png') - -@plot_test -def test_plot_fg_gf(tfg, skip_if_no_mpl): - - plot_fg_gf(tfg, save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_fg_gf.png') - -@plot_test -def test_plot_fg_peak_cens(tfg, skip_if_no_mpl): - - plot_fg_peak_cens(tfg, save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_fg_peak_cens.png') diff --git a/fooof/tests/sim/test_gen.py b/fooof/tests/sim/test_gen.py deleted file mode 100644 index 56d163ed..00000000 --- a/fooof/tests/sim/test_gen.py +++ /dev/null @@ -1,172 +0,0 @@ -"""Test functions for fooof.sim.gen""" - -import numpy as np -from numpy import array_equal - -from fooof.tests.tutils import default_group_params - -from fooof.sim.gen import * - -################################################################################################### -################################################################################################### - -def test_gen_freqs(): - - f_range = [3, 40] - f_res = 0.5 - - freqs = gen_freqs(f_range, f_res) - - assert freqs.min() == f_range[0] - assert freqs.max() == f_range[1] - assert np.mean(np.diff(freqs)) == f_res - -def test_gen_power_spectrum(): - - freq_range = [3, 50] - ap_params = [50, 2] - pe_params = [10, 0.5, 2, 20, 0.3, 4] - - xs, ys = gen_power_spectrum(freq_range, ap_params, pe_params) - - assert np.all(xs) - assert np.all(ys) - assert len(xs) == len(ys) - - # Test with a rotation applied returned - f_rotation = 20 - xs, ys = gen_power_spectrum(freq_range, ap_params, pe_params, f_rotation=f_rotation) - - assert np.all(xs) - assert np.all(ys) - assert len(xs) == len(ys) - -def test_gen_power_spectrum_return_params(): - - freq_range = [3, 50] - ap_params = [50, 2] - pe_params = [[10, 0.5, 2], [20, 0.3, 4]] - nlv = 0.01 - - xs, ys, sp = gen_power_spectrum(freq_range, ap_params, pe_params, - nlv, return_params=True) - - # Test returning parameters - assert array_equal(sp.aperiodic_params, ap_params) - assert array_equal(sp.periodic_params, pe_params) - assert sp.nlv == nlv - -def test_gen_group_power_spectra(): - - n_spectra = 3 - - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params()) - - assert np.all(xs) - assert np.all(ys) - assert ys.ndim == 2 - assert ys.shape[0] == n_spectra - - # Test the case in which periodic params are an empty list - xs, ys = gen_group_power_spectra(2, [3, 50], [1, 1], []) - - assert np.all(xs) - assert np.all(ys) - - # Test with a rotation applied returned - f_rotation = 20 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params(), f_rotation=f_rotation) - - assert np.all(xs) - assert np.all(ys) - -def test_gen_group_power_spectra_return_params(): - - n_spectra = 3 - - aps = [1, 1] - pes = [10, 0.5, 1] - nlv = 0.01 - - xs, ys, sim_params = gen_group_power_spectra(n_spectra, [1, 50], aps, pes, nlv, - return_params=True) - - assert n_spectra == ys.shape[0] == len(sim_params) - sp = sim_params[0] - assert array_equal(sp.aperiodic_params, aps) - assert array_equal(sp.periodic_params, [pes]) - assert sp.nlv == nlv - -def test_gen_aperiodic(): - - xs = gen_freqs([3, 50], 0.5) - - ap_nk = [50, 2] - apv_nk = gen_aperiodic(xs, ap_nk, 'fixed') - assert np.all(apv_nk) - - ap_kn = [50, 1, 1] - apv_kn = gen_aperiodic(xs, ap_kn, 'knee') - assert np.all(apv_kn) - - # Check without specifying aperiodic mode - apv_nk_2 = gen_aperiodic(xs, ap_nk) - assert np.array_equal(apv_nk, apv_nk_2) - apv_kn_2 = gen_aperiodic(xs, ap_kn) - assert np.array_equal(apv_kn, apv_kn_2) - -def test_gen_periodic(): - - xs = gen_freqs([3, 50], 0.5) - pe_params = [10, 2, 1] - - pe_vals = gen_periodic(xs, pe_params) - - assert np.all(np.invert(np.isnan(pe_vals))) - assert xs[np.argmax(pe_vals)] == 10 - -def test_gen_noise(): - - xs = gen_freqs([3, 50], 0.5) - - nlv = 0.1 - noise = gen_noise(xs, nlv) - assert np.all(np.invert(np.isnan(noise))) - assert np.isclose(np.std(noise), nlv, 0.25) - - nlv = 0.5 - noise = gen_noise(xs, nlv) - assert np.all(np.invert(np.isnan(noise))) - assert np.isclose(np.std(noise), nlv, 0.25) - -def test_gen_power_values(): - - xs = gen_freqs([3, 50], 0.5) - - ap_params = [50, 2] - pe_params = [10, 2, 1] - nlv = 0.1 - - ys = gen_power_vals(xs, ap_params, pe_params, nlv) - - assert np.all(ys) - -def test_gen_rotated_power_vals(): - - xs = gen_freqs([3, 50], 0.5) - - ap_params = [50, 2] - pe_params = [10, 2, 1] - nlv = 0.1 - f_rotation = 12 - - ys = gen_rotated_power_vals(xs, ap_params, pe_params, nlv, f_rotation) - - assert np.all(ys) - -def test_gen_model(): - - xs = gen_freqs([3, 50], 0.5) - ys = gen_model(xs, np.array([1, 1]), np.array([10, 0.5, 1])) - - assert np.all(ys) diff --git a/fooof/version.py b/fooof/version.py deleted file mode 100644 index 6df8ed02..00000000 --- a/fooof/version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '1.1.0' \ No newline at end of file diff --git a/motivations/concepts/plot_DoYouEvenOscillate.py b/motivations/concepts/plot_DoYouEvenOscillate.py index ce69c1da..ad8b222d 100644 --- a/motivations/concepts/plot_DoYouEvenOscillate.py +++ b/motivations/concepts/plot_DoYouEvenOscillate.py @@ -52,7 +52,7 @@ ################################################################################################### -# Set random seed, for consistency generating simulated data +# Set random seed, for consistency creating simulated data sim.set_random_seed(21) # Simulation Settings @@ -144,14 +144,14 @@ # White Noise # ^^^^^^^^^^^ # -# A 'white noise' signal is one that is generated with uncorrelated samples drawn from +# A 'white noise' signal is one that is created with uncorrelated samples drawn from # a random distribution. Since each element of the signal is sampled randomly, # there is no consistent rhythmic structure in the signal. # ################################################################################################### -# Generate a white noise time series signal +# Simulate a white noise time series signal white_sig = np.random.normal(0, 1, n_points) ################################################################################################### @@ -196,7 +196,7 @@ ################################################################################################### -# Generate a pink noise signal +# Simulate a pink noise signal pink_sig = sim.sim_powerlaw(n_seconds, s_rate, exponent=-1) ################################################################################################### @@ -247,7 +247,7 @@ ################################################################################################### -# Generate an oscillating signal +# Simulate an oscillating signal osc_sig = sim.sim_oscillation(n_seconds, s_rate, freq=10) ################################################################################################### @@ -298,7 +298,7 @@ 'sim_powerlaw' : {'exponent' : -1} } -# Generate a combined signal +# Simulate a combined signal combined_sig = sim.sim_combined(n_seconds, s_rate, components) ################################################################################################### diff --git a/motivations/concepts/plot_IfYouFilterTheyWillCome.py b/motivations/concepts/plot_IfYouFilterTheyWillCome.py index 76a8ca8d..4117ec99 100644 --- a/motivations/concepts/plot_IfYouFilterTheyWillCome.py +++ b/motivations/concepts/plot_IfYouFilterTheyWillCome.py @@ -30,7 +30,7 @@ import matplotlib.pyplot as plt # Import the Bands object, for managing frequency band definitions -from fooof.bands import Bands +from specparam.bands import Bands # Imports from NeuroDSP to simulate & plot time series from neurodsp.sim import sim_powerlaw, set_random_seed @@ -63,7 +63,7 @@ n_seconds = 4 times = create_times(n_seconds, s_rate) -# Set random seed, for consistency generating simulated data +# Set random seed, for consistency creating simulated data set_random_seed(21) ################################################################################################### diff --git a/motivations/measurements/plot_BandByBand.py b/motivations/measurements/plot_BandByBand.py index 7a3ee5ef..8e3a2d69 100644 --- a/motivations/measurements/plot_BandByBand.py +++ b/motivations/measurements/plot_BandByBand.py @@ -15,16 +15,16 @@ import numpy as np import matplotlib.pyplot as plt -# Import the FOOOF object -from fooof import FOOOF +# Import the model object +from specparam import SpectralModel # Import simulation, utility, and plotting tools -from fooof.bands import Bands -from fooof.utils import trim_spectrum -from fooof.analysis import get_band_peak_fm -from fooof.sim.gen import gen_power_spectrum -from fooof.sim.utils import set_random_seed -from fooof.plts.spectra import plot_spectra_shading +from specparam.bands import Bands +from specparam.utils import trim_spectrum +from specparam.analysis import get_band_peak +from specparam.sim import sim_power_spectrum +from specparam.sim.utils import set_random_seed +from specparam.plts.spectra import plot_spectra_shading ################################################################################################### # Overview @@ -115,8 +115,8 @@ def compare_exp(fm1, fm2): def compare_peak_pw(fm1, fm2, band_def): """Compare the power of detected peaks.""" - pw1 = get_band_peak_fm(fm1, band_def)[1] - pw2 = get_band_peak_fm(fm2, band_def)[1] + pw1 = get_band_peak(fm1, band_def)[1] + pw2 = get_band_peak(fm2, band_def)[1] return pw1 - pw2 @@ -153,14 +153,14 @@ def compare_band_pw(fm1, fm2, band_def): pe_g1 = [[2, 0.25, 1], [6, 0.2, 1], [10, 0.5, 1.5], [20, 0.2, 3], [40, 0.25, 3.5]] pe_g2 = [[2, 0.5, 1], [6, 0.3, 1], [10, 0.5, 1.5], [20, 0.15, 3], [40, 0.15, 3.5]] -# Set random seed, for consistency generating simulated data +# Set random seed, for consistency creating simulated data set_random_seed(21) ################################################################################################### # Simulate example power spectra for each group -freqs, g1_spectrum_bands = gen_power_spectrum(f_range, ap_params, pe_g1, nlv) -freqs, g2_spectrum_bands = gen_power_spectrum(f_range, ap_params, pe_g2, nlv) +freqs, g1_spectrum_bands = sim_power_spectrum(f_range, ap_params, pe_g1, nlv) +freqs, g2_spectrum_bands = sim_power_spectrum(f_range, ap_params, pe_g2, nlv) ################################################################################################### @@ -187,9 +187,9 @@ def compare_band_pw(fm1, fm2, band_def): ################################################################################################### -# Initialize FOOOF objects -fm_bands_g1 = FOOOF(verbose=False) -fm_bands_g2 = FOOOF(verbose=False) +# Initialize model objects +fm_bands_g1 = SpectralModel(verbose=False) +fm_bands_g2 = SpectralModel(verbose=False) # Fit power spectrum models fm_bands_g1.fit(freqs, g1_spectrum_bands) @@ -250,8 +250,8 @@ def compare_band_pw(fm1, fm2, band_def): ################################################################################################### # Simulate spectra for each group, with aperiodic differences -freqs, g1_spectrum_pa = gen_power_spectrum(f_range, [1.0, 1.25], [10, 0.5, 1.5], nlv) -freqs, g2_spectrum_pa = gen_power_spectrum(f_range, [0.7, 1.00], [10, 0.5, 1.5], nlv) +freqs, g1_spectrum_pa = sim_power_spectrum(f_range, [1.0, 1.25], [10, 0.5, 1.5], nlv) +freqs, g2_spectrum_pa = sim_power_spectrum(f_range, [0.7, 1.00], [10, 0.5, 1.5], nlv) ################################################################################################### @@ -275,9 +275,9 @@ def compare_band_pw(fm1, fm2, band_def): ################################################################################################### -# Initialize FOOOF objects -fm_pa_g1 = FOOOF(verbose=False) -fm_pa_g2 = FOOOF(verbose=False) +# Initialize model objects +fm_pa_g1 = SpectralModel(verbose=False) +fm_pa_g2 = SpectralModel(verbose=False) # Fit power spectrum models fm_pa_g1.fit(freqs, g1_spectrum_pa) diff --git a/motivations/measurements/plot_BandRatios.py b/motivations/measurements/plot_BandRatios.py index 113e9347..9668889a 100644 --- a/motivations/measurements/plot_BandRatios.py +++ b/motivations/measurements/plot_BandRatios.py @@ -17,8 +17,8 @@ # .. math:: # BR = \frac{avg(low band power)}{avg(high band power)} # -# In this notebook we will explore this measure in the context of conceptualizing -# neural power spectra as a combination of aperiodic and periodic activity. +# In this notebook we will explore this measure in the context of conceptualizing neural power +# spectra as a combination of aperiodic and periodic activity. # ################################################################################################### @@ -29,8 +29,9 @@ # relate to periodic and aperiodic activity. # # We have completed a full project investigating methodological properties of band -# ratio measures, which is available -# `here `_. +# ratio measures, which is available as a +# `published paper `_ and/or on +# `Github `_. # ################################################################################################### @@ -40,11 +41,11 @@ import matplotlib.pyplot as plt # Import simulation, utility, and plotting tools -from fooof.bands import Bands -from fooof.utils import trim_spectrum -from fooof.sim.gen import gen_power_spectrum -from fooof.sim.utils import set_random_seed -from fooof.plts.spectra import plot_spectra_shading +from specparam.bands import Bands +from specparam.utils import trim_spectrum +from specparam.sim import sim_power_spectrum +from specparam.sim.utils import set_random_seed +from specparam.plts.spectra import plot_spectra_shading ################################################################################################### @@ -82,13 +83,13 @@ alpha = [10, 0.5, 0.75] beta = [25, 0.3, 1.5] -# Set random seed, for consistency generating simulated data +# Set random seed, for consistency creating simulated data set_random_seed(21) ################################################################################################### # Simulate a power spectrum -freqs, powers = gen_power_spectrum(f_range, ap, [theta, alpha, beta], nlv, f_res) +freqs, powers = sim_power_spectrum(f_range, ap, [theta, alpha, beta], nlv, f_res) ################################################################################################### # Calculating Band Ratios @@ -162,20 +163,20 @@ def upd(data, index, value): # Simulate and collect power spectra with changes in each periodic parameter spectra = { 'Theta Frequency' : None, - 'Theta Power' : gen_power_spectrum(\ + 'Theta Power' : sim_power_spectrum(\ f_range, ap, [upd(theta, ipw, 0.5041), alpha, beta], nlv, f_res)[1], - 'Theta Bandwidth' : gen_power_spectrum(\ + 'Theta Bandwidth' : sim_power_spectrum(\ f_range, ap, [upd(theta, ibw, 1.61), alpha, beta], nlv, f_res)[1], - 'Alpha Frequency' : gen_power_spectrum(\ + 'Alpha Frequency' : sim_power_spectrum(\ f_range, ap, [theta, upd(alpha, icf, 8.212), beta], nlv, f_res)[1], 'Alpha Power' : None, - 'Alpha Bandwidth' : gen_power_spectrum(\ + 'Alpha Bandwidth' : sim_power_spectrum(\ f_range, ap, [theta, upd(alpha, ibw, 1.8845), beta], nlv, f_res)[1], - 'Beta Frequency' : gen_power_spectrum(\ + 'Beta Frequency' : sim_power_spectrum(\ f_range, ap, [theta, alpha, upd(beta, icf, 19.388)], nlv, f_res)[1], - 'Beta Power' : gen_power_spectrum(\ + 'Beta Power' : sim_power_spectrum(\ f_range, ap, [theta, alpha, upd(beta, ipw, 0.1403)], nlv, f_res)[1], - 'Beta Bandwidth' : gen_power_spectrum(\ + 'Beta Bandwidth' : sim_power_spectrum(\ f_range, ap, [theta, alpha, upd(beta, ibw, 0.609)], nlv, f_res)[1], } @@ -239,11 +240,11 @@ def upd(data, index, value): exp_spectra = { 'Exponent w Peaks' : \ [powers, - gen_power_spectrum(f_range, [0.13, 1.1099], + sim_power_spectrum(f_range, [0.13, 1.1099], [theta, alpha, beta], nlv, f_res)[1]], 'Exponent w/out Peaks' : \ - [gen_power_spectrum(f_range, ap, [], nlv, f_res)[1], - gen_power_spectrum(f_range, [0.13, 1.1417], [], nlv, f_res)[1]]} + [sim_power_spectrum(f_range, ap, [], nlv, f_res)[1], + sim_power_spectrum(f_range, [0.13, 1.1417], [], nlv, f_res)[1]]} ################################################################################################### @@ -287,6 +288,9 @@ def upd(data, index, value): # in neural data. # # For more investigation into band ratios, their methodological issues, applications to real -# data, and a comparison to parameterizing power spectra, see the full project -# `here `_, +# data, and a comparison to parameterizing power spectra, you can read about a full project +# on this topic in the +# `published paper `_ +# and/or explore the code on +# `Github `_. # diff --git a/motivations/measurements/plot_PeriodicAperiodicFeatures.py b/motivations/measurements/plot_PeriodicAperiodicFeatures.py index 81c4deca..9ec520ce 100644 --- a/motivations/measurements/plot_PeriodicAperiodicFeatures.py +++ b/motivations/measurements/plot_PeriodicAperiodicFeatures.py @@ -5,7 +5,7 @@ Demonstrating how changes in periodic & aperiodic parameters can be conflated. This example is a code implementation and quantitatively exact version of Figure 1 from the -`Parameterizing Neural Power Spectra `_ +`Parameterizing Neural Power Spectra `_ paper. """ @@ -46,10 +46,10 @@ import matplotlib.pyplot as plt # Import simulation, utility, and plotting tools -from fooof.bands import Bands -from fooof.utils import trim_spectrum -from fooof.sim.gen import gen_power_spectrum -from fooof.plts.spectra import plot_spectra_shading +from specparam.bands import Bands +from specparam.utils import trim_spectrum +from specparam.sim import sim_power_spectrum +from specparam.plts.spectra import plot_spectra_shading ################################################################################################### @@ -93,15 +93,15 @@ ################################################################################################### # Create baseline power spectrum, to compare to -freqs, powers_base = gen_power_spectrum(f_range, ap_base, pe_base, nlv, f_res) +freqs, powers_base = sim_power_spectrum(f_range, ap_base, pe_base, nlv, f_res) ################################################################################################### # Create comparison power spectra, with differences in different parameters of the data -_, powers_pw = gen_power_spectrum(f_range, ap_base, pw_diff, nlv, f_res) -_, powers_cf = gen_power_spectrum(f_range, ap_base, cf_diff, nlv, f_res) -_, powers_off = gen_power_spectrum(f_range, off_diff, pe_base, nlv, f_res) -_, powers_exp = gen_power_spectrum(f_range, exp_diff, pe_base, nlv, f_res) +_, powers_pw = sim_power_spectrum(f_range, ap_base, pw_diff, nlv, f_res) +_, powers_cf = sim_power_spectrum(f_range, ap_base, cf_diff, nlv, f_res) +_, powers_off = sim_power_spectrum(f_range, off_diff, pe_base, nlv, f_res) +_, powers_exp = sim_power_spectrum(f_range, exp_diff, pe_base, nlv, f_res) ################################################################################################### @@ -224,13 +224,13 @@ def calc_avg_power(freqs, powers, freq_range): ################################################################################################### # Create baseline power spectrum, to compare to -freqs, powers_noa_base = gen_power_spectrum(f_range, ap_base, pe_base_na, nlv, f_res) +freqs, powers_noa_base = sim_power_spectrum(f_range, ap_base, pe_base_na, nlv, f_res) # Collect all powers together, all_powers_na = {'Offset Change' : \ - gen_power_spectrum(f_range, off_diff_na, pe_base_na, nlv, f_res)[1], + sim_power_spectrum(f_range, off_diff_na, pe_base_na, nlv, f_res)[1], 'Exponent Change' : \ - gen_power_spectrum(f_range, exp_diff_na, pe_base_na, nlv, f_res)[1]} + sim_power_spectrum(f_range, exp_diff_na, pe_base_na, nlv, f_res)[1]} ################################################################################################### diff --git a/requirements-docs.txt b/requirements-docs.txt index e4303841..d00f6824 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -5,7 +5,7 @@ sphinx_bootstrap_theme numpydoc sphinx-copybutton -# Optional fooof dependencies that are required for documentation +# Optional dependencies that are required for documentation matplotlib tqdm diff --git a/setup.py b/setup.py index 2c2b6ac8..a2ba840a 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,10 @@ -"""FOOOF setup script.""" +"""Setup script for specparam.""" import os from setuptools import setup, find_packages # Get the current version number from inside the module -with open(os.path.join('fooof', 'version.py')) as version_file: +with open(os.path.join('specparam', 'version.py')) as version_file: exec(version_file.read()) # Load the long description from the README @@ -16,9 +16,9 @@ install_requires = requirements_file.read().splitlines() setup( - name = 'fooof', + name = 'specparam', version = __version__, - description = 'fitting oscillations & one-over f', + description = 'Spectral parameterization.', long_description = long_description, long_description_content_type = 'text/x-rst', python_requires = '>=3.6', diff --git a/specparam/__init__.py b/specparam/__init__.py new file mode 100644 index 00000000..c974450c --- /dev/null +++ b/specparam/__init__.py @@ -0,0 +1,7 @@ +"""Spectral parameterization.""" + +from .version import __version__ + +from .bands import Bands +from .objs import SpectralModel, SpectralGroupModel +from .objs.utils import fit_models_3d diff --git a/specparam/analysis/__init__.py b/specparam/analysis/__init__.py new file mode 100644 index 00000000..e72d40d1 --- /dev/null +++ b/specparam/analysis/__init__.py @@ -0,0 +1,4 @@ +"""Analysis sub-module for model parameters and related metrics.""" + +from .error import compute_pointwise_error, compute_pointwise_error_group +from .periodic import get_band_peak, get_band_peak_group diff --git a/fooof/analysis/error.py b/specparam/analysis/error.py similarity index 70% rename from fooof/analysis/error.py rename to specparam/analysis/error.py index ceb85c2d..92d1b822 100644 --- a/fooof/analysis/error.py +++ b/specparam/analysis/error.py @@ -1,20 +1,20 @@ -"""Functions to analyze and investigate FOOOF results - model fit error.""" +"""Functions to analyze and investigate model fit results, in terms of model fit error.""" import numpy as np -from fooof.sim.gen import gen_model -from fooof.plts.error import plot_spectral_error -from fooof.core.errors import NoModelError, NoDataError +from specparam.sim.gen import gen_model +from specparam.plts.error import plot_spectral_error +from specparam.core.errors import NoModelError, NoDataError ################################################################################################### ################################################################################################### -def compute_pointwise_error_fm(fm, plot_errors=True, return_errors=False, **plt_kwargs): - """Calculate the frequency by frequency error of a model fit from a FOOOF object. +def compute_pointwise_error(model, plot_errors=True, return_errors=False, **plt_kwargs): + """Calculate the frequency by frequency error of a model fit. Parameters ---------- - fm : FOOOF + model : SpectralModel Object containing the data and model. plot_errors : bool, optional, default: True Whether to plot the errors across frequencies. @@ -37,26 +37,26 @@ def compute_pointwise_error_fm(fm, plot_errors=True, return_errors=False, **plt_ If there are no model results available to calculate model error from. """ - if not fm.has_data: + if not model.has_data: raise NoDataError("Data must be available in the object to calculate errors.") - if not fm.has_model: + if not model.has_model: raise NoModelError("No model is available to use, can not proceed.") - errors = compute_pointwise_error(fm.fooofed_spectrum_, fm.power_spectrum) + errors = compute_pointwise_error_arr(model.modeled_spectrum_, model.power_spectrum) if plot_errors: - plot_spectral_error(fm.freqs, errors, **plt_kwargs) + plot_spectral_error(model.freqs, errors, **plt_kwargs) if return_errors: return errors -def compute_pointwise_error_fg(fg, plot_errors=True, return_errors=False, **plt_kwargs): - """Calculate the frequency by frequency error of model fits from a FOOOFGroup object. +def compute_pointwise_error_group(group, plot_errors=True, return_errors=False, **plt_kwargs): + """Calculate the frequency by frequency error of model fits for a group of fits. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object containing the data and models. plot_errors : bool, optional, default: True Whether to plot the errors across frequencies. @@ -79,35 +79,35 @@ def compute_pointwise_error_fg(fg, plot_errors=True, return_errors=False, **plt_ If there are no model results available to calculate model errors from. """ - if not np.any(fg.power_spectra): + if not np.any(group.power_spectra): raise NoDataError("Data must be available in the object to calculate errors.") - if not fg.has_model: + if not group.has_model: raise NoModelError("No model is available to use, can not proceed.") - errors = np.zeros_like(fg.power_spectra) + errors = np.zeros_like(group.power_spectra) - for ind, (res, data) in enumerate(zip(fg, fg.power_spectra)): + for ind, (res, data) in enumerate(zip(group, group.power_spectra)): - model = gen_model(fg.freqs, res.aperiodic_params, res.gaussian_params) + model = gen_model(group.freqs, res.aperiodic_params, res.gaussian_params) errors[ind, :] = np.abs(model - data) mean = np.mean(errors, 0) standard_dev = np.std(errors, 0) if plot_errors: - plot_spectral_error(fg.freqs, mean, standard_dev, **plt_kwargs) + plot_spectral_error(group.freqs, mean, standard_dev, **plt_kwargs) if return_errors: return errors -def compute_pointwise_error(model, data): +def compute_pointwise_error_arr(data_model, data): """Calculate point-wise error between original data and a model fit of that data. Parameters ---------- - model : 1d array - The model. + data_model : 1d array + The model of the data. data : 1d array The original data that is being modeled. @@ -117,4 +117,4 @@ def compute_pointwise_error(model, data): Calculated values of the difference between the data and the model. """ - return np.abs(model - data) + return np.abs(data_model - data) diff --git a/fooof/analysis/periodic.py b/specparam/analysis/periodic.py similarity index 77% rename from fooof/analysis/periodic.py rename to specparam/analysis/periodic.py index eb618d61..fab46d58 100644 --- a/fooof/analysis/periodic.py +++ b/specparam/analysis/periodic.py @@ -1,19 +1,19 @@ -"""Functions to analyze and investigate FOOOF results - periodic components.""" +"""Functions to analyze and investigate model fit results - periodic components.""" import numpy as np -from fooof.core.items import PEAK_INDS +from specparam.core.items import PEAK_INDS ################################################################################################### ################################################################################################### -def get_band_peak_fm(fm, band, select_highest=True, threshold=None, thresh_param='PW', - attribute='peak_params',): - """Extract peaks from a band of interest from a FOOOF object. +def get_band_peak(model, band, select_highest=True, threshold=None, + thresh_param='PW', attribute='peak_params'): + """Extract peaks from a band of interest from a model object. Parameters ---------- - fm : FOOOF + model : SpectralModel Object to extract peak data from. band : tuple of (float, float) Frequency range for the band of interest. @@ -35,25 +35,25 @@ def get_band_peak_fm(fm, band, select_highest=True, threshold=None, thresh_param Examples -------- - Select an alpha peak from an already fit FOOOF object 'fm', selecting the highest power alpha: + Select an alpha peak from a fit model object 'model', selecting the highest power alpha: - >>> alpha = get_band_peak_fm(fm, [7, 14], select_highest=True) # doctest:+SKIP + >>> alpha = get_band_peak(model, [7, 14], select_highest=True) # doctest:+SKIP - Select beta peaks from a FOOOF object 'fm', extracting all peaks in the range: + Select beta peaks from a model object 'model', extracting all peaks in the range: - >>> betas = get_band_peak_fm(fm, [13, 30], select_highest=False) # doctest:+SKIP + >>> betas = get_band_peak(model, [13, 30], select_highest=False) # doctest:+SKIP """ - return get_band_peak(getattr(fm, attribute + '_'), band, - select_highest, threshold, thresh_param) + return get_band_peak_arr(getattr(model, attribute + '_'), band, + select_highest, threshold, thresh_param) -def get_band_peak_fg(fg, band, threshold=None, thresh_param='PW', attribute='peak_params'): - """Extract peaks from a band of interest from a FOOOFGroup object. +def get_band_peak_group(group, band, threshold=None, thresh_param='PW', attribute='peak_params'): + """Extract peaks from a band of interest from a group model object. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to extract peak data from. band : tuple of (float, float) Frequency range for the band of interest. @@ -83,25 +83,25 @@ def get_band_peak_fg(fg, band, threshold=None, thresh_param='PW', attribute='pea you can do something like: >>> peaks = np.empty((0, 3)) - >>> for f_res in fg: # doctest:+SKIP - ... peaks = np.vstack((peaks, get_band_peak(f_res.peak_params, band, select_highest=False))) + >>> for res in group: # doctest:+SKIP + ... peaks = np.vstack((peaks, get_band_peak(res.peak_params, band, select_highest=False))) Examples -------- - Extract alpha peaks from a FOOOFGroup object 'fg' that already has model results: + Extract alpha peaks from a group model object 'group' that already has model results: - >>> alphas = get_band_peak_fg(fg, [7, 14]) # doctest:+SKIP + >>> alphas = get_band_peak_group(group, [7, 14]) # doctest:+SKIP - Extract peaks from a FOOOFGroup object 'fg', selecting those above a power threshold: + Extract peaks from a group model object 'group', selecting those above a power threshold: - >>> betas = get_band_peak_fg(fg, [13, 30], threshold=0.1) # doctest:+SKIP + >>> betas = get_band_peak_group(group, [13, 30], threshold=0.1) # doctest:+SKIP """ - return get_band_peak_group(fg.get_params(attribute), band, len(fg), - threshold, thresh_param) + return get_band_peak_group_arr(group.get_params(attribute), band, len(group), + threshold, thresh_param) -def get_band_peak_group(peak_params, band, n_fits, threshold=None, thresh_param='PW'): +def get_band_peak_group_arr(peak_params, band, n_fits, threshold=None, thresh_param='PW'): """Extract peaks within a given band of interest, from peaks from a group fit. Parameters @@ -112,7 +112,7 @@ def get_band_peak_group(peak_params, band, n_fits, threshold=None, thresh_param= Frequency range for the band of interest. Defined as: (lower_frequency_bound, upper_frequency_bound). n_fits : int - The number of model fits in the FOOOFGroup data. + The number of model fits in the group. threshold : float, optional A minimum threshold value to apply. thresh_param : {'PW', 'BW'} @@ -133,18 +133,17 @@ def get_band_peak_group(peak_params, band, n_fits, threshold=None, thresh_param= - Each row reflects an individual model fit, in order, filled with nan if no peak was present. """ - # Extracts an array per FOOOF fit, and extracts band peaks from it + # Extracts an array per model fit, and extracts band peaks from it band_peaks = np.zeros(shape=[n_fits, 3]) for ind in range(n_fits): - band_peaks[ind, :] = get_band_peak(peak_params[tuple([peak_params[:, -1] == ind])][:, 0:3], - band=band, select_highest=True, - threshold=threshold, - thresh_param=thresh_param) + band_peaks[ind, :] = get_band_peak_arr(\ + peak_params[tuple([peak_params[:, -1] == ind])][:, 0:3], + band=band, select_highest=True, threshold=threshold, thresh_param=thresh_param) return band_peaks -def get_band_peak(peak_params, band, select_highest=True, threshold=None, thresh_param='PW'): +def get_band_peak_arr(peak_params, band, select_highest=True, threshold=None, thresh_param='PW'): """Extract peaks within a given band of interest. Parameters diff --git a/specparam/bands/__init__.py b/specparam/bands/__init__.py new file mode 100644 index 00000000..cae13d82 --- /dev/null +++ b/specparam/bands/__init__.py @@ -0,0 +1,3 @@ +"""Bands sub-module.""" + +from .bands import Bands diff --git a/fooof/bands/bands.py b/specparam/bands/bands.py similarity index 100% rename from fooof/bands/bands.py rename to specparam/bands/bands.py diff --git a/specparam/core/__init__.py b/specparam/core/__init__.py new file mode 100644 index 00000000..addbcf8e --- /dev/null +++ b/specparam/core/__init__.py @@ -0,0 +1 @@ +"""Sub-module for core functions.""" diff --git a/specparam/core/errors.py b/specparam/core/errors.py new file mode 100644 index 00000000..ecc07ce9 --- /dev/null +++ b/specparam/core/errors.py @@ -0,0 +1,22 @@ +"""Custom error definitions.""" + +class SpecParamError(Exception): + """Base class for custom errors.""" + +class FitError(SpecParamError): + """Error for a failure to fit.""" + +class NoDataError(SpecParamError): + """Error for if data is missing.""" + +class DataError(SpecParamError): + """Error for if there is a problem with the data.""" + +class InconsistentDataError(SpecParamError): + """Error for if the data is inconsistent.""" + +class IncompatibleSettingsError(SpecParamError): + """Error for if settings are incompatible.""" + +class NoModelError(SpecParamError): + """Error for if the model is not fit.""" diff --git a/fooof/core/funcs.py b/specparam/core/funcs.py similarity index 96% rename from fooof/core/funcs.py rename to specparam/core/funcs.py index d6f2c02c..eef4d81b 100644 --- a/fooof/core/funcs.py +++ b/specparam/core/funcs.py @@ -2,14 +2,14 @@ NOTES ----- -- FOOOF currently (only) uses the exponential and gaussian functions. -- Linear & Quadratic functions are from previous versions of FOOOF. +- Model fitting currently (only) uses the exponential and gaussian functions. +- Linear & Quadratic functions are from previous versions. - They are left available for easy swapping back in, if desired. """ import numpy as np -from fooof.core.errors import InconsistentDataError +from specparam.core.errors import InconsistentDataError ################################################################################################### ################################################################################################### diff --git a/fooof/core/info.py b/specparam/core/info.py similarity index 84% rename from fooof/core/info.py rename to specparam/core/info.py index 81fbf28e..aff32bb1 100644 --- a/fooof/core/info.py +++ b/specparam/core/info.py @@ -1,19 +1,19 @@ -"""Internal functions to manage info related to FOOOF objects.""" +"""Internal functions to manage info related to model objects.""" ################################################################################################### ################################################################################################### def get_description(): - """Get dictionary specifying FOOOF attributes, and what kind of data they store. + """Get dictionary specifying model object attributes, and what kind of data they store. Returns ------- attributes : dict - Mapping of FOOOF object attributes, and what kind of data they are. + Mapping of model object attributes, and what kind of data they are. Notes ----- - This function organizes public FOOOF object attributes into: + This function organizes public model object attributes into: - results : parameters for and measures of the model - settings : model settings @@ -35,7 +35,7 @@ def get_description(): 'meta_data' : ['freq_range', 'freq_res'], 'arrays' : ['freqs', 'power_spectrum', 'aperiodic_params_', 'peak_params_', 'gaussian_params_'], - 'model_components' : ['fooofed_spectrum_', '_spectrum_flat', + 'model_components' : ['modeled_spectrum_', '_spectrum_flat', '_spectrum_peak_rm', '_ap_fit', '_peak_fit'], 'descriptors' : ['has_data', 'has_model', 'n_peaks_'] } @@ -108,12 +108,12 @@ def get_indices(aperiodic_mode): return indices -def get_info(fooof_obj, aspect): - """Get a selection of information from a FOOOF derived object. +def get_info(model_obj, aspect): + """Get a selection of information from a model objects. Parameters ---------- - fooof_obj : FOOOF or FOOOFGroup + model_obj : SpectralModel or SpectralGroupModel Object to get attributes from. aspect : {'settings', 'meta_data', 'results'} Which set of attributes to compare the objects across. @@ -121,7 +121,7 @@ def get_info(fooof_obj, aspect): Returns ------- dict - The set of specified info from the FOOOF derived object. + The set of specified info from the model object. """ - return {key : getattr(fooof_obj, key) for key in get_description()[aspect]} + return {key : getattr(model_obj, key) for key in get_description()[aspect]} diff --git a/fooof/core/io.py b/specparam/core/io.py similarity index 79% rename from fooof/core/io.py rename to specparam/core/io.py index 7a11793a..d0ca5a7d 100644 --- a/fooof/core/io.py +++ b/specparam/core/io.py @@ -1,12 +1,12 @@ -"""File I/O for FOOOF.""" +"""File I/O.""" import io import os import json from json import JSONDecodeError -from fooof.core.items import OBJ_DESC -from fooof.core.utils import dict_array_to_lst, dict_select_keys, dict_lst_to_array +from specparam.core.items import OBJ_DESC +from specparam.core.utils import dict_array_to_lst, dict_select_keys, dict_lst_to_array ################################################################################################### ################################################################################################### @@ -61,13 +61,13 @@ def fpath(file_path, file_name): return full_path -def save_fm(fm, file_name, file_path=None, append=False, - save_results=False, save_settings=False, save_data=False): - """Save out data, results and/or settings from a FOOOF object into a JSON file. +def save_model(model, file_name, file_path=None, append=False, + save_results=False, save_settings=False, save_data=False): + """Save out data, results and/or settings from a model object into a JSON file. Parameters ---------- - fm : FOOOF + model : SpectralModel Object to save data from. file_name : str or FileObject File to save data to. @@ -77,9 +77,9 @@ def save_fm(fm, file_name, file_path=None, append=False, Whether to append to an existing file, if available. This option is only valid (and only used) if 'file_name' is a str. save_results : bool, optional - Whether to save out FOOOF model fit results. + Whether to save out model fit results. save_settings : bool, optional - Whether to save out FOOOF settings. + Whether to save out settings. save_data : bool, optional Whether to save out input data. @@ -90,7 +90,7 @@ def save_fm(fm, file_name, file_path=None, append=False, """ # Convert object to dictionary & convert all arrays to lists, for JSON serializing - obj_dict = dict_array_to_lst(fm.__dict__) + obj_dict = dict_array_to_lst(model.__dict__) # Set and select which variables to keep. Use a set to drop any potential overlap # Note that results also saves frequency information to be able to recreate freq vector @@ -119,13 +119,13 @@ def save_fm(fm, file_name, file_path=None, append=False, raise ValueError("Save file not understood.") -def save_fg(fg, file_name, file_path=None, append=False, - save_results=False, save_settings=False, save_data=False): - """Save out results and/or settings from FOOOFGroup object. Saves out to a JSON file. +def save_group(group, file_name, file_path=None, append=False, + save_results=False, save_settings=False, save_data=False): + """Save out results and/or settings from group object. Saves out to a JSON file. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to save data from. file_name : str or FileObject File to save data to. @@ -135,9 +135,9 @@ def save_fg(fg, file_name, file_path=None, append=False, Whether to append to an existing file, if available. This option is only valid (and only used) if 'file_name' is a str. save_results : bool, optional - Whether to save out FOOOF model fit results. + Whether to save out model fit results. save_settings : bool, optional - Whether to save out FOOOF settings. + Whether to save out settings. save_data : bool, optional Whether to save out power spectra data. @@ -153,16 +153,16 @@ def save_fg(fg, file_name, file_path=None, append=False, # Save to string specified file, do not append if isinstance(file_name, str) and not append: with open(fpath(file_path, fname(file_name, 'json')), 'w') as f_obj: - _save_fg(fg, f_obj, save_results, save_settings, save_data) + _save_group(group, f_obj, save_results, save_settings, save_data) # Save to string specified file, appending elif isinstance(file_name, str) and append: with open(fpath(file_path, fname(file_name, 'json')), 'a') as f_obj: - _save_fg(fg, f_obj, save_results, save_settings, save_data) + _save_group(group, f_obj, save_results, save_settings, save_data) # Save to file-object specified file elif isinstance(file_name, io.IOBase): - _save_fg(fg, file_name, save_results, save_settings, save_data) + _save_group(group, file_name, save_results, save_settings, save_data) else: raise ValueError("Save file not understood.") @@ -226,30 +226,30 @@ def load_jsonlines(file_name, file_path): break -def _save_fg(fg, f_obj, save_results, save_settings, save_data): - """Helper function for saving FOOOFGroup - saves data given a file object. +def _save_group(group, f_obj, save_results, save_settings, save_data): + """Helper function for saving a group object - saves data given a file object. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to save data from. f_obj : FileObject File object to save data to. save_results : bool - Whether to save out FOOOF model fit results. + Whether to save out model fit results. save_settings : bool - Whether to save out FOOOF settings. + Whether to save out settings. save_data : bool Whether to save out power spectra data. """ # Since there is a single set of object settings, save them out once, at the top if save_settings: - save_fm(fg, file_name=f_obj, file_path=None, append=False, save_settings=True) + save_model(group, file_name=f_obj, file_path=None, append=False, save_settings=True) # For results & data, loop across all data and/or models, and save each out to a new line if save_results or save_data: - for ind in range(len(fg.group_results)): - fm = fg.get_fooof(ind, regenerate=False) - save_fm(fm, file_name=f_obj, file_path=None, append=False, - save_results=save_results, save_data=save_data) + for ind in range(len(group.group_results)): + model = group.get_model(ind, regenerate=False) + save_model(model, file_name=f_obj, file_path=None, append=False, + save_results=save_results, save_data=save_data) diff --git a/fooof/core/items.py b/specparam/core/items.py similarity index 77% rename from fooof/core/items.py rename to specparam/core/items.py index a427543e..ee07757a 100644 --- a/fooof/core/items.py +++ b/specparam/core/items.py @@ -1,6 +1,6 @@ -"""Instantiated objects that can be used as 'helper items' for working with FOOOF.""" +"""Instantiated objects that can be used as 'helper items' for working with model objects.""" -from fooof.core.info import get_description, get_peak_indices +from specparam.core.info import get_description, get_peak_indices ################################################################################################### ################################################################################################### diff --git a/fooof/core/jacobians.py b/specparam/core/jacobians.py similarity index 100% rename from fooof/core/jacobians.py rename to specparam/core/jacobians.py diff --git a/fooof/core/modutils.py b/specparam/core/modutils.py similarity index 98% rename from fooof/core/modutils.py rename to specparam/core/modutils.py index d379a9dc..32044bfd 100644 --- a/fooof/core/modutils.py +++ b/specparam/core/modutils.py @@ -1,4 +1,4 @@ -"""Utility functions & decorators for dealing with FOOOF, as a module.""" +"""Utility functions & decorators for the module.""" from importlib import import_module from functools import wraps @@ -70,7 +70,7 @@ def wrap(func): @wraps(func) def wrapped_func(*args, **kwargs): if not dep: - raise ImportError("Optional FOOOF dependency " + name + \ + raise ImportError("Optional dependency " + name + \ " is required for this functionality.") return func(*args, **kwargs) return wrapped_func diff --git a/fooof/core/reports.py b/specparam/core/reports.py similarity index 77% rename from fooof/core/reports.py rename to specparam/core/reports.py index 0c75c3a2..ec2a9f79 100644 --- a/fooof/core/reports.py +++ b/specparam/core/reports.py @@ -1,9 +1,11 @@ -"""Generate reports from FOOOF objects.""" +"""Generate reports from model objects.""" -from fooof.core.io import fname, fpath -from fooof.core.modutils import safe_import, check_dependency -from fooof.core.strings import gen_settings_str, gen_results_fm_str, gen_results_fg_str -from fooof.plts.fg import plot_fg_ap, plot_fg_gf, plot_fg_peak_cens +from specparam.core.io import fname, fpath +from specparam.core.modutils import safe_import, check_dependency +from specparam.core.strings import (gen_settings_str, gen_model_results_str, + gen_group_results_str) +from specparam.plts.group import (plot_group_aperiodic, plot_group_goodness, + plot_group_peak_frequencies) plt = safe_import('.pyplot', 'matplotlib') gridspec = safe_import('.gridspec', 'matplotlib') @@ -22,12 +24,14 @@ ################################################################################################### @check_dependency(plt, 'matplotlib') -def save_report_fm(fm, file_name, file_path=None, plt_log=False, add_settings=True, **plot_kwargs): +def save_model_report(model, file_name, file_path=None, plt_log=False, + add_settings=True, **plot_kwargs): + """Generate and save out a PDF report for a power spectrum model fit. Parameters ---------- - fm : FOOOF + model : SpectralModel Object with results from fitting a power spectrum. file_name : str Name to give the saved out file. @@ -51,19 +55,19 @@ def save_report_fm(fm, file_name, file_path=None, plt_log=False, add_settings=Tr # First - text results ax0 = plt.subplot(grid[0]) - results_str = gen_results_fm_str(fm) + results_str = gen_model_results_str(model) ax0.text(0.5, 0.7, results_str, REPORT_FONT, ha='center', va='center') ax0.set_frame_on(False) ax0.set(xticks=[], yticks=[]) # Second - data plot ax1 = plt.subplot(grid[1]) - fm.plot(plt_log=plt_log, ax=ax1, **plot_kwargs) + model.plot(plt_log=plt_log, ax=ax1, **plot_kwargs) - # Third - FOOOF settings + # Third - model settings if add_settings: ax2 = plt.subplot(grid[2]) - settings_str = gen_settings_str(fm, False) + settings_str = gen_settings_str(model, False) ax2.text(0.5, 0.1, settings_str, REPORT_FONT, ha='center', va='center') ax2.set_frame_on(False) ax2.set(xticks=[], yticks=[]) @@ -74,12 +78,12 @@ def save_report_fm(fm, file_name, file_path=None, plt_log=False, add_settings=Tr @check_dependency(plt, 'matplotlib') -def save_report_fg(fg, file_name, file_path=None, add_settings=True): +def save_group_report(group, file_name, file_path=None, add_settings=True): """Generate and save out a PDF report for a group of power spectrum models. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object with results from fitting a group of power spectra. file_name : str Name to give the saved out file. @@ -99,7 +103,7 @@ def save_report_fg(fg, file_name, file_path=None, add_settings=True): # First / top: text results ax0 = plt.subplot(grid[0, :]) - results_str = gen_results_fg_str(fg) + results_str = gen_group_results_str(group) ax0.text(0.5, 0.7, results_str, REPORT_FONT, ha='center', va='center') ax0.set_frame_on(False) ax0.set(xticks=[], yticks=[]) @@ -108,20 +112,20 @@ def save_report_fg(fg, file_name, file_path=None, add_settings=True): # Aperiodic parameters plot ax1 = plt.subplot(grid[1, 0]) - plot_fg_ap(fg, ax1, custom_styler=None) + plot_group_aperiodic(group, ax1, custom_styler=None) # Goodness of fit plot ax2 = plt.subplot(grid[1, 1]) - plot_fg_gf(fg, ax2, custom_styler=None) + plot_group_goodness(group, ax2, custom_styler=None) # Peak center frequencies plot ax3 = plt.subplot(grid[2, :]) - plot_fg_peak_cens(fg, ax3, custom_styler=None) + plot_group_peak_frequencies(group, ax3, custom_styler=None) # Third - Model settings if add_settings: ax4 = plt.subplot(grid[3, :]) - settings_str = gen_settings_str(fg, False) + settings_str = gen_settings_str(group, False) ax4.text(0.5, 0.1, settings_str, REPORT_FONT, ha='center', va='center') ax4.set_frame_on(False) ax4.set(xticks=[], yticks=[]) diff --git a/fooof/core/strings.py b/specparam/core/strings.py similarity index 71% rename from fooof/core/strings.py rename to specparam/core/strings.py index a118cca8..6b4a995c 100644 --- a/fooof/core/strings.py +++ b/specparam/core/strings.py @@ -1,9 +1,9 @@ -"""Formatted strings for printing out FOOOF related information.""" +"""Formatted strings for printing out model and fit related information.""" import numpy as np -from fooof.core.errors import NoModelError -from fooof.version import __version__ as FOOOF_VERSION +from specparam.core.errors import NoModelError +from specparam.version import __version__ as MODULE_VERSION ################################################################################################### ################################################################################################### @@ -35,8 +35,8 @@ def gen_width_warning_str(freq_res, bwl): output = '\n'.join([ '', - 'FOOOF WARNING: Lower-bound peak width limit is < or ~= the frequency resolution: ' + \ - '{:1.2f} <= {:1.2f}'.format(bwl, freq_res), + 'WARNING: Lower-bound peak width limit is < or ~= the frequency resolution: ' + \ + '{:1.2f} <= {:1.2f}'.format(bwl, freq_res), '\tLower bounds below frequency-resolution have no effect ' + \ '(effective lower bound is the frequency resolution).', '\tToo low a limit may lead to overfitting noise as small bandwidth peaks.', @@ -48,7 +48,7 @@ def gen_width_warning_str(freq_res, bwl): def gen_version_str(concise=False): - """Generate a string representation of the current version of FOOOF. + """Generate a string representation of the current version of the module. Parameters ---------- @@ -66,11 +66,11 @@ def gen_version_str(concise=False): # Header '=', '', - 'FOOOF - VERSION', + 'SpecParam - VERSION', '', # Version information - '{}'.format(FOOOF_VERSION), + '{}'.format(MODULE_VERSION), # Footer '', @@ -83,12 +83,12 @@ def gen_version_str(concise=False): return output -def gen_settings_str(fooof_obj, description=False, concise=False): - """Generate a string representation of current FOOOF settings. +def gen_settings_str(model_obj, description=False, concise=False): + """Generate a string representation of current fit settings. Parameters ---------- - fooof_obj : FOOOF or FOOOFGroup or FOOOFSettings + model_obj : SpectralModel or SpectralGroupModel or ModelSettings Object to access settings from. description : bool, optional, default: True Whether to also print out a description of the settings. @@ -120,19 +120,19 @@ def gen_settings_str(fooof_obj, description=False, concise=False): # Header '=', '', - 'FOOOF - SETTINGS', + 'SpecParam - SETTINGS', '', # Settings - include descriptions if requested - *[el for el in ['Peak Width Limits : {}'.format(fooof_obj.peak_width_limits), + *[el for el in ['Peak Width Limits : {}'.format(model_obj.peak_width_limits), '{}'.format(desc['peak_width_limits']), - 'Max Number of Peaks : {}'.format(fooof_obj.max_n_peaks), + 'Max Number of Peaks : {}'.format(model_obj.max_n_peaks), '{}'.format(desc['max_n_peaks']), - 'Minimum Peak Height : {}'.format(fooof_obj.min_peak_height), + 'Minimum Peak Height : {}'.format(model_obj.min_peak_height), '{}'.format(desc['min_peak_height']), - 'Peak Threshold: {}'.format(fooof_obj.peak_threshold), + 'Peak Threshold: {}'.format(model_obj.peak_threshold), '{}'.format(desc['peak_threshold']), - 'Aperiodic Mode : {}'.format(fooof_obj.aperiodic_mode), + 'Aperiodic Mode : {}'.format(model_obj.aperiodic_mode), '{}'.format(desc['aperiodic_mode'])] if el != ''], # Footer @@ -145,12 +145,12 @@ def gen_settings_str(fooof_obj, description=False, concise=False): return output -def gen_freq_range_str(fooof_obj, concise=False): +def gen_freq_range_str(model_obj, concise=False): """Generate a string representation of the fit range that was used for the model. Parameters ---------- - fooof_obj : FOOOF or FOOOFGroup + model_obj : SpectralModel or SpectralGroupModel Object to access settings from. concise : bool, optional, default: False Whether to print the report in concise mode. @@ -160,14 +160,14 @@ def gen_freq_range_str(fooof_obj, concise=False): If fit range is not available, will print out 'XX' for missing values. """ - freq_range = fooof_obj.freq_range if fooof_obj.has_data else ('XX', 'XX') + freq_range = model_obj.freq_range if model_obj.has_data else ('XX', 'XX') str_lst = [ # Header '=', '', - 'FOOOF - FIT RANGE', + 'SpecParam - FIT RANGE', '', # Frequency range information information @@ -185,7 +185,7 @@ def gen_freq_range_str(fooof_obj, concise=False): def gen_methods_report_str(concise=False): - """Generate a string representation of instructions for reporting about using FOOOF. + """Generate a string representation of instructions for reporting on using the module. Parameters ---------- @@ -203,11 +203,11 @@ def gen_methods_report_str(concise=False): # Header '=', '', - 'FOOOF - REPORTING', + 'SpecParam - REPORTING', '', # Methods report information - 'To report on using FOOOF, you should report (at minimum):', + 'To report on using spectral parameterization, you should report (at minimum):', '', '- the code version that was used', '- the algorithm settings that were used', @@ -223,19 +223,19 @@ def gen_methods_report_str(concise=False): return output -def gen_methods_text_str(fooof_obj=None): +def gen_methods_text_str(model_obj=None): """Generate a string representation of a template methods report. Parameters ---------- - fooof_obj : FOOOF or FOOOFGroup, optional - A FOOOF object with settings information available. + model_obj : SpectralModel or SpectralGroupModel, optional + A model object with settings information available. If None, the text is returned as a template, without values. """ template = ( - "The FOOOF algorithm (version {}) was used to parameterize " - "neural power spectra. Settings for the algorithm were set as: " + "The periodic & aperiodic spectral parameterization algorithm (version {}) " + "was used to parameterize neural power spectra. Settings for the algorithm were set as: " "peak width limits : {}; " "max number of peaks : {}; " "minimum peak height : {}; " @@ -245,28 +245,28 @@ def gen_methods_text_str(fooof_obj=None): "{} to {} Hz." ) - if fooof_obj: - freq_range = fooof_obj.freq_range if fooof_obj.has_data else ('XX', 'XX') + if model_obj: + freq_range = model_obj.freq_range if model_obj.has_data else ('XX', 'XX') else: freq_range = ('XX', 'XX') - methods_str = template.format(FOOOF_VERSION, - fooof_obj.peak_width_limits if fooof_obj else 'XX', - fooof_obj.max_n_peaks if fooof_obj else 'XX', - fooof_obj.min_peak_height if fooof_obj else 'XX', - fooof_obj.peak_threshold if fooof_obj else 'XX', - fooof_obj.aperiodic_mode if fooof_obj else 'XX', + methods_str = template.format(MODULE_VERSION, + model_obj.peak_width_limits if model_obj else 'XX', + model_obj.max_n_peaks if model_obj else 'XX', + model_obj.min_peak_height if model_obj else 'XX', + model_obj.peak_threshold if model_obj else 'XX', + model_obj.aperiodic_mode if model_obj else 'XX', *freq_range) return methods_str -def gen_results_fm_str(fm, concise=False): +def gen_model_results_str(model, concise=False): """Generate a string representation of model fit results. Parameters ---------- - fm : FOOOF + model : SpectralModel Object to access results from. concise : bool, optional, default: False Whether to print the report in concise mode. @@ -278,7 +278,7 @@ def gen_results_fm_str(fm, concise=False): """ # Returns a null report if no results are available - if np.all(np.isnan(fm.aperiodic_params_)): + if np.all(np.isnan(model.aperiodic_params_)): return _no_model_str(concise) # Create the formatted strings for printing @@ -287,32 +287,33 @@ def gen_results_fm_str(fm, concise=False): # Header '=', '', - ' FOOOF - POWER SPECTRUM MODEL', + 'POWER SPECTRUM MODEL', '', # Frequency range and resolution 'The model was run on the frequency range {} - {} Hz'.format( - int(np.floor(fm.freq_range[0])), int(np.ceil(fm.freq_range[1]))), - 'Frequency Resolution is {:1.2f} Hz'.format(fm.freq_res), + int(np.floor(model.freq_range[0])), int(np.ceil(model.freq_range[1]))), + 'Frequency Resolution is {:1.2f} Hz'.format(model.freq_res), '', # Aperiodic parameters - ('Aperiodic Parameters (offset, ' + ('knee, ' if fm.aperiodic_mode == 'knee' else '') + \ + ('Aperiodic Parameters (offset, ' + \ + ('knee, ' if model.aperiodic_mode == 'knee' else '') + \ 'exponent): '), - ', '.join(['{:2.4f}'] * len(fm.aperiodic_params_)).format(*fm.aperiodic_params_), + ', '.join(['{:2.4f}'] * len(model.aperiodic_params_)).format(*model.aperiodic_params_), '', # Peak parameters '{} peaks were found:'.format( - len(fm.peak_params_)), + len(model.peak_params_)), *['CF: {:6.2f}, PW: {:6.3f}, BW: {:5.2f}'.format(op[0], op[1], op[2]) \ - for op in fm.peak_params_], + for op in model.peak_params_], '', # Goodness if fit 'Goodness of fit metrics:', - 'R^2 of model fit is {:5.4f}'.format(fm.r_squared_), - 'Error of the fit is {:5.4f}'.format(fm.error_), + 'R^2 of model fit is {:5.4f}'.format(model.r_squared_), + 'Error of the fit is {:5.4f}'.format(model.error_), '', # Footer @@ -324,12 +325,12 @@ def gen_results_fm_str(fm, concise=False): return output -def gen_results_fg_str(fg, concise=False): +def gen_group_results_str(group, concise=False): """Generate a string representation of group fit results. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to access results from. concise : bool, optional, default: False Whether to print the report in concise mode. @@ -345,44 +346,44 @@ def gen_results_fg_str(fg, concise=False): If no model fit data is available to report. """ - if not fg.has_model: + if not group.has_model: raise NoModelError("No model fit results are available, can not proceed.") # Extract all the relevant data for printing - n_peaks = len(fg.get_params('peak_params')) - r2s = fg.get_params('r_squared') - errors = fg.get_params('error') - exps = fg.get_params('aperiodic_params', 'exponent') - kns = fg.get_params('aperiodic_params', 'knee') \ - if fg.aperiodic_mode == 'knee' else np.array([0]) + n_peaks = len(group.get_params('peak_params')) + r2s = group.get_params('r_squared') + errors = group.get_params('error') + exps = group.get_params('aperiodic_params', 'exponent') + kns = group.get_params('aperiodic_params', 'knee') \ + if group.aperiodic_mode == 'knee' else np.array([0]) str_lst = [ # Header '=', '', - ' FOOOF - GROUP RESULTS', + 'GROUP RESULTS', '', # Group information - 'Number of power spectra in the Group: {}'.format(len(fg.group_results)), - *[el for el in ['{} power spectra failed to fit'.format(fg.n_null_)] if fg.n_null_], + 'Number of power spectra in the Group: {}'.format(len(group.group_results)), + *[el for el in ['{} power spectra failed to fit'.format(group.n_null_)] if group.n_null_], '', # Frequency range and resolution 'The model was run on the frequency range {} - {} Hz'.format( - int(np.floor(fg.freq_range[0])), int(np.ceil(fg.freq_range[1]))), - 'Frequency Resolution is {:1.2f} Hz'.format(fg.freq_res), + int(np.floor(group.freq_range[0])), int(np.ceil(group.freq_range[1]))), + 'Frequency Resolution is {:1.2f} Hz'.format(group.freq_res), '', # Aperiodic parameters - knee fit status, and quick exponent description 'Power spectra were fit {} a knee.'.format(\ - 'with' if fg.aperiodic_mode == 'knee' else 'without'), + 'with' if group.aperiodic_mode == 'knee' else 'without'), '', 'Aperiodic Fit Values:', *[el for el in [' Knees - Min: {:6.2f}, Max: {:6.2f}, Mean: {:5.2f}' .format(np.nanmin(kns), np.nanmax(kns), np.nanmean(kns)), - ] if fg.aperiodic_mode == 'knee'], + ] if group.aperiodic_mode == 'knee'], 'Exponents - Min: {:6.3f}, Max: {:6.3f}, Mean: {:5.3f}' .format(np.nanmin(exps), np.nanmax(exps), np.nanmean(exps)), '', @@ -428,7 +429,7 @@ def gen_issue_str(concise=False): # Header '=', '', - 'FOOOF - ISSUE REPORTING', + 'SpecParam - ISSUE REPORTING', '', # Reporting bugs @@ -437,19 +438,14 @@ def gen_issue_str(concise=False): '', # Reporting a weird fit - 'If FOOOF gives you any weird / bad fits, please let us know!', - 'To do so, send us a FOOOF report, and a FOOOF data file, ', + 'If model fitting gives you any weird / bad fits, please let us know!', + 'To do so, you can send us a fit report, and an associated data file, ', '', - 'With a FOOOF object (fm), after fitting, run the following commands:', - "fm.create_report('FOOOF_bad_fit_report')", - "fm.save('FOOOF_bad_fit_data', True, True, True)", + 'With a model object (model), after fitting, run the following commands:', + "model.create_report('bad_fit_report')", + "model.save('bad_fit_data', True, True, True)", '', - 'Send the generated files to us.', - 'We will have a look, and provide any feedback we can.', - '', - - # Contact - 'Contact address: voytekresearch@gmail.com', + 'You can attach the generated files to a Github issue.', '', # Footer diff --git a/fooof/core/utils.py b/specparam/core/utils.py similarity index 99% rename from fooof/core/utils.py rename to specparam/core/utils.py index 29dd2103..9a7ab2a8 100644 --- a/fooof/core/utils.py +++ b/specparam/core/utils.py @@ -1,4 +1,4 @@ -"""Internal utility functions for FOOOF.""" +"""Internal utility functions.""" from inspect import isgenerator from itertools import chain, repeat diff --git a/specparam/data/__init__.py b/specparam/data/__init__.py new file mode 100644 index 00000000..1ca543e5 --- /dev/null +++ b/specparam/data/__init__.py @@ -0,0 +1,3 @@ +"""Data sub-module.""" + +from .data import ModelSettings, ModelRunModes, SpectrumMetaData, FitResults, SimParams diff --git a/fooof/data/conversions.py b/specparam/data/conversions.py similarity index 87% rename from fooof/data/conversions.py rename to specparam/data/conversions.py index 66cbb521..6e73a691 100644 --- a/fooof/data/conversions.py +++ b/specparam/data/conversions.py @@ -2,11 +2,11 @@ import numpy as np -from fooof import Bands -from fooof.core.funcs import infer_ap_func -from fooof.core.info import get_ap_indices, get_peak_indices -from fooof.core.modutils import safe_import, check_dependency -from fooof.analysis.periodic import get_band_peak +from specparam import Bands +from specparam.core.funcs import infer_ap_func +from specparam.core.info import get_ap_indices, get_peak_indices +from specparam.core.modutils import safe_import, check_dependency +from specparam.analysis.periodic import get_band_peak_arr pd = safe_import('pandas') @@ -18,7 +18,7 @@ def model_to_dict(fit_results, peak_org): Parameters ---------- - fit_results : FOOOFResults + fit_results : FitResults Results of a model fit. peak_org : int or Bands How to organize peaks. @@ -53,7 +53,7 @@ def model_to_dict(fit_results, peak_org): elif isinstance(peak_org, Bands): for band, f_range in peak_org: - for label, param in zip(get_peak_indices(), get_band_peak(peaks, f_range)): + for label, param in zip(get_peak_indices(), get_band_peak_arr(peaks, f_range)): fr_dict[band + '_' + label.lower()] = param # goodness-of-fit metrics @@ -62,13 +62,14 @@ def model_to_dict(fit_results, peak_org): return fr_dict + @check_dependency(pd, 'pandas') def model_to_dataframe(fit_results, peak_org): """Convert model fit results to a dataframe. Parameters ---------- - fit_results : FOOOFResults + fit_results : FitResults Results of a model fit. peak_org : int or Bands How to organize peaks. @@ -89,8 +90,8 @@ def group_to_dict(fit_results, peak_org): Parameters ---------- - fit_results : list of FOOOFResults - List of FOOOFResults objects. + fit_results : list of FitResults + List of FitResults objects. peak_org : int or Bands How to organize peaks. If int, extracts the first n peaks. @@ -116,8 +117,8 @@ def group_to_dataframe(fit_results, peak_org): Parameters ---------- - fit_results : list of FOOOFResults - List of FOOOFResults objects. + fit_results : list of FitResults + List of FitResults objects. peak_org : int or Bands How to organize peaks. If int, extracts the first n peaks. diff --git a/fooof/data/data.py b/specparam/data/data.py similarity index 89% rename from fooof/data/data.py rename to specparam/data/data.py index 2bee2943..d44d1e5c 100644 --- a/fooof/data/data.py +++ b/specparam/data/data.py @@ -1,6 +1,6 @@ -"""Data objects for FOOOF. +"""Data objects. -Notes on FOOOF data objects: +Notes on data objects: - these data objects are NamedTuples, immutable data types with attribute labels - the namedtuples are wrapped as classes (they are still immutable when doing this) - wrapping in objects helps to be able to render well formed documentation for them. @@ -13,7 +13,7 @@ ################################################################################################### ################################################################################################### -class FOOOFSettings(namedtuple('FOOOFSettings', ['peak_width_limits', 'max_n_peaks', +class ModelSettings(namedtuple('ModelSettings', ['peak_width_limits', 'max_n_peaks', 'min_peak_height', 'peak_threshold', 'aperiodic_mode'])): """User defined settings for the fitting algorithm. @@ -38,7 +38,7 @@ class FOOOFSettings(namedtuple('FOOOFSettings', ['peak_width_limits', 'max_n_pea __slots__ = () -class FOOOFRunModes(namedtuple('FOOOFRunModes', ['debug', 'check_freqs', 'check_data'])): +class ModelRunModes(namedtuple('ModelRunModes', ['debug', 'check_freqs', 'check_data'])): """Checks performed and errors raised by the model. Parameters @@ -57,7 +57,7 @@ class FOOOFRunModes(namedtuple('FOOOFRunModes', ['debug', 'check_freqs', 'check_ __slots__ = () -class FOOOFMetaData(namedtuple('FOOOFMetaData', ['freq_range', 'freq_res'])): +class SpectrumMetaData(namedtuple('SpectrumMetaData', ['freq_range', 'freq_res'])): """Metadata information about a power spectrum. Parameters @@ -74,8 +74,8 @@ class FOOOFMetaData(namedtuple('FOOOFMetaData', ['freq_range', 'freq_res'])): __slots__ = () -class FOOOFResults(namedtuple('FOOOFResults', ['aperiodic_params', 'peak_params', - 'r_squared', 'error', 'gaussian_params'])): +class FitResults(namedtuple('FitResults', ['aperiodic_params', 'peak_params', + 'r_squared', 'error', 'gaussian_params'])): """Model results from parameterizing a power spectrum. Parameters diff --git a/specparam/objs/__init__.py b/specparam/objs/__init__.py new file mode 100644 index 00000000..c57a381d --- /dev/null +++ b/specparam/objs/__init__.py @@ -0,0 +1,6 @@ +"""Objects sub-module, for model objects and functions that operate on model objects.""" + +from .fit import SpectralModel +from .group import SpectralGroupModel +from .utils import (compare_model_objs, average_group, average_reconstructions, + combine_model_objs, fit_models_3d) diff --git a/fooof/objs/fit.py b/specparam/objs/fit.py similarity index 91% rename from fooof/objs/fit.py rename to specparam/objs/fit.py index 503eddf0..ee488f51 100644 --- a/fooof/objs/fit.py +++ b/specparam/objs/fit.py @@ -1,8 +1,8 @@ -"""FOOOF Object - base object which defines the model. +"""Base model object, which defines the power spectrum model. Private Attributes ================== -Private attributes of the FOOOF object are documented here. +Private attributes of the model object are documented here. Data Attributes --------------- @@ -63,34 +63,33 @@ from numpy.linalg import LinAlgError from scipy.optimize import curve_fit -from fooof.core.utils import unlog -from fooof.core.items import OBJ_DESC -from fooof.core.info import get_indices -from fooof.core.io import save_fm, load_json -from fooof.core.reports import save_report_fm -from fooof.core.modutils import copy_doc_func_to_method -from fooof.core.utils import group_three, check_array_dim -from fooof.core.funcs import gaussian_function, get_ap_func, infer_ap_func -from fooof.core.jacobians import jacobian_gauss -from fooof.core.errors import (FitError, NoModelError, DataError, - NoDataError, InconsistentDataError) -from fooof.core.strings import (gen_settings_str, gen_results_fm_str, - gen_issue_str, gen_width_warning_str) - -from fooof.plts.fm import plot_fm -from fooof.utils.data import trim_spectrum -from fooof.utils.params import compute_gauss_std -from fooof.data import FOOOFSettings, FOOOFRunModes, FOOOFMetaData, FOOOFResults -from fooof.data.conversions import model_to_dataframe -from fooof.sim.gen import gen_freqs, gen_aperiodic, gen_periodic, gen_model +from specparam.core.utils import unlog +from specparam.core.items import OBJ_DESC +from specparam.core.info import get_indices +from specparam.core.io import save_model, load_json +from specparam.core.reports import save_model_report +from specparam.core.modutils import copy_doc_func_to_method +from specparam.core.utils import group_three, check_array_dim +from specparam.core.funcs import gaussian_function, get_ap_func, infer_ap_func +from specparam.core.jacobians import jacobian_gauss +from specparam.core.errors import (FitError, NoModelError, DataError, + NoDataError, InconsistentDataError) +from specparam.core.strings import (gen_settings_str, gen_model_results_str, + gen_issue_str, gen_width_warning_str) +from specparam.plts.model import plot_model +from specparam.utils.data import trim_spectrum +from specparam.utils.params import compute_gauss_std +from specparam.data import FitResults, ModelRunModes, ModelSettings, SpectrumMetaData +from specparam.data.conversions import model_to_dataframe +from specparam.sim.gen import gen_freqs, gen_aperiodic, gen_periodic, gen_model ################################################################################################### ################################################################################################### -class FOOOF(): - """Model a physiological power spectrum as a combination of aperiodic and periodic components. +class SpectralModel(): + """Model a power spectrum as a combination of aperiodic and periodic components. - WARNING: FOOOF expects frequency and power values in linear space. + WARNING: frequency and power values inputs must be in linear space. Passing in logged frequencies and/or power spectra is not detected, and will silently produce incorrect results. @@ -122,7 +121,7 @@ class FOOOF(): Frequency range of the power spectrum, as [lowest_freq, highest_freq]. freq_res : float Frequency resolution of the power spectrum. - fooofed_spectrum_ : 1d array + modeled_spectrum_ : 1d array The full model fit of the power spectrum, in log10 scale. aperiodic_params_ : 1d array Parameters that define the aperiodic fit. As [Offset, (Knee), Exponent]. @@ -162,7 +161,7 @@ class FOOOF(): def __init__(self, peak_width_limits=(0.5, 12.0), max_n_peaks=np.inf, min_peak_height=0.0, peak_threshold=2.0, aperiodic_mode='fixed', verbose=True): - """Initialize object with desired settings.""" + """Initialize model object.""" # Set input settings self.peak_width_limits = peak_width_limits @@ -182,7 +181,7 @@ def __init__(self, peak_width_limits=(0.5, 12.0), max_n_peaks=np.inf, min_peak_h self._ap_guess = (None, 0, None) # Bounds for aperiodic fitting, as: ((offset_low_bound, knee_low_bound, exp_low_bound), # (offset_high_bound, knee_high_bound, exp_high_bound)) - # By default, aperiodic fitting is unbound, but can be restricted here, if desired + # By default, aperiodic fitting is unbound, but can be restricted here # Even if fitting without knee, leave bounds for knee (they are dropped later) self._ap_bounds = ((-np.inf, -np.inf, -np.inf), (np.inf, np.inf, np.inf)) # Threshold for how far a peak has to be from edge to keep. @@ -298,7 +297,7 @@ def _reset_data_results(self, clear_freqs=False, clear_spectrum=False, clear_res self.r_squared_ = np.nan self.error_ = np.nan - self.fooofed_spectrum_ = None + self.modeled_spectrum_ = None self._spectrum_flat = None self._spectrum_peak_rm = None @@ -339,52 +338,52 @@ def add_data(self, freqs, power_spectrum, freq_range=None, clear_results=True): self._prepare_data(freqs, power_spectrum, freq_range, 1) - def add_settings(self, fooof_settings): - """Add settings into object from a FOOOFSettings object. + def add_settings(self, settings): + """Add settings into object from a ModelSettings object. Parameters ---------- - fooof_settings : FOOOFSettings - A data object containing the settings for a FOOOF model. + settings : ModelSettings + A data object containing the settings for a power spectrum model. """ for setting in OBJ_DESC['settings']: - setattr(self, setting, getattr(fooof_settings, setting)) + setattr(self, setting, getattr(settings, setting)) - self._check_loaded_settings(fooof_settings._asdict()) + self._check_loaded_settings(settings._asdict()) - def add_meta_data(self, fooof_meta_data): - """Add data information into object from a FOOOFMetaData object. + def add_meta_data(self, meta_data): + """Add data information into object from a SpectrumMetaData object. Parameters ---------- - fooof_meta_data : FOOOFMetaData + meta_data : SpectrumMetaData A meta data object containing meta data information. """ for meta_dat in OBJ_DESC['meta_data']: - setattr(self, meta_dat, getattr(fooof_meta_data, meta_dat)) + setattr(self, meta_dat, getattr(meta_data, meta_dat)) self._regenerate_freqs() - def add_results(self, fooof_result): - """Add results data into object from a FOOOFResults object. + def add_results(self, results): + """Add results data into object from a FitResults object. Parameters ---------- - fooof_result : FOOOFResults - A data object containing the results from fitting a FOOOF model. + results : FitResults + A data object containing the results from fitting a power spectrum model. """ - self.aperiodic_params_ = fooof_result.aperiodic_params - self.gaussian_params_ = fooof_result.gaussian_params - self.peak_params_ = fooof_result.peak_params - self.r_squared_ = fooof_result.r_squared - self.error_ = fooof_result.error + self.aperiodic_params_ = results.aperiodic_params + self.gaussian_params_ = results.gaussian_params + self.peak_params_ = results.peak_params + self.r_squared_ = results.r_squared + self.error_ = results.error - self._check_loaded_results(fooof_result._asdict()) + self._check_loaded_results(results._asdict()) def report(self, freqs=None, power_spectrum=None, freq_range=None, @@ -398,7 +397,7 @@ def report(self, freqs=None, power_spectrum=None, freq_range=None, power_spectrum : 1d array, optional Power values, which must be input in linear space. freq_range : list of [float, float], optional - Desired frequency range to fit the model to. + Frequency range to fit the model to. If not provided, fits across the entire given range. plt_log : bool, optional, default: False Whether or not to plot the frequency axis in log space. @@ -435,7 +434,8 @@ def fit(self, freqs=None, power_spectrum=None, freq_range=None): power_spectrum : 1d array, optional Power values, which must be input in linear space. freq_range : list of [float, float], optional - Frequency range to restrict power spectrum to. If not provided, keeps the entire range. + Frequency range to restrict power spectrum to. + If not provided, keeps the entire range. Raises ------ @@ -501,7 +501,7 @@ def fit(self, freqs=None, power_spectrum=None, freq_range=None): self._spectrum_flat = self.power_spectrum - self._ap_fit # Create full power_spectrum model fit - self.fooofed_spectrum_ = self._peak_fit + self._ap_fit + self.modeled_spectrum_ = self._peak_fit + self._ap_fit # Convert gaussian definitions to peak parameters self.peak_params_ = self._create_peak_params(self.gaussian_params_) @@ -548,7 +548,7 @@ def print_results(self, concise=False): Whether to print the report in a concise mode, or not. """ - print(gen_results_fm_str(self, concise)) + print(gen_model_results_str(self, concise)) @staticmethod @@ -569,11 +569,11 @@ def get_settings(self): Returns ------- - FOOOFSettings + ModelSettings Object containing the settings from the current object. """ - return FOOOFSettings(**{key : getattr(self, key) \ + return ModelSettings(**{key : getattr(self, key) \ for key in OBJ_DESC['settings']}) @@ -582,11 +582,11 @@ def get_run_modes(self): Returns ------- - FOOOFRunModes + ModelRunModes Object containing the run modes from the current object. """ - return FOOOFRunModes(**{key.strip('_') : getattr(self, key) \ + return ModelRunModes(**{key.strip('_') : getattr(self, key) \ for key in OBJ_DESC['run_modes']}) @@ -595,11 +595,11 @@ def get_meta_data(self): Returns ------- - FOOOFMetaData + SpectrumMetaData Object containing meta data from the current object. """ - return FOOOFMetaData(**{key : getattr(self, key) \ + return SpectrumMetaData(**{key : getattr(self, key) \ for key in OBJ_DESC['meta_data']}) @@ -684,12 +684,12 @@ def get_model(self, component='full', space='log'): assert space in ['linear', 'log'], "Input for 'space' invalid." if component == 'full': - output = self.fooofed_spectrum_ if space == 'log' else unlog(self.fooofed_spectrum_) + output = self.modeled_spectrum_ if space == 'log' else unlog(self.modeled_spectrum_) elif component == 'aperiodic': output = self._ap_fit if space == 'log' else unlog(self._ap_fit) elif component == 'peak': output = self._peak_fit if space == 'log' else \ - unlog(self.fooofed_spectrum_) - unlog(self._ap_fit) + unlog(self.modeled_spectrum_) - unlog(self._ap_fit) else: raise ValueError('Input for component invalid.') @@ -755,43 +755,41 @@ def get_results(self): Returns ------- - FOOOFResults + FitResults Object containing the model fit results from the current object. """ - return FOOOFResults(**{key.strip('_') : getattr(self, key) \ + return FitResults(**{key.strip('_') : getattr(self, key) \ for key in OBJ_DESC['results']}) - @copy_doc_func_to_method(plot_fm) + @copy_doc_func_to_method(plot_model) def plot(self, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum=None, - freq_range=None, plt_log=False, add_legend=True, save_fig=False, file_name=None, - file_path=None, ax=None, data_kwargs=None, model_kwargs=None, - aperiodic_kwargs=None, peak_kwargs=None, **plot_kwargs): + freq_range=None, plt_log=False, add_legend=True, ax=None, data_kwargs=None, + model_kwargs=None, aperiodic_kwargs=None, peak_kwargs=None, **plot_kwargs): - plot_fm(self, plot_peaks=plot_peaks, plot_aperiodic=plot_aperiodic, freqs=freqs, - power_spectrum=power_spectrum, freq_range=freq_range, plt_log=plt_log, - add_legend=add_legend, save_fig=save_fig, file_name=file_name, - file_path=file_path, ax=ax, data_kwargs=data_kwargs, model_kwargs=model_kwargs, - aperiodic_kwargs=aperiodic_kwargs, peak_kwargs=peak_kwargs, **plot_kwargs) + plot_model(self, plot_peaks=plot_peaks, plot_aperiodic=plot_aperiodic, freqs=freqs, + power_spectrum=power_spectrum, freq_range=freq_range, plt_log=plt_log, + add_legend=add_legend, ax=ax, data_kwargs=data_kwargs, model_kwargs=model_kwargs, + aperiodic_kwargs=aperiodic_kwargs, peak_kwargs=peak_kwargs, **plot_kwargs) - @copy_doc_func_to_method(save_report_fm) + @copy_doc_func_to_method(save_model_report) def save_report(self, file_name, file_path=None, plt_log=False, add_settings=True, **plot_kwargs): - save_report_fm(self, file_name, file_path, plt_log, add_settings, **plot_kwargs) + save_model_report(self, file_name, file_path, plt_log, add_settings, **plot_kwargs) - @copy_doc_func_to_method(save_fm) + @copy_doc_func_to_method(save_model) def save(self, file_name, file_path=None, append=False, save_results=False, save_settings=False, save_data=False): - save_fm(self, file_name, file_path, append, save_results, save_settings, save_data) + save_model(self, file_name, file_path, append, save_results, save_settings, save_data) def load(self, file_name, file_path=None, regenerate=True): - """Load in a FOOOF formatted JSON file to the current object. + """Load in a data file to the current object. Parameters ---------- @@ -1203,7 +1201,7 @@ def _create_peak_params(self, gaus_params): 'bandwidth' of the peak, as opposed to the gaussian parameter, which is 1-sided. Performing this conversion requires that the model has been run, - with `freqs`, `fooofed_spectrum_` and `_ap_fit` all required to be available. + with `freqs`, `modeled_spectrum_` and `_ap_fit` all required to be available. """ peak_params = np.empty((len(gaus_params), 3)) @@ -1214,7 +1212,7 @@ def _create_peak_params(self, gaus_params): ind = np.argmin(np.abs(self.freqs - peak[0])) # Collect peak parameter data - peak_params[ii] = [peak[0], self.fooofed_spectrum_[ind] - self._ap_fit[ind], + peak_params[ii] = [peak[0], self.modeled_spectrum_[ind] - self._ap_fit[ind], peak[2] * 2] return peak_params @@ -1299,7 +1297,7 @@ def _drop_peak_overlap(self, guess): def _calc_r_squared(self): """Calculate the r-squared goodness of fit of the model, compared to the original data.""" - r_val = np.corrcoef(self.power_spectrum, self.fooofed_spectrum_) + r_val = np.corrcoef(self.power_spectrum, self.modeled_spectrum_) self.r_squared_ = r_val[0][1] ** 2 @@ -1328,13 +1326,13 @@ def _calc_error(self, metric=None): metric = self._error_metric if not metric else metric if metric == 'MAE': - self.error_ = np.abs(self.power_spectrum - self.fooofed_spectrum_).mean() + self.error_ = np.abs(self.power_spectrum - self.modeled_spectrum_).mean() elif metric == 'MSE': - self.error_ = ((self.power_spectrum - self.fooofed_spectrum_) ** 2).mean() + self.error_ = ((self.power_spectrum - self.modeled_spectrum_) ** 2).mean() elif metric == 'RMSE': - self.error_ = np.sqrt(((self.power_spectrum - self.fooofed_spectrum_) ** 2).mean()) + self.error_ = np.sqrt(((self.power_spectrum - self.modeled_spectrum_) ** 2).mean()) else: error_msg = "Error metric '{}' not understood or not implemented.".format(metric) @@ -1352,7 +1350,8 @@ def _prepare_data(self, freqs, power_spectrum, freq_range, spectra_dim=1): Power values, which must be input in linear space. 1d vector, or 2d as [n_power_spectra, n_freqs]. freq_range : list of [float, float] - Frequency range to restrict power spectrum to. If None, keeps the entire range. + Frequency range to restrict power spectrum to. + If None, keeps the entire range. spectra_dim : int, optional, default: 1 Dimensionality that the power spectra should have. @@ -1392,7 +1391,7 @@ def _prepare_data(self, freqs, power_spectrum, freq_range, spectra_dim=1): # Check if power values are complex if np.iscomplexobj(power_spectrum): raise DataError("Input power spectra are complex values. " - "FOOOF does not currently support complex inputs.") + "Model fitting does not currently support complex inputs.") # Force data to be dtype of float64 # If they end up as float32, or less, scipy curve_fit fails (sometimes implicitly) @@ -1410,7 +1409,7 @@ def _prepare_data(self, freqs, power_spectrum, freq_range, spectra_dim=1): if freqs[0] == 0.0: freqs, power_spectrum = trim_spectrum(freqs, power_spectrum, [freqs[1], freqs.max()]) if self.verbose: - print("\nFOOOF WARNING: Skipping frequency == 0, " + print("\nFITTING WARNING: Skipping frequency == 0, " "as this causes a problem with fitting.") # Calculate frequency resolution, and actual frequency range of the data @@ -1434,7 +1433,7 @@ def _prepare_data(self, freqs, power_spectrum, freq_range, spectra_dim=1): error_msg = ("The input power spectra data, after logging, contains NaNs or Infs. " "This will cause the fitting to fail. " "One reason this can happen is if inputs are already logged. " - "Inputs data should be in linear spacing, not log.") + "Input data should be in linear spacing, not log.") raise DataError(error_msg) return freqs, power_spectrum, freq_range, freq_res @@ -1505,5 +1504,5 @@ def _regenerate_freqs(self): def _regenerate_model(self): """Regenerate model fit from parameters.""" - self.fooofed_spectrum_, self._peak_fit, self._ap_fit = gen_model( + self.modeled_spectrum_, self._peak_fit, self._ap_fit = gen_model( self.freqs, self.aperiodic_params_, self.gaussian_params_, return_components=True) diff --git a/fooof/objs/group.py b/specparam/objs/group.py similarity index 79% rename from fooof/objs/group.py rename to specparam/objs/group.py index 8949e025..31d8bbee 100644 --- a/fooof/objs/group.py +++ b/specparam/objs/group.py @@ -1,8 +1,7 @@ -"""FOOOFGroup object and associated code for using the FOOOF model on 2D groups of power spectra. +"""Group model object and associated code for fitting the model to 2D groups of power spectra. Notes ----- -FOOOFGroup object docs are imported from FOOOF object at runtime. Methods without defined docstrings import docs at runtime, from aliased external functions. """ @@ -11,35 +10,35 @@ import numpy as np -from fooof.objs import FOOOF -from fooof.plts.fg import plot_fg -from fooof.core.items import OBJ_DESC -from fooof.core.info import get_indices -from fooof.core.utils import check_inds -from fooof.core.errors import NoModelError -from fooof.core.reports import save_report_fg -from fooof.core.strings import gen_results_fg_str -from fooof.core.io import save_fg, load_jsonlines -from fooof.core.modutils import (copy_doc_func_to_method, safe_import, - docs_get_section, replace_docstring_sections) -from fooof.data.conversions import group_to_dataframe +from specparam.objs import SpectralModel +from specparam.plts.group import plot_group +from specparam.core.items import OBJ_DESC +from specparam.core.info import get_indices +from specparam.core.utils import check_inds +from specparam.core.errors import NoModelError +from specparam.core.reports import save_group_report +from specparam.core.strings import gen_group_results_str +from specparam.core.io import save_group, load_jsonlines +from specparam.core.modutils import (copy_doc_func_to_method, safe_import, + docs_get_section, replace_docstring_sections) +from specparam.data.conversions import group_to_dataframe ################################################################################################### ################################################################################################### -@replace_docstring_sections([docs_get_section(FOOOF.__doc__, 'Parameters'), - docs_get_section(FOOOF.__doc__, 'Notes')]) -class FOOOFGroup(FOOOF): +@replace_docstring_sections([docs_get_section(SpectralModel.__doc__, 'Parameters'), + docs_get_section(SpectralModel.__doc__, 'Notes')]) +class SpectralGroupModel(SpectralModel): """Model a group of power spectra as a combination of aperiodic and periodic components. - WARNING: FOOOF expects frequency and power values in linear space. + WARNING: frequency and power values inputs must be in linear space. Passing in logged frequencies and/or power spectra is not detected, and will silently produce incorrect results. Parameters ---------- - %copied in from FOOOF object + %copied in from SpectralModel object Attributes ---------- @@ -52,8 +51,8 @@ class FOOOFGroup(FOOOF): Frequency range of the power spectra, as [lowest_freq, highest_freq]. freq_res : float Frequency resolution of the power spectra. - group_results : list of FOOOFResults - Results of FOOOF model fit for each power spectrum. + group_results : list of FitResults + Results of the model fit for each power spectrum. has_data : bool Whether data is loaded to the object. has_model : bool @@ -67,9 +66,9 @@ class FOOOFGroup(FOOOF): Notes ----- - %copied in from FOOOF object - - The FOOOFGroup object inherits from the FOOOF object. As such it also has data - attributes (`power_spectrum` & `fooofed_spectrum_`), and parameter attributes + %copied in from SpectralModel object + - The group object inherits from the model object. As such it also has data + attributes (`power_spectrum` & `modeled_spectrum_`), and parameter attributes (`aperiodic_params_`, `peak_params_`, `gaussian_params_`, `r_squared_`, `error_`) which are defined in the context of individual model fits. These attributes are used during the fitting process, but in the group context do not store results @@ -82,7 +81,7 @@ class FOOOFGroup(FOOOF): def __init__(self, *args, **kwargs): """Initialize object with desired settings.""" - FOOOF.__init__(self, *args, **kwargs) + SpectralModel.__init__(self, *args, **kwargs) self.power_spectra = None @@ -126,14 +125,14 @@ def has_model(self): def n_peaks_(self): """How many peaks were fit for each model.""" - return [f_res.peak_params.shape[0] for f_res in self] if self.has_model else None + return [res.peak_params.shape[0] for res in self] if self.has_model else None @property def n_null_(self): """How many model fits are null.""" - return sum([1 for f_res in self.group_results if np.isnan(f_res.aperiodic_params[0])]) \ + return sum([1 for res in self.group_results if np.isnan(res.aperiodic_params[0])]) \ if self.has_model else None @@ -141,8 +140,8 @@ def n_null_(self): def null_inds_(self): """The indices for model fits that are null.""" - return [ind for ind, f_res in enumerate(self.group_results) \ - if np.isnan(f_res.aperiodic_params[0])] \ + return [ind for ind, res in enumerate(self.group_results) \ + if np.isnan(res.aperiodic_params[0])] \ if self.has_model else None @@ -217,7 +216,7 @@ def report(self, freqs=None, power_spectra=None, freq_range=None, n_jobs=1, prog power_spectra : 2d array, shape: [n_power_spectra, n_freqs], optional Matrix of power spectrum values, in linear space. freq_range : list of [float, float], optional - Desired frequency range to run FOOOF on. If not provided, fits the entire given range. + Frequency range to fit the model to. If not provided, fits the entire given range. n_jobs : int, optional, default: 1 Number of jobs to run in parallel. 1 is no parallelization. -1 uses all available cores. @@ -244,7 +243,7 @@ def fit(self, freqs=None, power_spectra=None, freq_range=None, n_jobs=1, progres power_spectra : 2d array, shape: [n_power_spectra, n_freqs], optional Matrix of power spectrum values, in linear space. freq_range : list of [float, float], optional - Desired frequency range to run FOOOF on. If not provided, fits the entire given range. + Frequency range to fit the model to. If not provided, fits the entire given range. n_jobs : int, optional, default: 1 Number of jobs to run in parallel. 1 is no parallelization. -1 uses all available cores. @@ -262,7 +261,7 @@ def fit(self, freqs=None, power_spectra=None, freq_range=None, n_jobs=1, progres # If 'verbose', print out a marker of what is being run if self.verbose and not progress: - print('Running FOOOFGroup across {} power spectra.'.format(len(self.power_spectra))) + print('Fitting model across {} power spectra.'.format(len(self.power_spectra))) # Run linearly if n_jobs == 1: @@ -277,7 +276,7 @@ def fit(self, freqs=None, power_spectra=None, freq_range=None, n_jobs=1, progres self._reset_group_results() n_jobs = cpu_count() if n_jobs == -1 else n_jobs with Pool(processes=n_jobs) as pool: - self.group_results = list(_progress(pool.imap(partial(_par_fit, fg=self), + self.group_results = list(_progress(pool.imap(partial(_par_fit, group=self), self.power_spectra), progress, len(self.power_spectra))) @@ -300,9 +299,9 @@ def drop(self, inds): """ for ind in check_inds(inds): - fm = self.get_fooof(ind) - fm._reset_data_results(clear_results=True) - self.group_results[ind] = fm.get_results() + model = self.get_model(ind) + model._reset_data_results(clear_results=True) + self.group_results[ind] = model.get_results() def get_results(self): @@ -336,8 +335,8 @@ def get_params(self, name, col=None): Notes ----- - When extracting peak information ('peak_params' or 'gaussian_params'), an additional column - is appended to the returned array, indicating the index of the model that the peak came from. + When extracting peak information ('peak_params' or 'gaussian_params'), an additional + column is appended to the returned array, indicating the index that the peak came from. """ if not self.has_model: @@ -356,7 +355,7 @@ def get_params(self, name, col=None): # Pull out the requested data field from the group data # As a special case, peak_params are pulled out in a way that appends - # an extra column, indicating which FOOOF run each peak comes from + # an extra column, indicating which model each peak comes from if name in ('peak_params', 'gaussian_params'): # Collect peak data, appending the index of the model it comes from @@ -364,7 +363,7 @@ def get_params(self, name, col=None): for index, data in enumerate(self.group_results)]) # This updates index to grab selected column, and the last column - # This last column is the 'index' column (FOOOF object source) + # This last column is the 'index' column (model object source) if col is not None: col = [col, -1] else: @@ -377,27 +376,27 @@ def get_params(self, name, col=None): return out - @copy_doc_func_to_method(plot_fg) + @copy_doc_func_to_method(plot_group) def plot(self, save_fig=False, file_name=None, file_path=None, **plot_kwargs): - plot_fg(self, save_fig=save_fig, file_name=file_name, file_path=file_path, **plot_kwargs) + plot_group(self, save_fig=save_fig, file_name=file_name, file_path=file_path, **plot_kwargs) - @copy_doc_func_to_method(save_report_fg) + @copy_doc_func_to_method(save_group_report) def save_report(self, file_name, file_path=None, add_settings=True): - save_report_fg(self, file_name, file_path, add_settings) + save_group_report(self, file_name, file_path, add_settings) - @copy_doc_func_to_method(save_fg) + @copy_doc_func_to_method(save_group) def save(self, file_name, file_path=None, append=False, save_results=False, save_settings=False, save_data=False): - save_fg(self, file_name, file_path, append, save_results, save_settings, save_data) + save_group(self, file_name, file_path, append, save_results, save_settings, save_data) def load(self, file_name, file_path=None): - """Load FOOOFGroup data from file. + """Load group data from file. Parameters ---------- @@ -440,44 +439,44 @@ def load(self, file_name, file_path=None): self._reset_data_results(clear_spectrum=True, clear_results=True) - def get_fooof(self, ind, regenerate=True): - """Get a FOOOF object for a specified model fit. + def get_model(self, ind, regenerate=True): + """Get a model fit object for a specified index. Parameters ---------- ind : int - The index of the FOOOFResults in FOOOFGroup.group_results to load. + The index of the model from `group_results` to access. regenerate : bool, optional, default: False - Whether to regenerate the model fits from the given fit parameters. + Whether to regenerate the model fits for the requested model. Returns ------- - fm : FOOOF - The FOOOFResults data loaded into a FOOOF object. + model : SpectralModel + The FitResults data loaded into a model object. """ - # Initialize a FOOOF object, with same settings & run modes as current FOOOFGroup - fm = FOOOF(*self.get_settings(), verbose=self.verbose) - fm.set_run_modes(*self.get_run_modes()) + # Initialize a model object, with same settings & check data mode as current object + model = SpectralModel(*self.get_settings(), verbose=self.verbose) + model.set_run_modes(*self.get_run_modes()) # Add data for specified single power spectrum, if available - # The power spectrum is inverted back to linear, as it is re-logged when added to FOOOF + # The power spectrum is inverted back to linear, as it is re-logged when added to object if self.has_data: - fm.add_data(self.freqs, np.power(10, self.power_spectra[ind])) + model.add_data(self.freqs, np.power(10, self.power_spectra[ind])) # If no power spectrum data available, copy over data information & regenerate freqs else: - fm.add_meta_data(self.get_meta_data()) + model.add_meta_data(self.get_meta_data()) # Add results for specified power spectrum, regenerating full fit if requested - fm.add_results(self.group_results[ind]) + model.add_results(self.group_results[ind]) if regenerate: - fm._regenerate_model() + model._regenerate_model() - return fm + return model def get_group(self, inds): - """Get a FOOOFGroup object with the specified sub-selection of model fits. + """Get a Group model object with the specified sub-selection of model fits. Parameters ---------- @@ -487,33 +486,33 @@ def get_group(self, inds): Returns ------- - fg : FOOOFGroup - The requested selection of results data loaded into a new FOOOFGroup object. + group : SpectralGroupModel + The requested selection of results data loaded into a new group model object. """ # Check and convert indices encoding to list of int inds = check_inds(inds) - # Initialize a new FOOOFGroup object, with same settings and run modes as current FOOOFGroup - fg = FOOOFGroup(*self.get_settings(), verbose=self.verbose) - fg.set_run_modes(*self.get_run_modes()) + # Initialize a new model object, with same settings as current object + group = SpectralGroupModel(*self.get_settings(), verbose=self.verbose) + group.set_run_modes(*self.get_run_modes()) # Add data for specified power spectra, if available - # The power spectra are inverted back to linear, as they are re-logged when added to FOOOF + # Power spectra are inverted back to linear, as they are re-logged when added to object if self.has_data: - fg.add_data(self.freqs, np.power(10, self.power_spectra[inds, :])) + group.add_data(self.freqs, np.power(10, self.power_spectra[inds, :])) # If no power spectrum data available, copy over data information & regenerate freqs else: - fg.add_meta_data(self.get_meta_data()) + group.add_meta_data(self.get_meta_data()) # Add results for specified power spectra - fg.group_results = [self.group_results[ind] for ind in inds] + group.group_results = [self.group_results[ind] for ind in inds] - return fg + return group def print_results(self, concise=False): - """Print out FOOOFGroup results. + """Print out the group results. Parameters ---------- @@ -521,7 +520,7 @@ def print_results(self, concise=False): Whether to print the report in a concise mode, or not. """ - print(gen_results_fg_str(self, concise)) + print(gen_group_results_str(self, concise)) def save_model_report(self, index, file_name, file_path=None, plt_log=False, @@ -544,7 +543,7 @@ def save_model_report(self, index, file_name, file_path=None, plt_log=False, Keyword arguments to pass into the plot method. """ - self.get_fooof(ind=index, regenerate=True).save_report(\ + self.get_model(ind=index, regenerate=True).save_report(\ file_name, file_path, plt_log, add_settings, **plot_kwargs) @@ -568,13 +567,13 @@ def to_df(self, peak_org): def _fit(self, *args, **kwargs): - """Create an alias to FOOOF.fit for FOOOFGroup object, for internal use.""" + """Create an alias to SpectralModel.fit for the group object, for internal use.""" super().fit(*args, **kwargs) def _get_results(self): - """Create an alias to FOOOF.get_results for FOOOFGroup object, for internal use.""" + """Create an alias to SpectralModel.get_results for the group object, for internal use.""" return super().get_results() @@ -589,12 +588,12 @@ def _check_width_limits(self): ################################################################################################### ################################################################################################### -def _par_fit(power_spectrum, fg): +def _par_fit(power_spectrum, group): """Helper function for running in parallel.""" - fg._fit(power_spectrum=power_spectrum) + group._fit(power_spectrum=power_spectrum) - return fg._get_results() + return group._get_results() def _progress(iterable, progress, n_to_run): @@ -632,7 +631,7 @@ def _progress(iterable, progress, n_to_run): raise ValueError("Progress bar option not understood.") # Set the display text for the progress bar - pbar_desc = 'Running FOOOFGroup' + pbar_desc = 'Running group fits.' # Use a tqdm, progress bar, if requested if progress: diff --git a/fooof/objs/utils.py b/specparam/objs/utils.py similarity index 55% rename from fooof/objs/utils.py rename to specparam/objs/utils.py index a9ac7934..09785e11 100644 --- a/fooof/objs/utils.py +++ b/specparam/objs/utils.py @@ -1,22 +1,22 @@ -"""Utility functions for managing and manipulating FOOOF objects.""" +"""Utility functions for managing and manipulating model objects.""" import numpy as np -from fooof.sim import gen_freqs -from fooof.data import FOOOFResults -from fooof.objs import FOOOF, FOOOFGroup -from fooof.analysis.periodic import get_band_peak_fg -from fooof.core.errors import NoModelError, IncompatibleSettingsError +from specparam.sim import gen_freqs +from specparam.data import FitResults +from specparam.objs import SpectralModel, SpectralGroupModel +from specparam.analysis.periodic import get_band_peak_group +from specparam.core.errors import NoModelError, IncompatibleSettingsError ################################################################################################### ################################################################################################### -def compare_info(fooof_lst, aspect): - """Compare a specified aspect of FOOOF objects across instances. +def compare_model_objs(model_objs, aspect): + """Compare multiple model, checking for consistent attributes. Parameters ---------- - fooof_lst : list of FOOOF and / or FOOOFGroup + model_objs : list of SpectralModel and/or SpectralGroupModel Objects whose attributes are to be compared. aspect : {'settings', 'meta_data'} Which set of attributes to compare the objects across. @@ -28,8 +28,8 @@ def compare_info(fooof_lst, aspect): """ # Check specified aspect of the objects are the same across instances - for f_obj_1, f_obj_2 in zip(fooof_lst[:-1], fooof_lst[1:]): - if getattr(f_obj_1, 'get_' + aspect)() != getattr(f_obj_2, 'get_' + aspect)(): + for m_obj_1, m_obj_2 in zip(model_objs[:-1], model_objs[1:]): + if getattr(m_obj_1, 'get_' + aspect)() != getattr(m_obj_2, 'get_' + aspect)(): consistent = False break else: @@ -38,12 +38,12 @@ def compare_info(fooof_lst, aspect): return consistent -def average_fg(fg, bands, avg_method='mean', regenerate=True): - """Average across model fits in a FOOOFGroup object. +def average_group(group, bands, avg_method='mean', regenerate=True): + """Average across model fits in a group model object. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object with model fit results to average across. bands : Bands Bands object that defines the frequency bands to collapse peaks across. @@ -54,7 +54,7 @@ def average_fg(fg, bands, avg_method='mean', regenerate=True): Returns ------- - fm : FOOOF + model : SpectralModel Object containing the average model results. Raises @@ -65,7 +65,7 @@ def average_fg(fg, bands, avg_method='mean', regenerate=True): If there are no model fit results available to average across. """ - if not fg.has_model: + if not group.has_model: raise NoModelError("No model fit results are available, can not proceed.") avg_funcs = {'mean' : np.nanmean, 'median' : np.nanmedian} @@ -73,7 +73,7 @@ def average_fg(fg, bands, avg_method='mean', regenerate=True): raise ValueError("Requested average method not understood.") # Aperiodic parameters: extract & average - ap_params = avg_funcs[avg_method](fg.get_params('aperiodic_params'), 0) + ap_params = avg_funcs[avg_method](group.get_params('aperiodic_params'), 0) # Periodic parameters: extract & average peak_params = [] @@ -81,8 +81,8 @@ def average_fg(fg, bands, avg_method='mean', regenerate=True): for band_def in bands.definitions: - peaks = get_band_peak_fg(fg, band_def, attribute='peak_params') - gauss = get_band_peak_fg(fg, band_def, attribute='gaussian_params') + peaks = get_band_peak_group(group, band_def, attribute='peak_params') + gauss = get_band_peak_group(group, band_def, attribute='gaussian_params') # Check if there are any extracted peaks - if not, don't add # Note that we only check peaks, but gauss should be the same @@ -94,31 +94,31 @@ def average_fg(fg, bands, avg_method='mean', regenerate=True): gauss_params = np.array(gauss_params) # Goodness of fit measures: extract & average - r2 = avg_funcs[avg_method](fg.get_params('r_squared')) - error = avg_funcs[avg_method](fg.get_params('error')) + r2 = avg_funcs[avg_method](group.get_params('r_squared')) + error = avg_funcs[avg_method](group.get_params('error')) - # Collect all results together, to be added to FOOOF object - results = FOOOFResults(ap_params, peak_params, r2, error, gauss_params) + # Collect all results together, to be added to the model object + results = FitResults(ap_params, peak_params, r2, error, gauss_params) - # Create the new FOOOF object, with settings, data info & results - fm = FOOOF() - fm.add_settings(fg.get_settings()) - fm.add_meta_data(fg.get_meta_data()) - fm.add_results(results) + # Create the new model object, with settings, data info & results + model = SpectralModel() + model.add_settings(group.get_settings()) + model.add_meta_data(group.get_meta_data()) + model.add_results(results) # Generate the average model from the parameters if regenerate: - fm._regenerate_model() + model._regenerate_model() - return fm + return model -def average_reconstructions(fg, avg_method='mean'): +def average_reconstructions(group, avg_method='mean'): """Average across model reconstructions for a group of power spectra. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object with model fit results to average across. avg : {'mean', 'median'} Averaging function to use. @@ -132,33 +132,33 @@ def average_reconstructions(fg, avg_method='mean'): Note that power values are in log10 space. """ - if not fg.has_model: + if not group.has_model: raise NoModelError("No model fit results are available, can not proceed.") avg_funcs = {'mean' : np.nanmean, 'median' : np.nanmedian} if avg_method not in avg_funcs.keys(): raise ValueError("Requested average method not understood.") - models = np.zeros(shape=fg.power_spectra.shape) - for ind in range(len(fg)): - models[ind, :] = fg.get_fooof(ind, regenerate=True).fooofed_spectrum_ + models = np.zeros(shape=group.power_spectra.shape) + for ind in range(len(group)): + models[ind, :] = group.get_model(ind, regenerate=True).modeled_spectrum_ avg_model = avg_funcs[avg_method](models, 0) - return fg.freqs, avg_model + return group.freqs, avg_model -def combine_fooofs(fooofs): - """Combine a group of FOOOF and/or FOOOFGroup objects into a single FOOOFGroup object. +def combine_model_objs(model_objs): + """Combine a group of model objects into a single group model object. Parameters ---------- - fooofs : list of FOOOF or FOOOFGroup - Objects to be concatenated into a FOOOFGroup. + model_objs : list of SpectralModel or SpectralGroupModel + Objects to be concatenated into a group model object. Returns ------- - fg : FOOOFGroup + group : SpectralGroupModel Resultant object from combining inputs. Raises @@ -168,97 +168,97 @@ def combine_fooofs(fooofs): Examples -------- - Combine FOOOF objects together (where `fm1`, `fm2` & `fm3` are assumed to be defined and fit): + Combine model objects together (where `fm1`, `fm2` & `fm3` are assumed to be defined and fit): - >>> fg = combine_fooofs([fm1, fm2, fm3]) # doctest:+SKIP + >>> group = combine_model_objs([fm1, fm2, fm3]) # doctest:+SKIP - Combine FOOOFGroup objects together (where `fg1` & `fg2` are assumed to be defined and fit): + Combine group model objects together (where `fg1` & `fg2` are assumed to be defined and fit): - >>> fg = combine_fooofs([fg1, fg2]) # doctest:+SKIP + >>> group = combine_model_objs([fg1, fg2]) # doctest:+SKIP """ # Compare settings - if not compare_info(fooofs, 'settings') or not compare_info(fooofs, 'meta_data'): + if not compare_model_objs(model_objs, 'settings') \ + or not compare_model_objs(model_objs, 'meta_data'): raise IncompatibleSettingsError("These objects have incompatible settings " "or meta data, and so cannot be combined.") - # Initialize FOOOFGroup object, with settings derived from input objects - fg = FOOOFGroup(*fooofs[0].get_settings(), verbose=fooofs[0].verbose) + # Initialize group model object, with settings derived from input objects + group = SpectralGroupModel(*model_objs[0].get_settings(), verbose=model_objs[0].verbose) # Use a temporary store to collect spectra, as we'll only add it if it is consistently present # We check how many frequencies by accessing meta data, in case of no frequency vector - meta_data = fooofs[0].get_meta_data() + meta_data = model_objs[0].get_meta_data() n_freqs = len(gen_freqs(meta_data.freq_range, meta_data.freq_res)) temp_power_spectra = np.empty([0, n_freqs]) - # Add FOOOF results from each FOOOF object to group - for f_obj in fooofs: + # Add results from each model object to group + for m_obj in model_objs: - # Add FOOOFGroup object - if isinstance(f_obj, FOOOFGroup): - fg.group_results.extend(f_obj.group_results) - if f_obj.power_spectra is not None: - temp_power_spectra = np.vstack([temp_power_spectra, f_obj.power_spectra]) + # Add group object + if isinstance(m_obj, SpectralGroupModel): + group.group_results.extend(m_obj.group_results) + if m_obj.power_spectra is not None: + temp_power_spectra = np.vstack([temp_power_spectra, m_obj.power_spectra]) - # Add FOOOF object + # Add model object else: - fg.group_results.append(f_obj.get_results()) - if f_obj.power_spectrum is not None: - temp_power_spectra = np.vstack([temp_power_spectra, f_obj.power_spectrum]) + group.group_results.append(m_obj.get_results()) + if m_obj.power_spectrum is not None: + temp_power_spectra = np.vstack([temp_power_spectra, m_obj.power_spectrum]) # If the number of collected power spectra is consistent, then add them to object - if len(fg) == temp_power_spectra.shape[0]: - fg.power_spectra = temp_power_spectra + if len(group) == temp_power_spectra.shape[0]: + group.power_spectra = temp_power_spectra # Set the check data mode, as True if any of the inputs have it on, False otherwise - fg.set_check_data_mode(any(getattr(f_obj, '_check_data') for f_obj in fooofs)) + group.set_check_data_mode(any(getattr(m_obj, '_check_data') for m_obj in model_objs)) # Add data information information - fg.add_meta_data(fooofs[0].get_meta_data()) + group.add_meta_data(model_objs[0].get_meta_data()) - return fg + return group -def fit_fooof_3d(fg, freqs, power_spectra, freq_range=None, n_jobs=1): - """Fit FOOOF models across a 3d array of power spectra. +def fit_models_3d(group, freqs, power_spectra, freq_range=None, n_jobs=1): + """Fit power spectrum models across a 3d array of power spectra. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to fit with, initialized with desired settings. freqs : 1d array Frequency values for the power spectra, in linear space. power_spectra : 3d array Power values, in linear space, with shape as: [n_conditions, n_power_spectra, n_freqs]. freq_range : list of [float, float], optional - Desired frequency range to fit. If not provided, fits the entire given range. + Frequency range to fit. If not provided, fits the entire given range. n_jobs : int, optional, default: 1 Number of jobs to run in parallel. 1 is no parallelization. -1 uses all available cores. Returns ------- - fgs : list of FOOOFGroups - Collected FOOOFGroups after fitting across power spectra, length of n_conditions. - + all_models : list of SpectralGroupModel + Collected model results after fitting across power spectra, length of n_conditions. Examples -------- Fit a 3d array of power spectra, assuming `freqs` and `spectra` are already defined: - >>> from fooof import FOOOFGroup - >>> fg = FOOOFGroup(peak_width_limits=[1, 6], min_peak_height=0.1) - >>> fgs = fit_fooof_3d(fg, freqs, power_spectra, freq_range=[3, 30]) # doctest:+SKIP + >>> from specparam import SpectralGroupModel + >>> group = SpectralGroupModel(peak_width_limits=[1, 6], min_peak_height=0.1) + >>> models = fit_models_3d(group, freqs, power_spectra, freq_range=[3, 30]) # doctest:+SKIP """ # Reshape 3d data to 2d and fit, in order to fit with a single group model object shape = np.shape(power_spectra) powers_2d = np.reshape(power_spectra, (shape[0] * shape[1], shape[2])) - fg.fit(freqs, powers_2d, freq_range, n_jobs) + group.fit(freqs, powers_2d, freq_range, n_jobs) # Reorganize 2d results into a list of model group objects, to reflect original shape - fgs = [fg.get_group(range(dim_a * shape[1], (dim_a + 1) * shape[1])) \ + all_models = [group.get_group(range(dim_a * shape[1], (dim_a + 1) * shape[1])) \ for dim_a in range(shape[0])] - return fgs + return all_models diff --git a/specparam/plts/__init__.py b/specparam/plts/__init__.py new file mode 100644 index 00000000..3e656740 --- /dev/null +++ b/specparam/plts/__init__.py @@ -0,0 +1,3 @@ +"""Plots sub-module.""" + +from .spectra import plot_spectra diff --git a/fooof/plts/annotate.py b/specparam/plts/annotate.py similarity index 68% rename from fooof/plts/annotate.py rename to specparam/plts/annotate.py index d72559aa..e19009d6 100644 --- a/fooof/plts/annotate.py +++ b/specparam/plts/annotate.py @@ -2,19 +2,19 @@ import numpy as np -from fooof.core.utils import nearest_ind -from fooof.core.errors import NoModelError -from fooof.core.funcs import gaussian_function -from fooof.core.modutils import safe_import, check_dependency +from specparam.core.utils import nearest_ind +from specparam.core.errors import NoModelError +from specparam.core.funcs import gaussian_function +from specparam.core.modutils import safe_import, check_dependency -from fooof.sim.gen import gen_aperiodic -from fooof.analysis.periodic import get_band_peak_fm -from fooof.utils.params import compute_knee_frequency, compute_fwhm +from specparam.sim.gen import gen_aperiodic +from specparam.analysis.periodic import get_band_peak +from specparam.utils.params import compute_knee_frequency, compute_fwhm -from fooof.plts.spectra import plot_spectra -from fooof.plts.utils import check_ax, savefig -from fooof.plts.settings import PLT_FIGSIZES, PLT_COLORS -from fooof.plts.style import style_spectrum_plot +from specparam.plts.spectra import plot_spectra +from specparam.plts.utils import check_ax, savefig +from specparam.plts.settings import PLT_FIGSIZES, PLT_COLORS +from specparam.plts.style import style_spectrum_plot plt = safe_import('.pyplot', 'matplotlib') mpatches = safe_import('.patches', 'matplotlib') @@ -24,50 +24,50 @@ @savefig @check_dependency(plt, 'matplotlib') -def plot_annotated_peak_search(fm): +def plot_annotated_peak_search(model): """Plot a series of plots illustrating the peak search from a flattened spectrum. Parameters ---------- - fm : FOOOF - FOOOF object, with model fit, data and settings available. + model : SpectralModel + Model object, with model fit, data and settings available. """ # Recalculate the initial aperiodic fit and flattened spectrum that # is the same as the one that is used in the peak fitting procedure - flatspec = fm.power_spectrum - \ - gen_aperiodic(fm.freqs, fm._robust_ap_fit(fm.freqs, fm.power_spectrum)) + flatspec = model.power_spectrum - \ + gen_aperiodic(model.freqs, model._robust_ap_fit(model.freqs, model.power_spectrum)) # Calculate ylims of the plot that are scaled to the range of the data ylims = [min(flatspec) - 0.1 * np.abs(min(flatspec)), max(flatspec) + 0.1 * max(flatspec)] # Sort parameters by peak height - gaussian_params = fm.gaussian_params_[fm.gaussian_params_[:, 1].argsort()][::-1] + gaussian_params = model.gaussian_params_[model.gaussian_params_[:, 1].argsort()][::-1] # Loop through the iterative search for each peak - for ind in range(fm.n_peaks_ + 1): + for ind in range(model.n_peaks_ + 1): # This forces the creation of a new plotting axes per iteration ax = check_ax(None, PLT_FIGSIZES['spectral']) - plot_spectra(fm.freqs, flatspec, ax=ax, linewidth=2.5, + plot_spectra(model.freqs, flatspec, ax=ax, linewidth=2.5, label='Flattened Spectrum', color=PLT_COLORS['data']) - plot_spectra(fm.freqs, [fm.peak_threshold * np.std(flatspec)]*len(fm.freqs), ax=ax, + plot_spectra(model.freqs, [model.peak_threshold * np.std(flatspec)]*len(model.freqs), ax=ax, label='Relative Threshold', color='orange', linewidth=2.5, linestyle='dashed') - plot_spectra(fm.freqs, [fm.min_peak_height]*len(fm.freqs), ax=ax, + plot_spectra(model.freqs, [model.min_peak_height]*len(model.freqs), ax=ax, label='Absolute Threshold', color='red', linewidth=2.5, linestyle='dashed') maxi = np.argmax(flatspec) - ax.plot(fm.freqs[maxi], flatspec[maxi], '.', + ax.plot(model.freqs[maxi], flatspec[maxi], '.', color=PLT_COLORS['periodic'], alpha=0.75, markersize=30) ax.set_ylim(ylims) ax.set_title('Iteration #' + str(ind+1), fontsize=16) - if ind < fm.n_peaks_: + if ind < model.n_peaks_: - gauss = gaussian_function(fm.freqs, *gaussian_params[ind, :]) - plot_spectra(fm.freqs, gauss, ax=ax, label='Gaussian Fit', + gauss = gaussian_function(model.freqs, *gaussian_params[ind, :]) + plot_spectra(model.freqs, gauss, ax=ax, label='Gaussian Fit', color=PLT_COLORS['periodic'], linestyle=':', linewidth=3.0) flatspec = flatspec - gauss @@ -77,14 +77,14 @@ def plot_annotated_peak_search(fm): @savefig @check_dependency(plt, 'matplotlib') -def plot_annotated_model(fm, plt_log=False, annotate_peaks=True, +def plot_annotated_model(model, plt_log=False, annotate_peaks=True, annotate_aperiodic=True, ax=None): - """Plot a an annotated power spectrum and model, from a FOOOF object. + """Plot a an annotated power spectrum and model, from a model object. Parameters ---------- - fm : FOOOF - FOOOF object, with model fit, data and settings available. + model : SpectralModel + Model object, with model fit, data and settings available. plt_log : boolean, optional, default: False Whether to plot the frequency values in log10 spacing. annotate_peaks : boolean, optional, default: True @@ -101,7 +101,7 @@ def plot_annotated_model(fm, plt_log=False, annotate_peaks=True, """ # Check that model is available - if not fm.has_model: + if not model.has_model: raise NoModelError("No model is available to plot, can not proceed.") # Settings @@ -112,16 +112,18 @@ def plot_annotated_model(fm, plt_log=False, annotate_peaks=True, # Create the baseline figure ax = check_ax(ax, PLT_FIGSIZES['spectral']) - fm.plot(plot_peaks='dot-shade-width', plt_log=plt_log, ax=ax, - data_kwargs={'lw' : lw1, 'alpha' : 0.6}, - aperiodic_kwargs={'lw' : lw1, 'zorder' : 10}, - model_kwargs={'lw' : lw1, 'alpha' : 0.5}, - peak_kwargs={'dot' : {'color' : PLT_COLORS['periodic'], 'ms' : ms1, 'lw' : lw2}, - 'shade' : {'color' : PLT_COLORS['periodic']}, - 'width' : {'color' : PLT_COLORS['periodic'], 'alpha' : 0.75, 'lw' : lw2}}) + model.plot(plot_peaks='dot-shade-width', plt_log=plt_log, ax=ax, + data_kwargs={'lw' : lw1, 'alpha' : 0.6}, + aperiodic_kwargs={'lw' : lw1, 'zorder' : 10}, + model_kwargs={'lw' : lw1, 'alpha' : 0.5}, + peak_kwargs={'dot' : {'color' : PLT_COLORS['periodic'], + 'ms' : ms1, 'lw' : lw2}, + 'shade' : {'color' : PLT_COLORS['periodic']}, + 'width' : {'color' : PLT_COLORS['periodic'], + 'alpha' : 0.75, 'lw' : lw2}}) # Get freqs for plotting, and convert to log if needed - freqs = fm.freqs if not plt_log else np.log10(fm.freqs) + freqs = model.freqs if not plt_log else np.log10(model.freqs) ## Buffers: for spacing things out on the plot (scaled by plot values) x_buff1 = max(freqs) * 0.1 @@ -133,10 +135,10 @@ def plot_annotated_model(fm, plt_log=False, annotate_peaks=True, # See: https://github.com/matplotlib/matplotlib/issues/12820. Fixed in 3.2.1. bug_buff = 0.000001 - if annotate_peaks and fm.n_peaks_: + if annotate_peaks and model.n_peaks_: # Extract largest peak, to annotate, grabbing gaussian params - gauss = get_band_peak_fm(fm, fm.freq_range, attribute='gaussian_params') + gauss = get_band_peak(model, model.freq_range, attribute='gaussian_params') peak_ctr, peak_hgt, peak_wid = gauss bw_freqs = [peak_ctr - 0.5 * compute_fwhm(peak_wid), @@ -146,7 +148,7 @@ def plot_annotated_model(fm, plt_log=False, annotate_peaks=True, peak_ctr = np.log10(peak_ctr) bw_freqs = np.log10(bw_freqs) - peak_top = fm.power_spectrum[nearest_ind(freqs, peak_ctr)] + peak_top = model.power_spectrum[nearest_ind(freqs, peak_ctr)] # Annotate Peak CF ax.annotate('Center Frequency', @@ -180,24 +182,24 @@ def plot_annotated_model(fm, plt_log=False, annotate_peaks=True, # Annotate Aperiodic Offset # Add a line to indicate offset, without adjusting plot limits below it ax.set_autoscaley_on(False) - ax.plot([freqs[0], freqs[0]], [ax.get_ylim()[0], fm.fooofed_spectrum_[0]], + ax.plot([freqs[0], freqs[0]], [ax.get_ylim()[0], model.modeled_spectrum_[0]], color=PLT_COLORS['aperiodic'], linewidth=lw2, alpha=0.5) ax.annotate('Offset', - xy=(freqs[0]+bug_buff, fm.power_spectrum[0]-y_buff1), - xytext=(freqs[0]-x_buff1, fm.power_spectrum[0]-y_buff1), + xy=(freqs[0]+bug_buff, model.power_spectrum[0]-y_buff1), + xytext=(freqs[0]-x_buff1, model.power_spectrum[0]-y_buff1), verticalalignment='center', horizontalalignment='center', arrowprops=dict(facecolor=PLT_COLORS['aperiodic'], shrink=shrink), color=PLT_COLORS['aperiodic'], fontsize=fontsize) # Annotate Aperiodic Knee - if fm.aperiodic_mode == 'knee': + if model.aperiodic_mode == 'knee': # Find the knee frequency point to annotate - knee_freq = compute_knee_frequency(fm.get_params('aperiodic', 'knee'), - fm.get_params('aperiodic', 'exponent')) + knee_freq = compute_knee_frequency(model.get_params('aperiodic', 'knee'), + model.get_params('aperiodic', 'exponent')) knee_freq = np.log10(knee_freq) if plt_log else knee_freq - knee_pow = fm.power_spectrum[nearest_ind(freqs, knee_freq)] + knee_pow = model.power_spectrum[nearest_ind(freqs, knee_freq)] # Add a dot to the plot indicating the knee frequency ax.plot(knee_freq, knee_pow, 'o', color=PLT_COLORS['aperiodic'], ms=ms1*1.5, alpha=0.7) @@ -212,8 +214,8 @@ def plot_annotated_model(fm, plt_log=False, annotate_peaks=True, # Annotate Aperiodic Exponent mid_ind = int(len(freqs)/2) ax.annotate('Exponent', - xy=(freqs[mid_ind], fm.power_spectrum[mid_ind]), - xytext=(freqs[mid_ind]-x_buff2, fm.power_spectrum[mid_ind]-y_buff1), + xy=(freqs[mid_ind], model.power_spectrum[mid_ind]), + xytext=(freqs[mid_ind]-x_buff2, model.power_spectrum[mid_ind]-y_buff1), verticalalignment='center', arrowprops=dict(facecolor=PLT_COLORS['aperiodic'], shrink=shrink), color=PLT_COLORS['aperiodic'], fontsize=fontsize) diff --git a/fooof/plts/aperiodic.py b/specparam/plts/aperiodic.py similarity index 93% rename from fooof/plts/aperiodic.py rename to specparam/plts/aperiodic.py index b4a08368..45c989d5 100644 --- a/fooof/plts/aperiodic.py +++ b/specparam/plts/aperiodic.py @@ -5,11 +5,11 @@ import numpy as np import matplotlib.pyplot as plt -from fooof.sim.gen import gen_freqs, gen_aperiodic -from fooof.core.modutils import safe_import, check_dependency -from fooof.plts.settings import PLT_FIGSIZES -from fooof.plts.style import style_param_plot, style_plot -from fooof.plts.utils import check_ax, recursive_plot, savefig, check_plot_kwargs +from specparam.sim.gen import gen_freqs, gen_aperiodic +from specparam.core.modutils import safe_import, check_dependency +from specparam.plts.settings import PLT_FIGSIZES +from specparam.plts.style import style_param_plot, style_plot +from specparam.plts.utils import check_ax, recursive_plot, savefig, check_plot_kwargs plt = safe_import('.pyplot', 'matplotlib') diff --git a/fooof/plts/error.py b/specparam/plts/error.py similarity index 85% rename from fooof/plts/error.py rename to specparam/plts/error.py index fd488ddf..53148403 100644 --- a/fooof/plts/error.py +++ b/specparam/plts/error.py @@ -2,11 +2,11 @@ import numpy as np -from fooof.core.modutils import safe_import, check_dependency -from fooof.plts.spectra import plot_spectra -from fooof.plts.settings import PLT_FIGSIZES -from fooof.plts.style import style_spectrum_plot, style_plot -from fooof.plts.utils import check_ax, savefig +from specparam.core.modutils import safe_import, check_dependency +from specparam.plts.spectra import plot_spectra +from specparam.plts.settings import PLT_FIGSIZES +from specparam.plts.style import style_spectrum_plot, style_plot +from specparam.plts.utils import check_ax, savefig plt = safe_import('.pyplot', 'matplotlib') diff --git a/fooof/plts/fg.py b/specparam/plts/group.py similarity index 57% rename from fooof/plts/fg.py rename to specparam/plts/group.py index eef0b6e4..86c7cc39 100644 --- a/fooof/plts/fg.py +++ b/specparam/plts/group.py @@ -1,16 +1,16 @@ -"""Plots for the FOOOFGroup object. +"""Plots for the group model object. Notes ----- -This file contains plotting functions that take as input a FOOOFGroup object. +This file contains plotting functions that take as input a group model object. """ -from fooof.core.errors import NoModelError -from fooof.core.modutils import safe_import, check_dependency -from fooof.plts.settings import PLT_FIGSIZES -from fooof.plts.templates import plot_scatter_1, plot_scatter_2, plot_hist -from fooof.plts.utils import savefig -from fooof.plts.style import style_plot +from specparam.core.errors import NoModelError +from specparam.core.modutils import safe_import, check_dependency +from specparam.plts.settings import PLT_FIGSIZES +from specparam.plts.templates import plot_scatter_1, plot_scatter_2, plot_hist +from specparam.plts.utils import savefig +from specparam.plts.style import style_plot plt = safe_import('.pyplot', 'matplotlib') gridspec = safe_import('.gridspec', 'matplotlib') @@ -20,29 +20,23 @@ @savefig @check_dependency(plt, 'matplotlib') -def plot_fg(fg, save_fig=False, file_name=None, file_path=None, **plot_kwargs): - """Plot a figure with subplots visualizing the parameters from a FOOOFGroup object. +def plot_group(group, **plot_kwargs): + """Plot a figure with subplots visualizing the parameters from a group model object. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object containing results from fitting a group of power spectra. - save_fig : bool, optional, default: False - Whether to save out a copy of the plot. - file_name : str, optional - Name to give the saved out file. - file_path : Path or str, optional - Path to directory to save to. If None, saves to current directory. **plot_kwargs Additional plot related keyword arguments, with styling options managed by ``style_plot``. Raises ------ NoModelError - If the FOOOF object does not have model fit data available to plot. + If the model object does not have model fit data available to plot. """ - if not fg.has_model: + if not group.has_model: raise NoModelError("No model fit results are available, can not proceed.") fig = plt.figure(figsize=plot_kwargs.pop('figsize', PLT_FIGSIZES['group'])) @@ -54,26 +48,26 @@ def plot_fg(fg, save_fig=False, file_name=None, file_path=None, **plot_kwargs): # Aperiodic parameters plot ax0 = plt.subplot(gs[0, 0]) - plot_fg_ap(fg, ax0, **scatter_kwargs, custom_styler=None) + plot_group_aperiodic(group, ax0, **scatter_kwargs, custom_styler=None) # Goodness of fit plot ax1 = plt.subplot(gs[0, 1]) - plot_fg_gf(fg, ax1, **scatter_kwargs, custom_styler=None) + plot_group_goodness(group, ax1, **scatter_kwargs, custom_styler=None) # Center frequencies plot ax2 = plt.subplot(gs[1, :]) - plot_fg_peak_cens(fg, ax2, **plot_kwargs, custom_styler=None) + plot_group_peak_frequencies(group, ax2, **plot_kwargs, custom_styler=None) @savefig @style_plot @check_dependency(plt, 'matplotlib') -def plot_fg_ap(fg, ax=None, **plot_kwargs): +def plot_group_aperiodic(group, ax=None, **plot_kwargs): """Plot aperiodic fit parameters, in a scatter plot. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to plot data from. ax : matplotlib.Axes, optional Figure axes upon which to plot. @@ -81,24 +75,24 @@ def plot_fg_ap(fg, ax=None, **plot_kwargs): Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ - if fg.aperiodic_mode == 'knee': - plot_scatter_2(fg.get_params('aperiodic_params', 'exponent'), 'Exponent', - fg.get_params('aperiodic_params', 'knee'), 'Knee', + if group.aperiodic_mode == 'knee': + plot_scatter_2(group.get_params('aperiodic_params', 'exponent'), 'Exponent', + group.get_params('aperiodic_params', 'knee'), 'Knee', 'Aperiodic Fit', ax=ax) else: - plot_scatter_1(fg.get_params('aperiodic_params', 'exponent'), 'Exponent', + plot_scatter_1(group.get_params('aperiodic_params', 'exponent'), 'Exponent', 'Aperiodic Fit', ax=ax) @savefig @style_plot @check_dependency(plt, 'matplotlib') -def plot_fg_gf(fg, ax=None, **plot_kwargs): +def plot_group_goodness(group, ax=None, **plot_kwargs): """Plot goodness of fit results, in a scatter plot. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to plot data from. ax : matplotlib.Axes, optional Figure axes upon which to plot. @@ -106,19 +100,19 @@ def plot_fg_gf(fg, ax=None, **plot_kwargs): Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ - plot_scatter_2(fg.get_params('error'), 'Error', - fg.get_params('r_squared'), 'R^2', 'Goodness of Fit', ax=ax) + plot_scatter_2(group.get_params('error'), 'Error', + group.get_params('r_squared'), 'R^2', 'Goodness of Fit', ax=ax) @savefig @style_plot @check_dependency(plt, 'matplotlib') -def plot_fg_peak_cens(fg, ax=None, **plot_kwargs): +def plot_group_peak_frequencies(group, ax=None, **plot_kwargs): """Plot peak center frequencies, in a histogram. Parameters ---------- - fg : FOOOFGroup + group : SpectralGroupModel Object to plot data from. ax : matplotlib.Axes, optional Figure axes upon which to plot. @@ -126,5 +120,5 @@ def plot_fg_peak_cens(fg, ax=None, **plot_kwargs): Additional plot related keyword arguments, with styling options managed by ``style_plot``. """ - plot_hist(fg.get_params('peak_params', 0)[:, 0], 'Center Frequency', - 'Peaks - Center Frequencies', x_lims=fg.freq_range, ax=ax) + plot_hist(group.get_params('peak_params', 0)[:, 0], 'Center Frequency', + 'Peaks - Center Frequencies', x_lims=group.freq_range, ax=ax) diff --git a/fooof/plts/fm.py b/specparam/plts/model.py similarity index 72% rename from fooof/plts/fm.py rename to specparam/plts/model.py index ae3be4d0..7e767500 100644 --- a/fooof/plts/fm.py +++ b/specparam/plts/model.py @@ -1,21 +1,21 @@ -"""Plots for the FOOOF object. +"""Plots for the model object. Notes ----- -This file contains plotting functions that take as input a FOOOF object. +This file contains plotting functions that take as input a model object. """ import numpy as np -from fooof.core.utils import nearest_ind -from fooof.core.modutils import safe_import, check_dependency -from fooof.sim.gen import gen_periodic -from fooof.utils.data import trim_spectrum -from fooof.utils.params import compute_fwhm -from fooof.plts.spectra import plot_spectra -from fooof.plts.settings import PLT_FIGSIZES, PLT_COLORS -from fooof.plts.utils import check_ax, check_plot_kwargs, savefig -from fooof.plts.style import style_spectrum_plot, style_plot +from specparam.core.utils import nearest_ind +from specparam.core.modutils import safe_import, check_dependency +from specparam.sim.gen import gen_periodic +from specparam.utils.data import trim_spectrum +from specparam.utils.params import compute_fwhm +from specparam.plts.spectra import plot_spectra +from specparam.plts.settings import PLT_FIGSIZES, PLT_COLORS +from specparam.plts.utils import check_ax, check_plot_kwargs, savefig +from specparam.plts.style import style_spectrum_plot, style_plot plt = safe_import('.pyplot', 'matplotlib') @@ -25,15 +25,14 @@ @savefig @style_plot @check_dependency(plt, 'matplotlib') -def plot_fm(fm, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum=None, - freq_range=None, plt_log=False, add_legend=True, save_fig=False, file_name=None, - file_path=None, ax=None, data_kwargs=None, model_kwargs=None, aperiodic_kwargs=None, - peak_kwargs=None, **plot_kwargs): - """Plot the power spectrum and model fit results from a FOOOF object. +def plot_model(model, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum=None, + freq_range=None, plt_log=False, add_legend=True, ax=None, data_kwargs=None, + model_kwargs=None, aperiodic_kwargs=None, peak_kwargs=None, **plot_kwargs): + """Plot the power spectrum and model fit results from a model object. Parameters ---------- - fm : FOOOF + model : SpectralModel Object containing a power spectrum and (optionally) results from fitting. plot_peaks : None or {'shade', 'dot', 'outline', 'line'}, optional What kind of approach to take to plot peaks. If None, peaks are not specifically plotted. @@ -52,12 +51,6 @@ def plot_fm(fm, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum Whether to plot the frequency values in log10 spacing. add_legend : boolean, optional, default: False Whether to add a legend describing the plot components. - save_fig : bool, optional, default: False - Whether to save out a copy of the plot. - file_name : str, optional - Name to give the saved out file. - file_path : Path or str, optional - Path to directory to save to. If None, saves to current directory. ax : matplotlib.Axes, optional Figure axes upon which to plot. data_kwargs, model_kwargs, aperiodic_kwargs, peak_kwargs : None or dict, optional @@ -67,8 +60,7 @@ def plot_fm(fm, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum Notes ----- - Since FOOOF objects store power values in log spacing, - the y-axis (power) is plotted in log spacing by default. + The y-axis (power) is plotted in log spacing by default. """ ax = check_ax(ax, plot_kwargs.pop('figsize', PLT_FIGSIZES['spectral'])) @@ -76,26 +68,27 @@ def plot_fm(fm, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum # Check inputs for what to plot custom_spectrum = (np.any(freqs) and np.any(power_spectrum)) - # Log settings - note that power values in FOOOF objects are already logged + # Log settings - note that power values in model objects are already logged log_freqs = plt_log log_powers = False # Plot the data, if available - if fm.has_data or custom_spectrum: + if model.has_data or custom_spectrum: data_defaults = {'color' : PLT_COLORS['data'], 'linewidth' : 2.0, 'label' : 'Original Spectrum' if add_legend else None} data_kwargs = check_plot_kwargs(data_kwargs, data_defaults) - plot_spectra(freqs if custom_spectrum else fm.freqs, - power_spectrum if custom_spectrum else fm.power_spectrum, + plot_spectra(freqs if custom_spectrum else model.freqs, + power_spectrum if custom_spectrum else model.power_spectrum, log_freqs, log_powers if not custom_spectrum else True, freq_range, ax=ax, **data_kwargs) # Add the full model fit, and components (if requested) - if fm.has_model: + if model.has_model: model_defaults = {'color' : PLT_COLORS['model'], 'linewidth' : 3.0, 'alpha' : 0.5, 'label' : 'Full Model Fit' if add_legend else None} model_kwargs = check_plot_kwargs(model_kwargs, model_defaults) - plot_spectra(fm.freqs, fm.fooofed_spectrum_, log_freqs, log_powers, ax=ax, **model_kwargs) + plot_spectra(model.freqs, model.modeled_spectrum_, + log_freqs, log_powers, ax=ax, **model_kwargs) # Plot the aperiodic component of the model fit if plot_aperiodic: @@ -103,23 +96,24 @@ def plot_fm(fm, plot_peaks=None, plot_aperiodic=True, freqs=None, power_spectrum 'alpha' : 0.5, 'linestyle' : 'dashed', 'label' : 'Aperiodic Fit' if add_legend else None} aperiodic_kwargs = check_plot_kwargs(aperiodic_kwargs, aperiodic_defaults) - plot_spectra(fm.freqs, fm._ap_fit, log_freqs, log_powers, ax=ax, **aperiodic_kwargs) + plot_spectra(model.freqs, model._ap_fit, + log_freqs, log_powers, ax=ax, **aperiodic_kwargs) # Plot the periodic components of the model fit if plot_peaks: - _add_peaks(fm, plot_peaks, plt_log, ax, peak_kwargs) + _add_peaks(model, plot_peaks, plt_log, ax, peak_kwargs) # Apply default style to plot style_spectrum_plot(ax, log_freqs, True) -def _add_peaks(fm, approach, plt_log, ax, peak_kwargs): +def _add_peaks(model, approach, plt_log, ax, peak_kwargs): """Add peaks to a model plot. Parameters ---------- - fm : FOOOF - FOOOF object containing results from fitting. + model : SpectralModel + Model object containing results from fitting. approach : {'shade', 'dot', 'outline', 'outline', 'line'} What kind of approach to take to plot peaks. Can also be a combination of approaches, separated by '-' (for example 'shade-line'). @@ -151,19 +145,19 @@ def _add_peaks(fm, approach, plt_log, ax, peak_kwargs): plot_kwargs = peak_kwargs.get(cur_approach, peak_kwargs) # Pass through to the peak plotting function - ADD_PEAK_FUNCS[cur_approach](fm, plt_log, ax, **plot_kwargs) + ADD_PEAK_FUNCS[cur_approach](model, plt_log, ax, **plot_kwargs) except KeyError: raise ValueError("Plot peak type not understood.") -def _add_peaks_shade(fm, plt_log, ax, **plot_kwargs): +def _add_peaks_shade(model, plt_log, ax, **plot_kwargs): """Add a shading in of all peaks. Parameters ---------- - fm : FOOOF - FOOOF object containing results from fitting. + model : SpectralModel + Model object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes @@ -175,21 +169,21 @@ def _add_peaks_shade(fm, plt_log, ax, **plot_kwargs): defaults = {'color' : PLT_COLORS['periodic'], 'alpha' : 0.25} plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) - for peak in fm.gaussian_params_: + for peak in model.gaussian_params_: - peak_freqs = np.log10(fm.freqs) if plt_log else fm.freqs - peak_line = fm._ap_fit + gen_periodic(fm.freqs, peak) + peak_freqs = np.log10(model.freqs) if plt_log else model.freqs + peak_line = model._ap_fit + gen_periodic(model.freqs, peak) - ax.fill_between(peak_freqs, peak_line, fm._ap_fit, **plot_kwargs) + ax.fill_between(peak_freqs, peak_line, model._ap_fit, **plot_kwargs) -def _add_peaks_dot(fm, plt_log, ax, **plot_kwargs): +def _add_peaks_dot(model, plt_log, ax, **plot_kwargs): """Add a short line, from aperiodic to peak, with a dot at the top. Parameters ---------- - fm : FOOOF - FOOOF object containing results from fitting. + model : SpectralModel + Model object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes @@ -201,9 +195,9 @@ def _add_peaks_dot(fm, plt_log, ax, **plot_kwargs): defaults = {'color' : PLT_COLORS['periodic'], 'alpha' : 0.6, 'lw' : 2.5, 'ms' : 6} plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) - for peak in fm.peak_params_: + for peak in model.peak_params_: - ap_point = np.interp(peak[0], fm.freqs, fm._ap_fit) + ap_point = np.interp(peak[0], model.freqs, model._ap_fit) freq_point = np.log10(peak[0]) if plt_log else peak[0] # Add the line from the aperiodic fit up the tip of the peak @@ -213,13 +207,13 @@ def _add_peaks_dot(fm, plt_log, ax, **plot_kwargs): ax.plot(freq_point, ap_point + peak[1], marker='o', **plot_kwargs) -def _add_peaks_outline(fm, plt_log, ax, **plot_kwargs): +def _add_peaks_outline(model, plt_log, ax, **plot_kwargs): """Add an outline of each peak. Parameters ---------- - fm : FOOOF - FOOOF object containing results from fitting. + model : SpectralModel + Model object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes @@ -231,27 +225,27 @@ def _add_peaks_outline(fm, plt_log, ax, **plot_kwargs): defaults = {'color' : PLT_COLORS['periodic'], 'alpha' : 0.7, 'lw' : 1.5} plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) - for peak in fm.gaussian_params_: + for peak in model.gaussian_params_: # Define the frequency range around each peak to plot - peak bandwidth +/- 3 peak_range = [peak[0] - peak[2]*3, peak[0] + peak[2]*3] # Generate a peak reconstruction for each peak, and trim to desired range - peak_line = fm._ap_fit + gen_periodic(fm.freqs, peak) - peak_freqs, peak_line = trim_spectrum(fm.freqs, peak_line, peak_range) + peak_line = model._ap_fit + gen_periodic(model.freqs, peak) + peak_freqs, peak_line = trim_spectrum(model.freqs, peak_line, peak_range) # Plot the peak outline peak_freqs = np.log10(peak_freqs) if plt_log else peak_freqs ax.plot(peak_freqs, peak_line, **plot_kwargs) -def _add_peaks_line(fm, plt_log, ax, **plot_kwargs): +def _add_peaks_line(model, plt_log, ax, **plot_kwargs): """Add a long line, from the top of the plot, down through the peak, with an arrow at the top. Parameters ---------- - fm : FOOOF - FOOOF object containing results from fitting. + model : SpectralModel + Model object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes @@ -265,20 +259,20 @@ def _add_peaks_line(fm, plt_log, ax, **plot_kwargs): ylims = ax.get_ylim() - for peak in fm.peak_params_: + for peak in model.peak_params_: freq_point = np.log10(peak[0]) if plt_log else peak[0] ax.plot([freq_point, freq_point], ylims, '-', **plot_kwargs) ax.plot(freq_point, ylims[1], 'v', **plot_kwargs) -def _add_peaks_width(fm, plt_log, ax, **plot_kwargs): +def _add_peaks_width(model, plt_log, ax, **plot_kwargs): """Add a line across the width of peaks. Parameters ---------- - fm : FOOOF - FOOOF object containing results from fitting. + model : SpectralModel + Model object containing results from fitting. plt_log : boolean Whether to plot the frequency values in log10 spacing. ax : matplotlib.Axes @@ -295,9 +289,9 @@ def _add_peaks_width(fm, plt_log, ax, **plot_kwargs): defaults = {'color' : PLT_COLORS['periodic'], 'alpha' : 0.6, 'lw' : 2.5, 'ms' : 6} plot_kwargs = check_plot_kwargs(plot_kwargs, defaults) - for peak in fm.gaussian_params_: + for peak in model.gaussian_params_: - peak_top = fm.power_spectrum[nearest_ind(fm.freqs, peak[0])] + peak_top = model.power_spectrum[nearest_ind(model.freqs, peak[0])] bw_freqs = [peak[0] - 0.5 * compute_fwhm(peak[2]), peak[0] + 0.5 * compute_fwhm(peak[2])] diff --git a/fooof/plts/periodic.py b/specparam/plts/periodic.py similarity index 93% rename from fooof/plts/periodic.py rename to specparam/plts/periodic.py index e6e923dd..293ddf00 100644 --- a/fooof/plts/periodic.py +++ b/specparam/plts/periodic.py @@ -4,12 +4,12 @@ import numpy as np -from fooof.sim import gen_freqs -from fooof.core.funcs import gaussian_function -from fooof.core.modutils import safe_import, check_dependency -from fooof.plts.settings import PLT_FIGSIZES -from fooof.plts.style import style_param_plot, style_plot -from fooof.plts.utils import check_ax, recursive_plot, savefig, check_plot_kwargs +from specparam.sim import gen_freqs +from specparam.core.funcs import gaussian_function +from specparam.core.modutils import safe_import, check_dependency +from specparam.plts.settings import PLT_FIGSIZES +from specparam.plts.style import style_param_plot, style_plot +from specparam.plts.utils import check_ax, recursive_plot, savefig, check_plot_kwargs plt = safe_import('.pyplot', 'matplotlib') diff --git a/fooof/plts/settings.py b/specparam/plts/settings.py similarity index 100% rename from fooof/plts/settings.py rename to specparam/plts/settings.py diff --git a/fooof/plts/spectra.py b/specparam/plts/spectra.py similarity index 95% rename from fooof/plts/spectra.py rename to specparam/plts/spectra.py index 852b198b..c14634a1 100644 --- a/fooof/plts/spectra.py +++ b/specparam/plts/spectra.py @@ -11,10 +11,10 @@ import numpy as np from scipy.stats import sem -from fooof.core.modutils import safe_import, check_dependency -from fooof.plts.settings import PLT_FIGSIZES -from fooof.plts.style import style_spectrum_plot, style_plot -from fooof.plts.utils import check_ax, add_shades, savefig, check_plot_kwargs +from specparam.core.modutils import safe_import, check_dependency +from specparam.plts.settings import PLT_FIGSIZES +from specparam.plts.style import style_spectrum_plot, style_plot +from specparam.plts.utils import check_ax, add_shades, savefig, check_plot_kwargs plt = safe_import('.pyplot', 'matplotlib') @@ -66,7 +66,8 @@ def plot_spectra(freqs, power_spectra, log_freqs=False, log_powers=False, freq_r plt_freqs = repeat(freqs) if isinstance(freqs, np.ndarray) and freqs.ndim == 1 else freqs # Set labels - labels = plot_kwargs.pop('label') if 'label' in plot_kwargs.keys() and labels is None else labels + labels = plot_kwargs.pop('label') \ + if 'label' in plot_kwargs.keys() and labels is None else labels labels = repeat(labels) if not isinstance(labels, list) else cycle(labels) colors = repeat(colors) if not isinstance(colors, list) else cycle(colors) diff --git a/fooof/plts/style.py b/specparam/plts/style.py similarity index 96% rename from fooof/plts/style.py rename to specparam/plts/style.py index f4063c02..05bff602 100644 --- a/fooof/plts/style.py +++ b/specparam/plts/style.py @@ -5,9 +5,9 @@ import matplotlib.pyplot as plt -from fooof.plts.settings import (AXIS_STYLE_ARGS, LINE_STYLE_ARGS, COLLECTION_STYLE_ARGS, - CUSTOM_STYLE_ARGS, STYLE_ARGS, TICK_LABELSIZE, TITLE_FONTSIZE, - LABEL_SIZE, LEGEND_SIZE, LEGEND_LOC) +from specparam.plts.settings import (AXIS_STYLE_ARGS, LINE_STYLE_ARGS, COLLECTION_STYLE_ARGS, + CUSTOM_STYLE_ARGS, STYLE_ARGS, TICK_LABELSIZE, TITLE_FONTSIZE, + LABEL_SIZE, LEGEND_SIZE, LEGEND_LOC) ################################################################################################### ################################################################################################### @@ -250,7 +250,7 @@ def style_plot(func, *args, **kwargs): To see the full set of style arguments that are supported, run the following code: - >>> from fooof.plts.style import check_style_options + >>> from specparam.plts.style import check_style_options >>> check_style_options() Valid style arguments: Axis title, xlabel, ylabel, xlim, ylim, xticks, yticks, xticklabels, yticklabels diff --git a/fooof/plts/templates.py b/specparam/plts/templates.py similarity index 92% rename from fooof/plts/templates.py rename to specparam/plts/templates.py index b637e595..f520f67e 100644 --- a/fooof/plts/templates.py +++ b/specparam/plts/templates.py @@ -1,16 +1,17 @@ -"""Plot templates for the FOOOF module. +"""Plot templates for the module. Notes ----- -These are template plot structures for FOOOF plots and/or reports. +These are template plot structures for plots and/or reports. They are not expected to be used directly by the user. """ import numpy as np -from fooof.core.modutils import safe_import, check_dependency -from fooof.plts.utils import check_ax, set_alpha -from fooof.plts.settings import TITLE_FONTSIZE, LABEL_SIZE, TICK_LABELSIZE + +from specparam.core.modutils import safe_import, check_dependency +from specparam.plts.utils import check_ax, set_alpha +from specparam.plts.settings import TITLE_FONTSIZE, LABEL_SIZE, TICK_LABELSIZE plt = safe_import('.pyplot', 'matplotlib') diff --git a/fooof/plts/utils.py b/specparam/plts/utils.py similarity index 96% rename from fooof/plts/utils.py rename to specparam/plts/utils.py index 21713605..f9156282 100644 --- a/fooof/plts/utils.py +++ b/specparam/plts/utils.py @@ -12,10 +12,10 @@ import numpy as np -from fooof.core.io import fname, fpath -from fooof.core.modutils import safe_import -from fooof.core.utils import resolve_aliases -from fooof.plts.settings import PLT_ALPHA_LEVELS, PLT_ALIASES +from specparam.core.io import fname, fpath +from specparam.core.modutils import safe_import +from specparam.core.utils import resolve_aliases +from specparam.plts.settings import PLT_ALPHA_LEVELS, PLT_ALIASES plt = safe_import('.pyplot', 'matplotlib') diff --git a/specparam/sim/__init__.py b/specparam/sim/__init__.py new file mode 100644 index 00000000..f0416511 --- /dev/null +++ b/specparam/sim/__init__.py @@ -0,0 +1,7 @@ +""""Simulation sub-module.""" + +# Link the Sim Params object into `sim`, so it can be imported from here +from specparam.data import SimParams + +from .sim import sim_power_spectrum, sim_group_power_spectra +from .gen import gen_freqs diff --git a/specparam/sim/gen.py b/specparam/sim/gen.py new file mode 100644 index 00000000..f63b2421 --- /dev/null +++ b/specparam/sim/gen.py @@ -0,0 +1,234 @@ +"""Functions for generating model components.""" + +import numpy as np + +from specparam.core.funcs import get_ap_func, get_pe_func, infer_ap_func + +from specparam.sim.transform import rotate_spectrum + +################################################################################################### +################################################################################################### + +def gen_freqs(freq_range, freq_res): + """Generate a frequency vector. + + Parameters + ---------- + freq_range : list of [float, float] + Frequency range to create frequencies across, as [f_low, f_high], inclusive. + freq_res : float + Frequency resolution for the frequency vector. + + Returns + ------- + freqs : 1d array + Frequency values, in linear spacing. + + Examples + -------- + Generate a vector of frequency values from 1 to 50: + + >>> freqs = gen_freqs([1, 50], freq_res=0.5) + """ + + # The end value has something added to it, to make sure the last value is included + # It adds a fraction to not accidentally include points beyond range + # due to rounding / or uneven division of the freq_res into range to simulate + freqs = np.arange(freq_range[0], freq_range[1] + (0.5 * freq_res), freq_res) + + return freqs + + +def gen_aperiodic(freqs, aperiodic_params, aperiodic_mode=None): + """Generate aperiodic values. + + Parameters + ---------- + freqs : 1d array + Frequency vector to create aperiodic component for. + aperiodic_params : list of float + Parameters that define the aperiodic component. + aperiodic_mode : {'fixed', 'knee'}, optional + Which kind of aperiodic component to generate. + If not provided, is inferred from the parameters. + + Returns + ------- + ap_vals : 1d array + Aperiodic values, in log10 spacing. + """ + + if not aperiodic_mode: + aperiodic_mode = infer_ap_func(aperiodic_params) + + ap_func = get_ap_func(aperiodic_mode) + + ap_vals = ap_func(freqs, *aperiodic_params) + + return ap_vals + + +def gen_periodic(freqs, periodic_params, periodic_mode='gaussian'): + """Generate periodic values. + + Parameters + ---------- + freqs : 1d array + Frequency vector to create peak values for. + periodic_params : list of float + Parameters to create the periodic component. + periodic_mode : {'gaussian'}, optional + Which kind of periodic component to generate. + + Returns + ------- + peak_vals : 1d array + Peak values, in log10 spacing. + """ + + pe_func = get_pe_func(periodic_mode) + + pe_vals = pe_func(freqs, *periodic_params) + + return pe_vals + + +def gen_noise(freqs, nlv): + """Generate noise values for a simulated power spectrum. + + Parameters + ---------- + freqs : 1d array + Frequency vector to create noise values for. + nlv : float + Noise level to generate. + + Returns + ------- + noise_vals : 1d vector + Noise values. + + Notes + ----- + This approach generates noise as randomly distributed white noise. + The 'level' of noise is controlled as the scale of the normal distribution. + """ + + noise_vals = np.random.normal(0, nlv, len(freqs)) + + return noise_vals + + +def gen_power_vals(freqs, aperiodic_params, periodic_params, nlv): + """Generate power values for a simulated power spectrum. + + Parameters + ---------- + freqs : 1d array + Frequency vector to create power values for. + aperiodic_params : list of float + Parameters to create the aperiodic component of the power spectrum. + periodic_params : list of float + Parameters to create the periodic component of the power spectrum. + nlv : float + Noise level to add to generated power spectrum. + + Returns + ------- + powers : 1d vector + Power values, in linear spacing. + + Notes + ----- + This function should be used when simulating power spectra, as it: + + - Takes in input parameter definitions as lists, as used for simulating power spectra. + - Returns the power spectrum in linear spacing, as is used for simulating power spectra. + """ + + ap_vals = gen_aperiodic(freqs, aperiodic_params) + pe_vals = gen_periodic(freqs, periodic_params) + noise = gen_noise(freqs, nlv) + + powers = np.power(10, ap_vals + pe_vals + noise) + + return powers + + +def gen_rotated_power_vals(freqs, aperiodic_params, periodic_params, nlv, f_rotation): + """Generate power values for a simulated power spectrum, rotated around a given frequency. + + Parameters + ---------- + freqs : 1d array + Frequency vector to create power values for. + aperiodic_params : list of float + Parameters to create the aperiodic component of the power spectrum. + periodic_params : list of float + Parameters to create the periodic component of the power spectrum. + nlv : float + Noise level to add to generated power spectrum. + f_rotation : float + Frequency value, in Hz, about which rotation is applied, at which power is unchanged. + + Returns + ------- + powers : 1d vector + Power values, in linear spacing. + + Raises + ------ + ValueError + If a rotation is requested on a power spectrum with a knee, as this is not supported. + """ + + if len(aperiodic_params) == 3: + raise ValueError('Cannot rotate power spectra generated with a knee.') + + powers = gen_power_vals(freqs, [0, 0], periodic_params, nlv) + powers = rotate_spectrum(freqs, powers, aperiodic_params[1], f_rotation) + + return powers + + +def gen_model(freqs, aperiodic_params, periodic_params, return_components=False): + """Generate a power spectrum model for a given parameter definition. + + Parameters + ---------- + freqs : 1d array + Frequency vector to create the model for. + aperiodic_params : 1d array + Parameters to create the aperiodic component of the modeled power spectrum. + periodic_params : 2d array + Parameters to create the periodic component of the modeled power spectrum. + return_components : bool, optional, default: False + Whether to also return the components of the model. + + Returns + ------- + full_model : 1d array + The full power spectrum model, in log10 spacing. + pe_fit : 1d array + The periodic component of the model, containing the peaks. + Only returned if `return_components` is True. + ap_fit : 1d array + The aperiodic component of the model. + Only returned if `return_components` is True. + + Notes + ----- + This function should be used when computing model reconstructions, as it: + + - Takes in input parameter definitions as arrays, as used in model objects. + - Returns the power spectrum in log10 spacing, as is used in model models. + """ + + ap_fit = gen_aperiodic(freqs, aperiodic_params) + pe_fit = gen_periodic(freqs, np.ndarray.flatten(periodic_params)) + full_model = pe_fit + ap_fit + + if return_components: + return full_model, pe_fit, ap_fit + else: + return full_model diff --git a/fooof/sim/params.py b/specparam/sim/params.py similarity index 97% rename from fooof/sim/params.py rename to specparam/sim/params.py index 8c492122..5fcfde1f 100644 --- a/fooof/sim/params.py +++ b/specparam/sim/params.py @@ -2,12 +2,12 @@ import numpy as np -from fooof.core.utils import group_three, check_flat -from fooof.core.info import get_indices -from fooof.core.funcs import infer_ap_func -from fooof.core.errors import InconsistentDataError +from specparam.core.utils import group_three, check_flat +from specparam.core.info import get_indices +from specparam.core.funcs import infer_ap_func +from specparam.core.errors import InconsistentDataError -from fooof.data import SimParams +from specparam.data import SimParams ################################################################################################### ################################################################################################### diff --git a/fooof/sim/gen.py b/specparam/sim/sim.py similarity index 55% rename from fooof/sim/gen.py rename to specparam/sim/sim.py index 33c87311..29ad464a 100644 --- a/fooof/sim/gen.py +++ b/specparam/sim/sim.py @@ -1,49 +1,18 @@ -"""Functions for generating model components and simulated power spectra.""" +"""Functions for simulating power spectra.""" import numpy as np -from fooof.core.utils import check_iter, check_flat -from fooof.core.funcs import get_ap_func, get_pe_func, infer_ap_func - -from fooof.sim.params import collect_sim_params -from fooof.sim.transform import rotate_spectrum, compute_rotation_offset +from specparam.core.utils import check_iter, check_flat +from specparam.sim.params import collect_sim_params +from specparam.sim.gen import gen_freqs, gen_power_vals, gen_rotated_power_vals +from specparam.sim.transform import compute_rotation_offset ################################################################################################### ################################################################################################### -def gen_freqs(freq_range, freq_res): - """Generate a frequency vector. - - Parameters - ---------- - freq_range : list of [float, float] - Frequency range to create frequencies across, as [f_low, f_high], inclusive. - freq_res : float - Frequency resolution of desired frequency vector. - - Returns - ------- - freqs : 1d array - Frequency values, in linear spacing. - - Examples - -------- - Generate a vector of frequency values from 1 to 50: - - >>> freqs = gen_freqs([1, 50], freq_res=0.5) - """ - - # The end value has something added to it, to make sure the last value is included - # It adds a fraction to not accidentally include points beyond range - # due to rounding / or uneven division of the freq_res into range to simulate - freqs = np.arange(freq_range[0], freq_range[1] + (0.5 * freq_res), freq_res) - - return freqs - - -def gen_power_spectrum(freq_range, aperiodic_params, periodic_params, nlv=0.005, +def sim_power_spectrum(freq_range, aperiodic_params, periodic_params, nlv=0.005, freq_res=0.5, f_rotation=None, return_params=False): - """Generate a simulated power spectrum. + """Simulate a power spectrum. Parameters ---------- @@ -120,15 +89,15 @@ def gen_power_spectrum(freq_range, aperiodic_params, periodic_params, nlv=0.005, -------- Generate a power spectrum with a single peak, at 10 Hz: - >>> freqs, powers = gen_power_spectrum([1, 50], [0, 2], [10, 0.5, 1]) + >>> freqs, powers = sim_power_spectrum([1, 50], [0, 2], [10, 0.5, 1]) Generate a power spectrum with alpha and beta peaks: - >>> freqs, powers = gen_power_spectrum([1, 50], [0, 2], [[10, 0.5, 1], [20, 0.5, 1]]) + >>> freqs, powers = sim_power_spectrum([1, 50], [0, 2], [[10, 0.5, 1], [20, 0.5, 1]]) Generate a power spectrum, that was rotated around a particular frequency point: - >>> freqs, powers = gen_power_spectrum([1, 50], [None, 2], [10, 0.5, 1], f_rotation=15) + >>> freqs, powers = sim_power_spectrum([1, 50], [None, 2], [10, 0.5, 1], f_rotation=15) """ freqs = gen_freqs(freq_range, freq_res) @@ -153,9 +122,9 @@ def gen_power_spectrum(freq_range, aperiodic_params, periodic_params, nlv=0.005, return freqs, powers -def gen_group_power_spectra(n_spectra, freq_range, aperiodic_params, periodic_params, nlvs=0.005, +def sim_group_power_spectra(n_spectra, freq_range, aperiodic_params, periodic_params, nlvs=0.005, freq_res=0.5, f_rotation=None, return_params=False): - """Generate a group of simulated power spectra. + """Simulate multiple power spectra. Parameters ---------- @@ -237,27 +206,27 @@ def gen_group_power_spectra(n_spectra, freq_range, aperiodic_params, periodic_pa -------- Generate 2 power spectra using the same parameters: - >>> freqs, powers = gen_group_power_spectra(2, [1, 50], [0, 2], [10, 0.5, 1]) + >>> freqs, powers = sim_group_power_spectra(2, [1, 50], [0, 2], [10, 0.5, 1]) Generate 10 power spectra, randomly sampling possible parameters: - >>> from fooof.sim.params import param_sampler + >>> from specparam.sim.params import param_sampler >>> ap_opts = param_sampler([[0, 1.0], [0, 1.5], [0, 2]]) >>> pe_opts = param_sampler([[], [10, 0.5, 1], [10, 0.5, 1, 20, 0.25, 1]]) - >>> freqs, powers = gen_group_power_spectra(10, [1, 50], ap_opts, pe_opts) + >>> freqs, powers = sim_group_power_spectra(10, [1, 50], ap_opts, pe_opts) Generate 5 power spectra, rotated around 20 Hz: >>> ap_params = [[None, 1], [None, 1.25], [None, 1.5], [None, 1.75], [None, 2]] >>> pe_params = [10, 0.5, 1] - >>> freqs, powers = gen_group_power_spectra(5, [1, 50], ap_params, pe_params, f_rotation=20) + >>> freqs, powers = sim_group_power_spectra(5, [1, 50], ap_params, pe_params, f_rotation=20) Generate power spectra stepping across exponent values, and return parameter values: - >>> from fooof.sim.params import Stepper, param_iter + >>> from specparam.sim.params import Stepper, param_iter >>> ap_params = param_iter([0, Stepper(1, 2, 0.25)]) >>> pe_params = [10, 0.5, 1] - >>> freqs, powers, sps = gen_group_power_spectra(5, [1, 50], ap_params, pe_params, + >>> freqs, powers, sps = sim_group_power_spectra(5, [1, 50], ap_params, pe_params, ... return_params=True) """ @@ -288,198 +257,3 @@ def gen_group_power_spectra(n_spectra, freq_range, aperiodic_params, periodic_pa return freqs, powers, sim_params else: return freqs, powers - - -def gen_aperiodic(freqs, aperiodic_params, aperiodic_mode=None): - """Generate aperiodic values. - - Parameters - ---------- - freqs : 1d array - Frequency vector to create aperiodic component for. - aperiodic_params : list of float - Parameters that define the aperiodic component. - aperiodic_mode : {'fixed', 'knee'}, optional - Which kind of aperiodic component to generate. - If not provided, is inferred from the parameters. - - Returns - ------- - ap_vals : 1d array - Aperiodic values, in log10 spacing. - """ - - if not aperiodic_mode: - aperiodic_mode = infer_ap_func(aperiodic_params) - - ap_func = get_ap_func(aperiodic_mode) - - ap_vals = ap_func(freqs, *aperiodic_params) - - return ap_vals - - -def gen_periodic(freqs, periodic_params, periodic_mode='gaussian'): - """Generate periodic values. - - Parameters - ---------- - freqs : 1d array - Frequency vector to create peak values for. - periodic_params : list of float - Parameters to create the periodic component. - periodic_mode : {'gaussian'}, optional - Which kind of periodic component to generate. - - Returns - ------- - peak_vals : 1d array - Peak values, in log10 spacing. - """ - - pe_func = get_pe_func(periodic_mode) - - pe_vals = pe_func(freqs, *periodic_params) - - return pe_vals - - -def gen_noise(freqs, nlv): - """Generate noise values for a simulated power spectrum. - - Parameters - ---------- - freqs : 1d array - Frequency vector to create noise values for. - nlv : float - Noise level to generate. - - Returns - ------- - noise_vals : 1d vector - Noise values. - - Notes - ----- - This approach generates noise as randomly distributed white noise. - The 'level' of noise is controlled as the scale of the normal distribution. - """ - - noise_vals = np.random.normal(0, nlv, len(freqs)) - - return noise_vals - - -def gen_power_vals(freqs, aperiodic_params, periodic_params, nlv): - """Generate power values for a simulated power spectrum. - - Parameters - ---------- - freqs : 1d array - Frequency vector to create power values for. - aperiodic_params : list of float - Parameters to create the aperiodic component of the power spectrum. - periodic_params : list of float - Parameters to create the periodic component of the power spectrum. - nlv : float - Noise level to add to generated power spectrum. - - Returns - ------- - powers : 1d vector - Power values, in linear spacing. - - Notes - ----- - This function should be used when simulating power spectra, as it: - - - Takes in input parameter definitions as lists, as used for simulating power spectra. - - Returns the power spectrum in linear spacing, as is used for simulating power spectra. - """ - - ap_vals = gen_aperiodic(freqs, aperiodic_params) - pe_vals = gen_periodic(freqs, periodic_params) - noise = gen_noise(freqs, nlv) - - powers = np.power(10, ap_vals + pe_vals + noise) - - return powers - - -def gen_rotated_power_vals(freqs, aperiodic_params, periodic_params, nlv, f_rotation): - """Generate power values for a simulated power spectrum, rotated around a given frequency. - - Parameters - ---------- - freqs : 1d array - Frequency vector to create power values for. - aperiodic_params : list of float - Parameters to create the aperiodic component of the power spectrum. - periodic_params : list of float - Parameters to create the periodic component of the power spectrum. - nlv : float - Noise level to add to generated power spectrum. - f_rotation : float - Frequency value, in Hz, about which rotation is applied, at which power is unchanged. - - Returns - ------- - powers : 1d vector - Power values, in linear spacing. - - Raises - ------ - ValueError - If a rotation is requested on a power spectrum with a knee, as this is not supported. - """ - - if len(aperiodic_params) == 3: - raise ValueError('Cannot rotate power spectra generated with a knee.') - - powers = gen_power_vals(freqs, [0, 0], periodic_params, nlv) - powers = rotate_spectrum(freqs, powers, aperiodic_params[1], f_rotation) - - return powers - - -def gen_model(freqs, aperiodic_params, periodic_params, return_components=False): - """Generate a power spectrum model for a given parameter definition. - - Parameters - ---------- - freqs : 1d array - Frequency vector to create the model for. - aperiodic_params : 1d array - Parameters to create the aperiodic component of the modeled power spectrum. - periodic_params : 2d array - Parameters to create the periodic component of the modeled power spectrum. - return_components : bool, optional, default: False - Whether to also return the components of the model. - - Returns - ------- - full_model : 1d array - The full power spectrum model, in log10 spacing. - pe_fit : 1d array - The periodic component of the model, containing the peaks. - Only returned if `return_components` is True. - ap_fit : 1d array - The aperiodic component of the model. - Only returned if `return_components` is True. - - Notes - ----- - This function should be used when computing model reconstructions, as it: - - - Takes in input parameter definitions as arrays, as used in FOOOF objects. - - Returns the power spectrum in log10 spacing, as is used in FOOOF models. - """ - - ap_fit = gen_aperiodic(freqs, aperiodic_params) - pe_fit = gen_periodic(freqs, np.ndarray.flatten(periodic_params)) - full_model = pe_fit + ap_fit - - if return_components: - return full_model, pe_fit, ap_fit - else: - return full_model diff --git a/fooof/sim/transform.py b/specparam/sim/transform.py similarity index 94% rename from fooof/sim/transform.py rename to specparam/sim/transform.py index 5623b749..04395db2 100644 --- a/fooof/sim/transform.py +++ b/specparam/sim/transform.py @@ -2,7 +2,7 @@ import numpy as np -from fooof.sim.params import update_sim_ap_params +from specparam.sim.params import update_sim_ap_params ################################################################################################### ################################################################################################### @@ -61,8 +61,8 @@ def rotate_spectrum(freqs, power_spectrum, delta_exponent, f_rotation): -------- Rotate a simulated spectrum, changing the exponent around a rotation point of 25 Hz: - >>> from fooof.sim.gen import gen_power_spectrum - >>> freqs, powers = gen_power_spectrum([1, 50], [1, 1], [10, 0.5, 1]) + >>> from specparam.sim import sim_power_spectrum + >>> freqs, powers = sim_power_spectrum([1, 50], [1, 1], [10, 0.5, 1]) >>> rotated_powers = rotate_spectrum(freqs, powers, 0.5, 25) """ @@ -99,8 +99,8 @@ def translate_spectrum(power_spectrum, delta_offset): -------- Translate a simulated spectrum, moving the offset up: - >>> from fooof.sim.gen import gen_power_spectrum - >>> freqs, powers = gen_power_spectrum([1, 50], [1, 1], [10, 0.5, 1]) + >>> from specparam.sim import sim_power_spectrum + >>> freqs, powers = sim_power_spectrum([1, 50], [1, 1], [10, 0.5, 1]) >>> translated_powers = translate_spectrum(powers, 0.5) """ @@ -147,8 +147,8 @@ def rotate_sim_spectrum(freqs, power_spectrum, delta_exponent, f_rotation, sim_p -------- Rotate a simulated spectrum, changing the exponent around a rotation point of 25 Hz: - >>> from fooof.sim.gen import gen_power_spectrum - >>> freqs, powers, sp = gen_power_spectrum([1, 50], [1, 1], [10, 0.5, 1], return_params=True) + >>> from specparam.sim import sim_power_spectrum + >>> freqs, powers, sp = sim_power_spectrum([1, 50], [1, 1], [10, 0.5, 1], return_params=True) >>> rotated_powers, new_sp = rotate_sim_spectrum(freqs, powers, 0.5, 25, sp) """ @@ -186,8 +186,8 @@ def translate_sim_spectrum(power_spectrum, delta_offset, sim_params): -------- Translate a simulated spectrum, moving the offset up: - >>> from fooof.sim.gen import gen_power_spectrum - >>> freqs, powers, sp = gen_power_spectrum([1, 50], [1, 1], [10, 0.5, 1], return_params=True) + >>> from specparam.sim import sim_power_spectrum + >>> freqs, powers, sp = sim_power_spectrum([1, 50], [1, 1], [10, 0.5, 1], return_params=True) >>> translated_powers, new_sp = translate_sim_spectrum(powers, 0.5, sp) """ diff --git a/fooof/sim/utils.py b/specparam/sim/utils.py similarity index 89% rename from fooof/sim/utils.py rename to specparam/sim/utils.py index ea59f38c..fb1ede94 100644 --- a/fooof/sim/utils.py +++ b/specparam/sim/utils.py @@ -2,6 +2,8 @@ import numpy as np +from specparam.sim.gen import gen_freqs as create_freqs + ################################################################################################### ################################################################################################### diff --git a/specparam/tests/__init__.py b/specparam/tests/__init__.py new file mode 100644 index 00000000..0e5fc442 --- /dev/null +++ b/specparam/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for the spectral parameterization module.""" diff --git a/fooof/tests/analysis/__init__.py b/specparam/tests/analysis/__init__.py similarity index 100% rename from fooof/tests/analysis/__init__.py rename to specparam/tests/analysis/__init__.py diff --git a/specparam/tests/analysis/test_error.py b/specparam/tests/analysis/test_error.py new file mode 100644 index 00000000..42f8eb5b --- /dev/null +++ b/specparam/tests/analysis/test_error.py @@ -0,0 +1,34 @@ +"""Test functions for specparam.analysis.error.""" + +from specparam.analysis.error import * + +################################################################################################### +################################################################################################### + +def test_compute_pointwise_error(tfm): + + errs = compute_pointwise_error(tfm, False, True) + assert np.all(errs) + +def test_compute_pointwise_error_plt(tfm, skip_if_no_mpl): + """Run a separate test to run with plot pass-through.""" + + compute_pointwise_error(tfm, True, False) + +def test_compute_pointwise_error_group(tfg): + + errs = compute_pointwise_error_group(tfg, False, True) + assert np.all(errs) + +def test_compute_pointwise_error_group_plt(tfg, skip_if_no_mpl): + """Run a separate test to run with plot pass-through.""" + + compute_pointwise_error_group(tfg, True, False) + +def test_compute_pointwise_error_arr(): + + d1 = np.ones(5) * 2 + d2 = np.ones(5) + + errs = compute_pointwise_error_arr(d1, d2) + assert np.array_equal(errs, np.array([1, 1, 1, 1, 1])) diff --git a/fooof/tests/analysis/test_periodic.py b/specparam/tests/analysis/test_periodic.py similarity index 66% rename from fooof/tests/analysis/test_periodic.py rename to specparam/tests/analysis/test_periodic.py index db386c3f..549017c1 100644 --- a/fooof/tests/analysis/test_periodic.py +++ b/specparam/tests/analysis/test_periodic.py @@ -1,29 +1,29 @@ -"""Test functions for fooof.analysis.periodic.""" +"""Test functions for specparam.analysis.periodic.""" import numpy as np -from fooof.analysis.periodic import * +from specparam.analysis.periodic import * ################################################################################################### ################################################################################################### -def test_get_band_peak_fm(tfm): +def test_get_band_peak(tfm): - assert np.all(get_band_peak_fm(tfm, (8, 12))) + assert np.all(get_band_peak(tfm, (8, 12))) -def test_get_band_peak_fg(tfg): +def test_get_band_peak_group(tfg): - assert np.all(get_band_peak_fg(tfg, (8, 12))) + assert np.all(get_band_peak_group(tfg, (8, 12))) def test_get_band_peak_group(): data = np.array([[10, 1, 1.8, 0], [13, 1, 2, 2], [14, 2, 4, 2]]) - out1 = get_band_peak_group(data, [8, 12], 3) + out1 = get_band_peak_group_arr(data, [8, 12], 3) assert out1.shape == (3, 3) assert np.array_equal(out1[0, :], [10, 1, 1.8]) - out2 = get_band_peak_group(data, [12, 16], 3) + out2 = get_band_peak_group_arr(data, [12, 16], 3) assert out2.shape == (3, 3) assert np.array_equal(out2[2, :], [14, 2, 4]) @@ -32,21 +32,21 @@ def test_get_band_peak(): data = np.array([[10, 1, 1.8], [14, 2, 4]]) # Test single result - assert np.array_equal(get_band_peak(data, [10, 12]), [10, 1, 1.8]) + assert np.array_equal(get_band_peak_arr(data, [10, 12]), [10, 1, 1.8]) # Test no results - returns nan - assert np.all(np.isnan(get_band_peak(data, [4, 8]))) + assert np.all(np.isnan(get_band_peak_arr(data, [4, 8]))) # Test multiple results - return all - assert np.array_equal(get_band_peak(data, [10, 15], select_highest=False), + assert np.array_equal(get_band_peak_arr(data, [10, 15], select_highest=False), np.array([[10, 1, 1.8], [14, 2, 4]])) # Test multiple results - return one - assert np.array_equal(get_band_peak(data, [10, 15], select_highest=True), + assert np.array_equal(get_band_peak_arr(data, [10, 15], select_highest=True), np.array([14, 2, 4])) # Test applying a threshold - assert np.array_equal(get_band_peak(data, [10, 15], threshold=1.5, select_highest=False), + assert np.array_equal(get_band_peak_arr(data, [10, 15], threshold=1.5, select_highest=False), np.array([14, 2, 4])) def test_get_highest_peak(): @@ -66,7 +66,7 @@ def test_threshold_peaks(): assert np.array_equal(threshold_peaks(data, 2, param='BW'), np.array([[14, 2, 4], [12, 3, 2.5]])) - # Check it works with an [n_peaks, 4] array, as from FOOOFGroup + # Check it works with an [n_peaks, 4] array, as from SpectralGroupModel data = np.array([[10, 1, 1.8, 0], [13, 1, 2, 2], [14, 2, 4, 2]]) assert np.array_equal(threshold_peaks(data, 1.5), np.array([[14, 2, 4, 2]])) @@ -74,10 +74,10 @@ def test_empty_inputs(): data = np.empty(shape=[0, 3]) - assert np.all(get_band_peak(data, [8, 12])) + assert np.all(get_band_peak_arr(data, [8, 12])) assert np.all(get_highest_peak(data)) assert np.all(threshold_peaks(data, 1)) data = np.empty(shape=[0, 4]) - assert np.all(get_band_peak_group(data, [8, 12], 0)) + assert np.all(get_band_peak_group_arr(data, [8, 12], 0)) diff --git a/fooof/tests/bands/__init__.py b/specparam/tests/bands/__init__.py similarity index 100% rename from fooof/tests/bands/__init__.py rename to specparam/tests/bands/__init__.py diff --git a/fooof/tests/bands/test_bands.py b/specparam/tests/bands/test_bands.py similarity index 92% rename from fooof/tests/bands/test_bands.py rename to specparam/tests/bands/test_bands.py index 0b308b0a..1f22b27c 100644 --- a/fooof/tests/bands/test_bands.py +++ b/specparam/tests/bands/test_bands.py @@ -1,8 +1,8 @@ -"""Test functions for fooof.data.bands.""" +"""Test functions for specparam.data.bands.""" from pytest import raises -from fooof.bands.bands import * +from specparam.bands.bands import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/conftest.py b/specparam/tests/conftest.py similarity index 84% rename from fooof/tests/conftest.py rename to specparam/tests/conftest.py index bf0c47d5..a2c4bf7b 100644 --- a/fooof/tests/conftest.py +++ b/specparam/tests/conftest.py @@ -6,11 +6,10 @@ import numpy as np -from fooof.core.modutils import safe_import - -from fooof.tests.tutils import get_tfm, get_tfg, get_tbands, get_tresults, get_tdocstring -from fooof.tests.settings import (BASE_TEST_FILE_PATH, TEST_DATA_PATH, - TEST_REPORTS_PATH, TEST_PLOTS_PATH) +from specparam.core.modutils import safe_import +from specparam.tests.tutils import get_tfm, get_tfg, get_tbands, get_tresults, get_tdocstring +from specparam.tests.settings import (BASE_TEST_FILE_PATH, TEST_DATA_PATH, + TEST_REPORTS_PATH, TEST_PLOTS_PATH) plt = safe_import('.pyplot', 'matplotlib') diff --git a/fooof/tests/core/__init__.py b/specparam/tests/core/__init__.py similarity index 100% rename from fooof/tests/core/__init__.py rename to specparam/tests/core/__init__.py diff --git a/fooof/tests/core/test_funcs.py b/specparam/tests/core/test_funcs.py similarity index 95% rename from fooof/tests/core/test_funcs.py rename to specparam/tests/core/test_funcs.py index 4a64503b..5332676b 100644 --- a/fooof/tests/core/test_funcs.py +++ b/specparam/tests/core/test_funcs.py @@ -1,13 +1,13 @@ -"""Tests for fooof.core.funcs.""" +"""Tests for specparam.core.funcs.""" from pytest import raises import numpy as np from scipy.stats import norm, linregress -from fooof.core.errors import InconsistentDataError +from specparam.core.errors import InconsistentDataError -from fooof.core.funcs import * +from specparam.core.funcs import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/core/test_info.py b/specparam/tests/core/test_info.py similarity index 90% rename from fooof/tests/core/test_info.py rename to specparam/tests/core/test_info.py index b6742214..142ab634 100644 --- a/fooof/tests/core/test_info.py +++ b/specparam/tests/core/test_info.py @@ -1,6 +1,6 @@ -"""Tests for fooof.core.info.""" +"""Tests for specparam.core.info.""" -from fooof.core.info import * +from specparam.core.info import * ################################################################################################### ################################################################################################### @@ -10,7 +10,7 @@ def test_get_description(tfm): desc = get_description() objs = dir(tfm) - # Test that everything in dict is a valid component of the fooof object + # Test that everything in dict is a valid component of the model object for ke, va in desc.items(): for it in va: assert it in objs diff --git a/fooof/tests/core/test_io.py b/specparam/tests/core/test_io.py similarity index 61% rename from fooof/tests/core/test_io.py rename to specparam/tests/core/test_io.py index 7a18f69e..e8e6dd13 100644 --- a/fooof/tests/core/test_io.py +++ b/specparam/tests/core/test_io.py @@ -1,13 +1,13 @@ -"""Tests for fooof.core.io.""" +"""Tests for specparam.core.io.""" import os from pathlib import Path -from fooof.core.items import OBJ_DESC +from specparam.core.items import OBJ_DESC -from fooof.tests.settings import TEST_DATA_PATH +from specparam.tests.settings import TEST_DATA_PATH -from fooof.core.io import * +from specparam.core.io import * ################################################################################################### ################################################################################################### @@ -29,97 +29,96 @@ def test_fpath(): assert fpath('/path/', 'data.json') == '/path/data.json' assert fpath(Path('/path/'), 'data.json') == '/path/data.json' -def test_save_fm_str(tfm): - """Check saving fm data, with file specifiers as strings.""" +def test_save_model_str(tfm): + """Check saving model object data, with file specifiers as strings.""" # Test saving out each set of save elements - file_name_res = 'test_fooof_res' - file_name_set = 'test_fooof_set' - file_name_dat = 'test_fooof_dat' + file_name_res = 'test_res' + file_name_set = 'test_set' + file_name_dat = 'test_dat' - save_fm(tfm, file_name_res, TEST_DATA_PATH, False, True, False, False) - save_fm(tfm, file_name_set, TEST_DATA_PATH, False, False, True, False) - save_fm(tfm, file_name_dat, TEST_DATA_PATH, False, False, False, True) + save_model(tfm, file_name_res, TEST_DATA_PATH, False, True, False, False) + save_model(tfm, file_name_set, TEST_DATA_PATH, False, False, True, False) + save_model(tfm, file_name_dat, TEST_DATA_PATH, False, False, False, True) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name_res + '.json')) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name_set + '.json')) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name_dat + '.json')) # Test saving out all save elements - file_name_all = 'test_fooof_all' - save_fm(tfm, file_name_all, TEST_DATA_PATH, False, True, True, True) + file_name_all = 'test_all' + save_model(tfm, file_name_all, TEST_DATA_PATH, False, True, True, True) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name_all + '.json')) - -def test_save_fm_append(tfm): +def test_save_model_append(tfm): """Check saving fm data, appending to a file.""" - file_name = 'test_fooof_append' + file_name = 'test_append' - save_fm(tfm, file_name, TEST_DATA_PATH, True, True, True, True) - save_fm(tfm, file_name, TEST_DATA_PATH, True, True, True, True) + save_model(tfm, file_name, TEST_DATA_PATH, True, True, True, True) + save_model(tfm, file_name, TEST_DATA_PATH, True, True, True, True) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name + '.json')) -def test_save_fm_fobj(tfm): +def test_save_model_fobj(tfm): """Check saving fm data, with file object file specifier.""" - file_name = 'test_fooof_fileobj' + file_name = 'test_fileobj' # Save, using file-object: three successive lines with three possible save settings with open(os.path.join(TEST_DATA_PATH, file_name + '.json'), 'w') as f_obj: - save_fm(tfm, f_obj, TEST_DATA_PATH, False, True, False, False) - save_fm(tfm, f_obj, TEST_DATA_PATH, False, False, True, False) - save_fm(tfm, f_obj, TEST_DATA_PATH, False, False, False, True) + save_model(tfm, f_obj, TEST_DATA_PATH, False, True, False, False) + save_model(tfm, f_obj, TEST_DATA_PATH, False, False, True, False) + save_model(tfm, f_obj, TEST_DATA_PATH, False, False, False, True) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name + '.json')) -def test_save_fg(tfg): +def test_save_group(tfg): """Check saving fg data.""" - res_file_name = 'test_fooofgroup_res' - set_file_name = 'test_fooofgroup_set' - dat_file_name = 'test_fooofgroup_dat' + res_file_name = 'test_group_res' + set_file_name = 'test_group_set' + dat_file_name = 'test_group_dat' - save_fg(tfg, file_name=res_file_name, file_path=TEST_DATA_PATH, save_results=True) - save_fg(tfg, file_name=set_file_name, file_path=TEST_DATA_PATH, save_settings=True) - save_fg(tfg, file_name=dat_file_name, file_path=TEST_DATA_PATH, save_data=True) + save_group(tfg, file_name=res_file_name, file_path=TEST_DATA_PATH, save_results=True) + save_group(tfg, file_name=set_file_name, file_path=TEST_DATA_PATH, save_settings=True) + save_group(tfg, file_name=dat_file_name, file_path=TEST_DATA_PATH, save_data=True) assert os.path.exists(os.path.join(TEST_DATA_PATH, res_file_name + '.json')) assert os.path.exists(os.path.join(TEST_DATA_PATH, set_file_name + '.json')) assert os.path.exists(os.path.join(TEST_DATA_PATH, dat_file_name + '.json')) # Test saving out all save elements - file_name_all = 'test_fooofgroup_all' - save_fg(tfg, file_name_all, TEST_DATA_PATH, False, True, True, True) + file_name_all = 'test_group_all' + save_group(tfg, file_name_all, TEST_DATA_PATH, False, True, True, True) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name_all + '.json')) -def test_save_fg_append(tfg): +def test_save_group_append(tfg): """Check saving fg data, appending to file.""" - file_name = 'test_fooofgroup_append' + file_name = 'test_group_append' - save_fg(tfg, file_name, TEST_DATA_PATH, True, save_results=True) - save_fg(tfg, file_name, TEST_DATA_PATH, True, save_results=True) + save_group(tfg, file_name, TEST_DATA_PATH, True, save_results=True) + save_group(tfg, file_name, TEST_DATA_PATH, True, save_results=True) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name + '.json')) -def test_save_fg_fobj(tfg): +def test_save_group_fobj(tfg): """Check saving fg data, with file object file specifier.""" - file_name = 'test_fooof_fileobj' + file_name = 'test_fileobj' with open(os.path.join(TEST_DATA_PATH, file_name + '.json'), 'w') as f_obj: - save_fg(tfg, f_obj, TEST_DATA_PATH, False, True, False, False) + save_group(tfg, f_obj, TEST_DATA_PATH, False, True, False, False) assert os.path.exists(os.path.join(TEST_DATA_PATH, file_name + '.json')) def test_load_json_str(): """Test loading JSON file, with str file specifier. - Loads files from test_save_fm_str. + Loads files from test_save_model_str. """ - file_name = 'test_fooof_all' + file_name = 'test_all' data = load_json(file_name, TEST_DATA_PATH) @@ -127,10 +126,10 @@ def test_load_json_str(): def test_load_json_fobj(): """Test loading JSON file, with file object file specifier. - Loads files from test_save_fm_str. + Loads files from test_save_model_str. """ - file_name = 'test_fooof_all' + file_name = 'test_all' with open(os.path.join(TEST_DATA_PATH, file_name + '.json'), 'r') as f_obj: data = load_json(f_obj, '') @@ -139,10 +138,10 @@ def test_load_json_fobj(): def test_load_jsonlines(): """Test loading JSONlines file. - Loads files from test_save_fg. + Loads files from test_save_group. """ - res_file_name = 'test_fooofgroup_res' + res_file_name = 'test_group_res' for data in load_jsonlines(res_file_name, TEST_DATA_PATH): assert data @@ -152,7 +151,7 @@ def test_load_file_contents(): Note that is this test fails, it likely stems from an issue from saving. """ - file_name = 'test_fooof_all' + file_name = 'test_all' loaded_data = load_json(file_name, TEST_DATA_PATH) # Check settings diff --git a/fooof/tests/core/test_jacobians.py b/specparam/tests/core/test_jacobians.py similarity index 91% rename from fooof/tests/core/test_jacobians.py rename to specparam/tests/core/test_jacobians.py index aae25aa7..de5919fb 100644 --- a/fooof/tests/core/test_jacobians.py +++ b/specparam/tests/core/test_jacobians.py @@ -1,6 +1,6 @@ -"""Tests for fooof.core.jacobians.""" +"""Tests for specparam.core.jacobians.""" -from fooof.core.jacobians import * +from specparam.core.jacobians import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/core/test_modutils.py b/specparam/tests/core/test_modutils.py similarity index 97% rename from fooof/tests/core/test_modutils.py rename to specparam/tests/core/test_modutils.py index 39a1a803..d0f03025 100644 --- a/fooof/tests/core/test_modutils.py +++ b/specparam/tests/core/test_modutils.py @@ -1,11 +1,11 @@ -"""Tests for fooof.core.modutils. +"""Tests for specparam.core.modutils. Note: the decorators for copying documentation are not currently tested. """ from pytest import raises -from fooof.core.modutils import * +from specparam.core.modutils import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/core/test_reports.py b/specparam/tests/core/test_reports.py similarity index 56% rename from fooof/tests/core/test_reports.py rename to specparam/tests/core/test_reports.py index cf6fc5ce..6d490155 100644 --- a/fooof/tests/core/test_reports.py +++ b/specparam/tests/core/test_reports.py @@ -1,26 +1,26 @@ -"""Tests for fooof.core.reports.""" +"""Tests for specparam.core.reports.""" import os -from fooof.tests.settings import TEST_REPORTS_PATH +from specparam.tests.settings import TEST_REPORTS_PATH -from fooof.core.reports import * +from specparam.core.reports import * ################################################################################################### ################################################################################################### -def test_save_report_fm(tfm, skip_if_no_mpl): +def test_save_model_report(tfm, skip_if_no_mpl): file_name = 'test_report' - save_report_fm(tfm, file_name, TEST_REPORTS_PATH) + save_model_report(tfm, file_name, TEST_REPORTS_PATH) assert os.path.exists(os.path.join(TEST_REPORTS_PATH, file_name + '.pdf')) -def test_save_report_fg(tfg, skip_if_no_mpl): +def test_save_group_report(tfg, skip_if_no_mpl): file_name = 'test_group_report' - save_report_fg(tfg, file_name, TEST_REPORTS_PATH) + save_group_report(tfg, file_name, TEST_REPORTS_PATH) assert os.path.exists(os.path.join(TEST_REPORTS_PATH, file_name + '.pdf')) diff --git a/fooof/tests/core/test_strings.py b/specparam/tests/core/test_strings.py similarity index 75% rename from fooof/tests/core/test_strings.py rename to specparam/tests/core/test_strings.py index b54aaf47..0543d1ff 100644 --- a/fooof/tests/core/test_strings.py +++ b/specparam/tests/core/test_strings.py @@ -1,7 +1,7 @@ -"""Tests for fooof.core.strings.""" +"""Tests for specparam.core.strings.""" -from fooof.core.strings import * -from fooof.core.strings import _format, _no_model_str +from specparam.core.strings import * +from specparam.core.strings import _format, _no_model_str ################################################################################################### ################################################################################################### @@ -28,17 +28,17 @@ def test_gen_methods_report_str(): def test_gen_methods_text_str(tfm): - # Test with and without passing in a FOOOF object + # Test with and without passing in a model object assert gen_methods_text_str() assert gen_methods_text_str(tfm) -def test_gen_results_fm_str(tfm): +def test_gen_model_results_str(tfm): - assert gen_results_fm_str(tfm) + assert gen_model_results_str(tfm) -def test_gen_results_fg_str(tfg): +def test_gen_group_results_str(tfg): - assert gen_results_fg_str(tfg) + assert gen_group_results_str(tfg) def test_gen_issue_str(): diff --git a/fooof/tests/core/test_utils.py b/specparam/tests/core/test_utils.py similarity index 98% rename from fooof/tests/core/test_utils.py rename to specparam/tests/core/test_utils.py index 01115eaa..ab693456 100644 --- a/fooof/tests/core/test_utils.py +++ b/specparam/tests/core/test_utils.py @@ -1,4 +1,4 @@ -"""Tests for fooof.core.utils.""" +"""Tests for specparam.core.utils.""" from collections.abc import Iterable from itertools import repeat @@ -7,7 +7,7 @@ from pytest import raises -from fooof.core.utils import * +from specparam.core.utils import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/data/__init__.py b/specparam/tests/data/__init__.py similarity index 100% rename from fooof/tests/data/__init__.py rename to specparam/tests/data/__init__.py diff --git a/fooof/tests/data/test_conversions.py b/specparam/tests/data/test_conversions.py similarity index 93% rename from fooof/tests/data/test_conversions.py rename to specparam/tests/data/test_conversions.py index 607a6f4e..1131618b 100644 --- a/fooof/tests/data/test_conversions.py +++ b/specparam/tests/data/test_conversions.py @@ -1,13 +1,13 @@ -"""Tests for the fooof.data.conversions.""" +"""Tests for the specparam.data.conversions.""" from copy import deepcopy import numpy as np -from fooof.core.modutils import safe_import +from specparam.core.modutils import safe_import pd = safe_import('pandas') -from fooof.data.conversions import * +from specparam.data.conversions import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/data/test_data.py b/specparam/tests/data/test_data.py similarity index 69% rename from fooof/tests/data/test_data.py rename to specparam/tests/data/test_data.py index 3bc1dab5..09a1f9dd 100644 --- a/fooof/tests/data/test_data.py +++ b/specparam/tests/data/test_data.py @@ -1,43 +1,43 @@ -"""Tests for the fooof.data.data. +"""Tests for the specparam.data.data. For testing the data objects, the testing approach is to check that the object has the expected fields, given what is defined in the object description. """ -from fooof.core.items import OBJ_DESC +from specparam.core.items import OBJ_DESC -from fooof.data.data import * +from specparam.data.data import * ################################################################################################### ################################################################################################### -def test_fooof_settings(): +def test_model_settings(): - settings = FOOOFSettings([1, 8], 8, 0.25, 2, 'fixed') + settings = ModelSettings([1, 8], 8, 0.25, 2, 'fixed') assert settings for field in OBJ_DESC['settings']: assert getattr(settings, field) -def test_fooof_run_modes(): +def test_spectrum_meta_data(): - run_modes = FOOOFRunModes(True, True, True) - assert run_modes - - for field in OBJ_DESC['run_modes']: - assert getattr(run_modes, field.strip('_')) - -def test_fooof_meta_data(): - - meta_data = FOOOFMetaData([1, 50], 0.5) + meta_data = SpectrumMetaData([1, 50], 0.5) assert meta_data for field in OBJ_DESC['meta_data']: assert getattr(meta_data, field) -def test_fooof_results(): +def test_model_run_modes(): + + run_modes = ModelRunModes(True, True, True) + assert run_modes + + for field in OBJ_DESC['run_modes']: + assert getattr(run_modes, field.strip('_')) + +def test_fit_results(): - results = FOOOFResults([1, 1], [10, 0.5, 1], 0.95, 0.05, [10, 0.5, 0.5]) + results = FitResults([1, 1], [10, 0.5, 1], 0.95, 0.05, [10, 0.5, 0.5]) assert results results_fields = OBJ_DESC['results'] diff --git a/fooof/tests/objs/__init__.py b/specparam/tests/objs/__init__.py similarity index 100% rename from fooof/tests/objs/__init__.py rename to specparam/tests/objs/__init__.py diff --git a/fooof/tests/objs/test_fit.py b/specparam/tests/objs/test_fit.py similarity index 70% rename from fooof/tests/objs/test_fit.py rename to specparam/tests/objs/test_fit.py index ad20ff1c..22237362 100644 --- a/fooof/tests/objs/test_fit.py +++ b/specparam/tests/objs/test_fit.py @@ -1,4 +1,4 @@ -"""Tests for fooof.objs.fit, including the FOOOF object and it's methods. +"""Tests for specparam.objs.fit, including the model object and it's methods. NOTES ----- @@ -9,60 +9,60 @@ import numpy as np from pytest import raises -from fooof.core.items import OBJ_DESC -from fooof.core.errors import FitError -from fooof.core.utils import group_three -from fooof.core.modutils import safe_import -from fooof.core.errors import DataError, NoDataError, InconsistentDataError -from fooof.sim import gen_freqs, gen_power_spectrum -from fooof.data import FOOOFSettings, FOOOFMetaData, FOOOFResults +from specparam.core.items import OBJ_DESC +from specparam.core.errors import FitError +from specparam.core.utils import group_three +from specparam.sim import gen_freqs, sim_power_spectrum +from specparam.data import ModelSettings, SpectrumMetaData, FitResults +from specparam.core.modutils import safe_import +from specparam.core.errors import DataError, NoDataError, InconsistentDataError pd = safe_import('pandas') -from fooof.tests.settings import TEST_DATA_PATH -from fooof.tests.tutils import get_tfm, plot_test +from specparam.tests.settings import TEST_DATA_PATH +from specparam.tests.tutils import get_tfm, plot_test -from fooof.objs.fit import * +from specparam.objs.fit import * ################################################################################################### ################################################################################################### -def test_fooof(): - """Check FOOOF object initializes properly.""" +def test_model_object(): + """Check model object initializes properly.""" - assert FOOOF(verbose=False) + assert SpectralModel(verbose=False) -def test_fooof_has_data(tfm): +def test_has_data(tfm): """Test the has_data property attribute, with and without model fits.""" assert tfm.has_data - ntfm = FOOOF() + ntfm = SpectralModel() assert not ntfm.has_data -def test_fooof_has_model(tfm): +def test_has_model(tfm): """Test the has_model property attribute, with and without model fits.""" assert tfm.has_model - ntfm = FOOOF() + ntfm = SpectralModel() assert not ntfm.has_model -def test_fooof_n_peaks(tfm): +def test_n_peaks(tfm): """Test the n_peaks property attribute.""" assert tfm.n_peaks_ -def test_fooof_fit_nk(): - """Test FOOOF fit, no knee.""" +def test_fit_nk(): + """Test fit, no knee.""" ap_params = [50, 2] gauss_params = [10, 0.5, 2, 20, 0.3, 4] nlv = 0.0025 - xs, ys = gen_power_spectrum([3, 50], ap_params, gauss_params, nlv) + xs, ys = sim_power_spectrum([3, 50], ap_params, gauss_params, nlv) - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) tfm.fit(xs, ys) # Check model results - aperiodic parameters @@ -72,31 +72,31 @@ def test_fooof_fit_nk(): for ii, gauss in enumerate(group_three(gauss_params)): assert np.allclose(gauss, tfm.gaussian_params_[ii], [2.0, 0.5, 1.0]) -def test_fooof_fit_nk_noise(): - """Test FOOOF fit on noisy data, to make sure nothing breaks.""" +def test_fit_nk_noise(): + """Test fit on noisy data, to make sure nothing breaks.""" ap_params = [50, 2] gauss_params = [10, 0.5, 2, 20, 0.3, 4] nlv = 1.0 - xs, ys = gen_power_spectrum([3, 50], ap_params, gauss_params, nlv) + xs, ys = sim_power_spectrum([3, 50], ap_params, gauss_params, nlv) - tfm = FOOOF(max_n_peaks=8, verbose=False) + tfm = SpectralModel(max_n_peaks=8, verbose=False) tfm.fit(xs, ys) # No accuracy checking here - just checking that it ran assert tfm.has_model -def test_fooof_fit_knee(): - """Test FOOOF fit, with a knee.""" +def test_fit_knee(): + """Test fit, with a knee.""" ap_params = [50, 10, 1] gauss_params = [10, 0.3, 2, 20, 0.1, 4, 60, 0.3, 1] nlv = 0.0025 - xs, ys = gen_power_spectrum([1, 150], ap_params, gauss_params, nlv) + xs, ys = sim_power_spectrum([1, 150], ap_params, gauss_params, nlv) - tfm = FOOOF(aperiodic_mode='knee', verbose=False) + tfm = SpectralModel(aperiodic_mode='knee', verbose=False) tfm.fit(xs, ys) # Check model results - aperiodic parameters @@ -106,14 +106,14 @@ def test_fooof_fit_knee(): for ii, gauss in enumerate(group_three(gauss_params)): assert np.allclose(gauss, tfm.gaussian_params_[ii], [2.0, 0.5, 1.0]) -def test_fooof_fit_measures(): +def test_fit_measures(): """Test goodness of fit & error metrics, post model fitting.""" - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) # Hack fake data with known properties: total error magnitude 2 tfm.power_spectrum = np.array([1, 2, 3, 4, 5]) - tfm.fooofed_spectrum_ = np.array([1, 2, 5, 4, 5]) + tfm.modeled_spectrum_ = np.array([1, 2, 5, 4, 5]) # Check default goodness of fit and error measures tfm._calc_r_squared() @@ -129,14 +129,14 @@ def test_fooof_fit_measures(): with raises(ValueError): tfm._calc_error(metric='BAD') -def test_fooof_checks(): - """Test various checks, errors and edge cases in FOOOF. +def test_checks(): + """Test various checks, errors and edge cases for model fitting. This tests all the input checking done in `_prepare_data`. """ - xs, ys = gen_power_spectrum([3, 50], [50, 2], [10, 0.5, 2]) + xs, ys = sim_power_spectrum([3, 50], [50, 2], [10, 0.5, 2]) - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) ## Check checks & errors done in `_prepare_data` @@ -160,7 +160,7 @@ def test_fooof_checks(): tfm.fit(xs, ys, [3, 40]) # Check freq of 0 issue - xs, ys = gen_power_spectrum([3, 50], [50, 2], [10, 0.5, 2]) + xs, ys = sim_power_spectrum([3, 50], [50, 2], [10, 0.5, 2]) tfm.fit(xs, ys) assert tfm.freqs[0] != 0 @@ -177,16 +177,16 @@ def test_fooof_checks(): ## Check errors & errors done in `fit` # Check fit, and string report model error (no data / model fit) - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) with raises(NoDataError): tfm.fit() -def test_fooof_load(): - """Test load into FOOOF. Note: loads files from test_core_io.""" +def test_load(): + """Test loading data into model object. Note: loads files from test_core_io.""" # Test loading just results - tfm = FOOOF(verbose=False) - file_name_res = 'test_fooof_res' + tfm = SpectralModel(verbose=False) + file_name_res = 'test_res' tfm.load(file_name_res, TEST_DATA_PATH) # Check that result attributes get filled for result in OBJ_DESC['results']: @@ -199,8 +199,8 @@ def test_fooof_load(): assert getattr(tfm, 'power_spectrum') is None # Test loading just settings - tfm = FOOOF(verbose=False) - file_name_set = 'test_fooof_set' + tfm = SpectralModel(verbose=False) + file_name_set = 'test_set' tfm.load(file_name_set, TEST_DATA_PATH) for setting in OBJ_DESC['settings']: assert getattr(tfm, setting) is not None @@ -210,8 +210,8 @@ def test_fooof_load(): assert tfm.power_spectrum is None # Test loading just data - tfm = FOOOF(verbose=False) - file_name_dat = 'test_fooof_dat' + tfm = SpectralModel(verbose=False) + file_name_dat = 'test_dat' tfm.load(file_name_dat, TEST_DATA_PATH) assert tfm.power_spectrum is not None # Test that settings and results are None @@ -221,8 +221,8 @@ def test_fooof_load(): assert np.all(np.isnan(getattr(tfm, result))) # Test loading all elements - tfm = FOOOF(verbose=False) - file_name_all = 'test_fooof_all' + tfm = SpectralModel(verbose=False) + file_name_all = 'test_all' tfm.load(file_name_all, TEST_DATA_PATH) for result in OBJ_DESC['results']: assert not np.all(np.isnan(getattr(tfm, result))) @@ -234,9 +234,9 @@ def test_fooof_load(): assert getattr(tfm, meta_dat) is not None def test_add_data(): - """Tests method to add data to FOOOF objects.""" + """Tests method to add data to model objects.""" - # This test uses it's own FOOOF object, to not add stuff to the global one + # This test uses it's own model object, to not add stuff to the global one tfm = get_tfm() # Test data for adding @@ -250,7 +250,7 @@ def test_add_data(): # Test that prior data does not get cleared, when requesting not to clear tfm._reset_data_results(True, True, True) - tfm.add_results(FOOOFResults([1, 1], [10, 0.5, 0.5], 0.95, 0.02, [10, 0.5, 0.25])) + tfm.add_results(FitResults([1, 1], [10, 0.5, 0.5], 0.95, 0.02, [10, 0.5, 0.25])) tfm.add_data(freqs, pows, clear_results=False) assert tfm.has_data assert tfm.has_model @@ -262,54 +262,54 @@ def test_add_data(): assert not tfm.has_model def test_add_settings(): - """Tests method to add settings to FOOOF objects.""" + """Tests method to add settings to model object.""" - # This test uses it's own FOOOF object, to not add stuff to the global one + # This test uses it's own model object, to not add stuff to the global one tfm = get_tfm() # Test adding settings - fooof_settings = FOOOFSettings([1, 4], 6, 0, 2, 'fixed') - tfm.add_settings(fooof_settings) + settings = ModelSettings([1, 4], 6, 0, 2, 'fixed') + tfm.add_settings(settings) for setting in OBJ_DESC['settings']: - assert getattr(tfm, setting) == getattr(fooof_settings, setting) + assert getattr(tfm, setting) == getattr(settings, setting) def test_add_meta_data(): - """Tests method to add meta data to FOOOF objects.""" + """Tests method to add meta data to model object.""" - # This test uses it's own FOOOF object, to not add stuff to the global one + # This test uses it's own model object, to not add stuff to the global one tfm = get_tfm() # Test adding meta data - fooof_meta_data = FOOOFMetaData([3, 40], 0.5) - tfm.add_meta_data(fooof_meta_data) + meta_data = SpectrumMetaData([3, 40], 0.5) + tfm.add_meta_data(meta_data) for meta_dat in OBJ_DESC['meta_data']: - assert getattr(tfm, meta_dat) == getattr(fooof_meta_data, meta_dat) + assert getattr(tfm, meta_dat) == getattr(meta_data, meta_dat) def test_add_results(): - """Tests method to add results to FOOOF objects.""" + """Tests method to add results to model object.""" - # This test uses it's own FOOOF object, to not add stuff to the global one + # This test uses it's own model object, to not add stuff to the global one tfm = get_tfm() # Test adding results - fooof_results = FOOOFResults([1, 1], [10, 0.5, 0.5], 0.95, 0.02, [10, 0.5, 0.25]) - tfm.add_results(fooof_results) + results = FitResults([1, 1], [10, 0.5, 0.5], 0.95, 0.02, [10, 0.5, 0.25]) + tfm.add_results(results) assert tfm.has_model for setting in OBJ_DESC['results']: - assert getattr(tfm, setting) == getattr(fooof_results, setting.strip('_')) + assert getattr(tfm, setting) == getattr(results, setting.strip('_')) def test_obj_gets(tfm): - """Tests methods that return FOOOF data objects. + """Tests methods that return data objects. Checks: get_settings, get_meta_data, get_results """ settings = tfm.get_settings() - assert isinstance(settings, FOOOFSettings) + assert isinstance(settings, ModelSettings) meta_data = tfm.get_meta_data() - assert isinstance(meta_data, FOOOFMetaData) + assert isinstance(meta_data, SpectrumMetaData) results = tfm.get_results() - assert isinstance(results, FOOOFResults) + assert isinstance(results, FitResults) def test_get_components(tfm): @@ -338,14 +338,14 @@ def test_get_params(tfm): assert np.any(tfm.get_params(dname, dtype)) def test_copy(): - """Test copy FOOOF method.""" + """Test copy model object method.""" - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) ntfm = tfm.copy() assert tfm != ntfm -def test_fooof_prints(tfm): +def test_prints(tfm): """Test methods that print (alias and pass through methods). Checks: print_settings, print_results, print_report_issue. @@ -356,12 +356,12 @@ def test_fooof_prints(tfm): tfm.print_report_issue() @plot_test -def test_fooof_plot(tfm, skip_if_no_mpl): - """Check the alias to plot FOOOF.""" +def test_plot(tfm, skip_if_no_mpl): + """Check the alias to plot spectra & model results.""" tfm.plot() -def test_fooof_resets(): +def test_resets(): """Check that all relevant data is cleared in the reset method.""" # Note: uses it's own tfm, to not clear the global one @@ -375,25 +375,25 @@ def test_fooof_resets(): assert getattr(tfm, field) is None for field in OBJ_DESC['results']: assert np.all(np.isnan(getattr(tfm, field))) - assert tfm.freqs is None and tfm.fooofed_spectrum_ is None + assert tfm.freqs is None and tfm.modeled_spectrum_ is None -def test_fooof_report(skip_if_no_mpl): +def test_report(skip_if_no_mpl): """Check that running the top level model method runs.""" - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) - tfm.report(*gen_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) + tfm.report(*sim_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) assert tfm -def test_fooof_fit_failure(): - """Test FOOOF fit failures.""" +def test_fit_failure(): + """Test model fit failures.""" ## Induce a runtime error, and check it runs through - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) tfm._maxfev = 2 - tfm.fit(*gen_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) + tfm.fit(*sim_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) # Check after failing out of fit, all results are reset for result in OBJ_DESC['results']: @@ -401,35 +401,35 @@ def test_fooof_fit_failure(): ## Monkey patch to check errors in general # This mimics the main fit-failure, without requiring bad data / waiting for it to fail. - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) def raise_runtime_error(*args, **kwargs): raise FitError('Test-MonkeyPatch') tfm._fit_peaks = raise_runtime_error - # Run a FOOOF fit - this should raise an error, but continue in try/except - tfm.fit(*gen_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) + # Run a model fit - this should raise an error, but continue in try/except + tfm.fit(*sim_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) # Check after failing out of fit, all results are reset for result in OBJ_DESC['results']: assert np.all(np.isnan(getattr(tfm, result))) -def test_fooof_debug(): - """Test FOOOF in debug mode, including with fit failures.""" +def test_debug(): + """Test model object in debug mode, including with fit failures.""" - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) tfm._maxfev = 2 tfm.set_debug_mode(True) assert tfm._debug is True with raises(FitError): - tfm.fit(*gen_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) + tfm.fit(*sim_power_spectrum([3, 50], [50, 2], [10, 0.5, 2, 20, 0.3, 4])) -def test_fooof_set_check_modes(tfm): +def test_set_check_modes(tfm): """Test changing check_modes using set_check_modes, and that checks get turned off. - Note that testing for checks raising errors happens in test_fooof_checks.`""" + Note that testing for checks raising errors happens in test_checks.`""" - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) tfm.set_check_modes(False, False) assert tfm._check_freqs is False @@ -458,12 +458,12 @@ def test_fooof_set_check_modes(tfm): def test_set_run_modes(): - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) tfm.set_run_modes(False, False, False) for field in OBJ_DESC['run_modes']: assert getattr(tfm, field) is False -def test_fooof_to_df(tfm, tbands, skip_if_no_pandas): +def test_to_df(tfm, tbands, skip_if_no_pandas): df1 = tfm.to_df(2) assert isinstance(df1, pd.Series) diff --git a/fooof/tests/objs/test_group.py b/specparam/tests/objs/test_group.py similarity index 70% rename from fooof/tests/objs/test_group.py rename to specparam/tests/objs/test_group.py index 5f1e23d1..26313d19 100644 --- a/fooof/tests/objs/test_group.py +++ b/specparam/tests/objs/test_group.py @@ -1,4 +1,4 @@ -"""Tests for the fooof.objs.group, including the FOOOFGroup object and it's methods. +"""Tests for the specparam.objs.group, including the group model object and it's methods. NOTES ----- @@ -11,58 +11,57 @@ import numpy as np from numpy.testing import assert_equal -from fooof.core.items import OBJ_DESC -from fooof.core.modutils import safe_import -from fooof.core.errors import DataError, NoDataError, InconsistentDataError -from fooof.data import FOOOFResults -from fooof.sim import gen_group_power_spectra +from specparam.data import FitResults +from specparam.core.items import OBJ_DESC +from specparam.core.modutils import safe_import +from specparam.sim import sim_group_power_spectra pd = safe_import('pandas') -from fooof.tests.settings import TEST_DATA_PATH, TEST_REPORTS_PATH -from fooof.tests.tutils import default_group_params, plot_test +from specparam.tests.settings import TEST_DATA_PATH, TEST_REPORTS_PATH +from specparam.tests.tutils import default_group_params, plot_test -from fooof.objs.group import * +from specparam.objs.group import * ################################################################################################### ################################################################################################### -def test_fg(): - """Check FOOOFGroup object initializes properly.""" +def test_group(): + """Check group object initializes properly.""" - # Note: doesn't assert fg itself, as it return false when group_results are empty - # This is due to the __len__ used in FOOOFGroup - fg = FOOOFGroup(verbose=False) - assert isinstance(fg, FOOOFGroup) + # Note: doesn't assert the object itself, which returns false when `group_results` is empty + # This is due to the `__len__` used in the group object + fg = SpectralGroupModel(verbose=False) + assert isinstance(fg, SpectralGroupModel) -def test_fg_iter(tfg): - """Check iterating through FOOOFGroup.""" +def test_iter(tfg): + """Check iterating through group object.""" for res in tfg: assert res -def test_fg_getitem(tfg): - """Check indexing, from custom __getitem__, in FOOOFGroup.""" +def test_getitem(tfg): + """Check indexing, from custom `__getitem__` in group object.""" assert tfg[0] -def test_fg_has_data(tfg): +def test_has_data(tfg): """Test the has_data property attribute, with and without data.""" assert tfg.has_model - ntfg = FOOOFGroup() + ntfg = SpectralGroupModel() assert not ntfg.has_data -def test_fg_has_model(tfg): +def test_has_model(tfg): """Test the has_model property attribute, with and without model fits.""" assert tfg.has_model - ntfg = FOOOFGroup() + ntfg = SpectralGroupModel() assert not ntfg.has_model -def test_fooof_n_peaks(tfg): +def test_n_peaks(tfg): """Test the n_peaks property attribute.""" assert tfg.n_peaks_ @@ -79,63 +78,63 @@ def test_null_inds(tfg): # Since there should be no failed fits, this should return an empty list assert tfg.null_inds_ == [] -def test_fg_fit_nk(): - """Test FOOOFGroup fit, no knee.""" +def test_fit_nk(): + """Test group fit, no knee.""" n_spectra = 2 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params(), nlvs=0) + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params(), nlvs=0) - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) tfg.fit(xs, ys) out = tfg.get_results() assert out assert len(out) == n_spectra - assert isinstance(out[0], FOOOFResults) + assert isinstance(out[0], FitResults) assert np.all(out[1].aperiodic_params) -def test_fg_fit_nk_noise(): - """Test FOOOFGroup fit, no knee, on noisy data, to make sure nothing breaks.""" +def test_fit_nk_noise(): + """Test group fit, no knee, on noisy data, to make sure nothing breaks.""" n_spectra = 5 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params(), nlvs=1.0) + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params(), nlvs=1.0) - tfg = FOOOFGroup(max_n_peaks=8, verbose=False) + tfg = SpectralGroupModel(max_n_peaks=8, verbose=False) tfg.fit(xs, ys) # No accuracy checking here - just checking that it ran assert tfg.has_model -def test_fg_fit_knee(): - """Test FOOOFGroup fit, with a knee.""" +def test_fit_knee(): + """Test group fit, with a knee.""" n_spectra = 2 ap_params = [50, 2, 1] gaussian_params = [10, 0.5, 2, 20, 0.3, 4] - xs, ys = gen_group_power_spectra(n_spectra, [1, 150], ap_params, gaussian_params, nlvs=0) + xs, ys = sim_group_power_spectra(n_spectra, [1, 150], ap_params, gaussian_params, nlvs=0) - tfg = FOOOFGroup(aperiodic_mode='knee', verbose=False) + tfg = SpectralGroupModel(aperiodic_mode='knee', verbose=False) tfg.fit(xs, ys) # No accuracy checking here - just checking that it ran assert tfg.has_model -def test_fg_fit_progress(tfg): - """Test running FOOOFGroup fitting, with a progress bar.""" +def test_fit_progress(tfg): + """Test running group fitting, with a progress bar.""" tfg.fit(progress='tqdm') def test_fg_fail(): - """Test FOOOFGroup fit, in a way that some fits will fail. + """Test group fit, in a way that some fits will fail. Also checks that model failures don't cause errors. """ # Create some noisy spectra that will be hard to fit - fs, ps = gen_group_power_spectra(10, [3, 6], [1, 1], [10, 1, 1], nlvs=10) + fs, ps = sim_group_power_spectra(10, [3, 6], [1, 1], [10, 1, 1], nlvs=10) # Use a fg with the max iterations set so low that it will fail to converge - ntfg = FOOOFGroup() + ntfg = SpectralGroupModel() ntfg._maxfev = 5 # Fit models, where some will fail, to see if it completes cleanly @@ -161,13 +160,13 @@ def test_fg_fail(): assert ntfg.n_null_ > 0 assert ntfg.null_inds_ -def test_fg_drop(): - """Test function to drop results from FOOOFGroup.""" +def test_drop(): + """Test function to drop results from group object.""" n_spectra = 3 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params()) + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params()) - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) # Test dropping one ind tfg.fit(xs, ys) @@ -187,28 +186,28 @@ def test_fg_drop(): for field in dropped_fres._fields: assert np.all(np.isnan(getattr(dropped_fres, field))) - # Test that a FOOOFGroup that has had inds dropped still works with `get_params` + # Test that a group object that has had inds dropped still works with `get_params` cfs = tfg.get_params('peak_params', 1) exps = tfg.get_params('aperiodic_params', 'exponent') assert np.all(np.isnan(exps[drop_inds])) assert np.all(np.invert(np.isnan(np.delete(exps, drop_inds)))) -def test_fg_fit_par(): - """Test FOOOFGroup fit, running in parallel.""" +def test_fit_par(): + """Test group fit, running in parallel.""" n_spectra = 2 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params()) + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params()) - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) tfg.fit(xs, ys, n_jobs=2) out = tfg.get_results() assert out assert len(out) == n_spectra - assert isinstance(out[0], FOOOFResults) + assert isinstance(out[0], FitResults) assert np.all(out[1].aperiodic_params) -def test_fg_print(tfg): +def test_print(tfg): """Check print method (alias).""" tfg.print_results() @@ -241,20 +240,20 @@ def test_get_params(tfg): assert np.any(tfg.get_params(dname, dtype)) @plot_test -def test_fg_plot(tfg, skip_if_no_mpl): +def test_plot(tfg, skip_if_no_mpl): """Check alias method for plot.""" tfg.plot() -def test_fg_load(): - """Test load into FOOOFGroup. Note: loads files from test_core_io.""" +def test_load(): + """Test load into group object. Note: loads files from test_core_io.""" - file_name_res = 'test_fooofgroup_res' - file_name_set = 'test_fooofgroup_set' - file_name_dat = 'test_fooofgroup_dat' + file_name_res = 'test_group_res' + file_name_set = 'test_group_set' + file_name_dat = 'test_group_dat' # Test loading just results - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) tfg.load(file_name_res, TEST_DATA_PATH) assert len(tfg.group_results) > 0 # Test that settings and data are None @@ -265,7 +264,7 @@ def test_fg_load(): assert tfg.power_spectra is None # Test loading just settings - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) tfg.load(file_name_set, TEST_DATA_PATH) for setting in OBJ_DESC['settings']: assert getattr(tfg, setting) is not None @@ -275,7 +274,7 @@ def test_fg_load(): assert tfg.power_spectra is None # Test loading just data - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) tfg.load(file_name_dat, TEST_DATA_PATH) assert tfg.power_spectra is not None # Test that settings and results are None @@ -285,8 +284,8 @@ def test_fg_load(): assert np.all(np.isnan(getattr(tfg, result))) # Test loading all elements - tfg = FOOOFGroup(verbose=False) - file_name_all = 'test_fooofgroup_all' + tfg = SpectralGroupModel(verbose=False) + file_name_all = 'test_group_all' tfg.load(file_name_all, TEST_DATA_PATH) assert len(tfg.group_results) > 0 for setting in OBJ_DESC['settings']: @@ -295,29 +294,29 @@ def test_fg_load(): for meta_dat in OBJ_DESC['meta_data']: assert getattr(tfg, meta_dat) is not None -def test_fg_report(skip_if_no_mpl): +def test_report(skip_if_no_mpl): """Check that running the top level model method runs.""" n_spectra = 2 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params()) + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params()) - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) tfg.report(xs, ys) assert tfg -def test_fg_get_fooof(tfg): - """Check return of an individual model fit to a FOOOF object from FOOOFGroup.""" +def test_get_model(tfg): + """Check return of an individual model fit from a group object.""" # Check without regenerating - tfm0 = tfg.get_fooof(0, False) + tfm0 = tfg.get_model(0, False) assert tfm0 # Check that settings are copied over properly for setting in OBJ_DESC['settings']: assert getattr(tfg, setting) == getattr(tfm0, setting) # Check with regenerating - tfm1 = tfg.get_fooof(1, True) + tfm1 = tfg.get_model(1, True) assert tfm1 # Check that regenerated model is created for result in OBJ_DESC['results']: @@ -326,24 +325,24 @@ def test_fg_get_fooof(tfg): # Test when object has no data (clear a copy of tfg) new_tfg = tfg.copy() new_tfg._reset_data_results(False, True, True, True) - tfm2 = new_tfg.get_fooof(0, True) + tfm2 = new_tfg.get_model(0, True) assert tfm2 # Check that data info is copied over properly for meta_dat in OBJ_DESC['meta_data']: assert getattr(tfm2, meta_dat) -def test_fg_get_group(tfg): - """Check the return of a sub-sampled FOOOFGroup.""" +def test_get_group(tfg): + """Check the return of a sub-sampled group object.""" # Check with list index inds1 = [1, 2] nfg1 = tfg.get_group(inds1) - assert isinstance(nfg1, FOOOFGroup) + assert isinstance(nfg1, SpectralGroupModel) # Check with range index inds2 = range(0, 2) nfg2 = tfg.get_group(inds2) - assert isinstance(nfg2, FOOOFGroup) + assert isinstance(nfg2, SpectralGroupModel) # Check that settings are copied over properly for setting in OBJ_DESC['settings']: diff --git a/fooof/tests/objs/test_utils.py b/specparam/tests/objs/test_utils.py similarity index 58% rename from fooof/tests/objs/test_utils.py rename to specparam/tests/objs/test_utils.py index 28a4c87e..77bb375d 100644 --- a/fooof/tests/objs/test_utils.py +++ b/specparam/tests/objs/test_utils.py @@ -1,49 +1,49 @@ -"""Test functions for fooof.objs.utils.""" +"""Test functions for specparam.objs.utils.""" from pytest import raises import numpy as np -from fooof import FOOOFGroup -from fooof.objs.utils import compare_info -from fooof.sim import gen_group_power_spectra -from fooof.core.errors import NoModelError, IncompatibleSettingsError +from specparam import SpectralGroupModel +from specparam.objs.utils import compare_model_objs +from specparam.sim import sim_group_power_spectra +from specparam.core.errors import NoModelError, IncompatibleSettingsError -from fooof.tests.tutils import default_group_params +from specparam.tests.tutils import default_group_params -from fooof.objs.utils import * +from specparam.objs.utils import * ################################################################################################### ################################################################################################### -def test_compare_info(tfm, tfg): +def test_compare_model_objs(tfm, tfg): for f_obj in [tfm, tfg]: f_obj2 = f_obj.copy() - assert compare_info([f_obj, f_obj2], 'settings') + assert compare_model_objs([f_obj, f_obj2], 'settings') f_obj2.peak_width_limits = [2, 4] f_obj2._reset_internal_settings() - assert not compare_info([f_obj, f_obj2], 'settings') + assert not compare_model_objs([f_obj, f_obj2], 'settings') - assert compare_info([f_obj, f_obj2], 'meta_data') + assert compare_model_objs([f_obj, f_obj2], 'meta_data') f_obj2.freq_range = [5, 25] - assert not compare_info([f_obj, f_obj2], 'meta_data') + assert not compare_model_objs([f_obj, f_obj2], 'meta_data') -def test_average_fg(tfg, tbands): +def test_average_group(tfg, tbands): - nfm = average_fg(tfg, tbands) + nfm = average_group(tfg, tbands) assert nfm # Test bad average method error with raises(ValueError): - average_fg(tfg, tbands, avg_method='BAD') + average_group(tfg, tbands, avg_method='BAD') # Test no data available error - ntfg = FOOOFGroup() + ntfg = SpectralGroupModel() with raises(NoModelError): - average_fg(ntfg, tbands) + average_group(ntfg, tbands) def test_average_reconstructions(tfg): @@ -52,57 +52,57 @@ def test_average_reconstructions(tfg): assert isinstance(avg_model, np.ndarray) assert freqs.shape == avg_model.shape -def test_combine_fooofs(tfm, tfg): +def test_combine_model_objs(tfm, tfg): tfm2 = tfm.copy() tfm3 = tfm.copy() tfg2 = tfg.copy() tfg3 = tfg.copy() - # Check combining 2 FOOOFs - nfg1 = combine_fooofs([tfm, tfm2]) + # Check combining 2 model objects + nfg1 = combine_model_objs([tfm, tfm2]) assert nfg1 assert len(nfg1) == 2 - assert compare_info([nfg1, tfm], 'settings') + assert compare_model_objs([nfg1, tfm], 'settings') assert nfg1.group_results[0] == tfm.get_results() assert nfg1.group_results[-1] == tfm2.get_results() - # Check combining 3 FOOOFs - nfg2 = combine_fooofs([tfm, tfm2, tfm3]) + # Check combining 3 model objects + nfg2 = combine_model_objs([tfm, tfm2, tfm3]) assert nfg2 assert len(nfg2) == 3 - assert compare_info([nfg2, tfm], 'settings') + assert compare_model_objs([nfg2, tfm], 'settings') assert nfg2.group_results[0] == tfm.get_results() assert nfg2.group_results[-1] == tfm3.get_results() - # Check combining 2 FOOOFGroups - nfg3 = combine_fooofs([tfg, tfg2]) + # Check combining 2 group objects + nfg3 = combine_model_objs([tfg, tfg2]) assert nfg3 assert len(nfg3) == len(tfg) + len(tfg2) - assert compare_info([nfg3, tfg, tfg2], 'settings') + assert compare_model_objs([nfg3, tfg, tfg2], 'settings') assert nfg3.group_results[0] == tfg.group_results[0] assert nfg3.group_results[-1] == tfg2.group_results[-1] - # Check combining 3 FOOOFGroups - nfg4 = combine_fooofs([tfg, tfg2, tfg3]) + # Check combining 3 group objects + nfg4 = combine_model_objs([tfg, tfg2, tfg3]) assert nfg4 assert len(nfg4) == len(tfg) + len(tfg2) + len(tfg3) - assert compare_info([nfg4, tfg, tfg2, tfg3], 'settings') + assert compare_model_objs([nfg4, tfg, tfg2, tfg3], 'settings') assert nfg4.group_results[0] == tfg.group_results[0] assert nfg4.group_results[-1] == tfg3.group_results[-1] - # Check combining a mixture of FOOOF & FOOOFGroup - nfg5 = combine_fooofs([tfg, tfm, tfg2, tfm2]) + # Check combining a mixture of model & group objects + nfg5 = combine_model_objs([tfg, tfm, tfg2, tfm2]) assert nfg5 assert len(nfg5) == len(tfg) + 1 + len(tfg2) + 1 - assert compare_info([nfg5, tfg, tfm, tfg2, tfm2], 'settings') + assert compare_model_objs([nfg5, tfg, tfm, tfg2, tfm2], 'settings') assert nfg5.group_results[0] == tfg.group_results[0] assert nfg5.group_results[-1] == tfm2.get_results() # Check combining objects with no data tfm2._reset_data_results(False, True, True) tfg2._reset_data_results(False, True, True, True) - nfg6 = combine_fooofs([tfm2, tfg2]) + nfg6 = combine_model_objs([tfm2, tfg2]) assert len(nfg6) == 1 + len(tfg2) assert nfg6.power_spectra is None @@ -115,7 +115,7 @@ def test_combine_errors(tfm, tfg): f_obj2._reset_internal_settings() with raises(IncompatibleSettingsError): - combine_fooofs([f_obj, f_obj2]) + combine_model_objs([f_obj, f_obj2]) # Incompatible data information for f_obj in [tfm, tfg]: @@ -123,18 +123,18 @@ def test_combine_errors(tfm, tfg): f_obj2.freq_range = [5, 30] with raises(IncompatibleSettingsError): - combine_fooofs([f_obj, f_obj2]) + combine_model_objs([f_obj, f_obj2]) -def test_fit_fooof_3d(tfg): +def test_fit_models_3d(tfg): n_groups = 2 n_spectra = 3 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params()) + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params()) ys = np.stack([ys] * n_groups, axis=0) spectra_shape = np.shape(ys) - tfg = FOOOFGroup() - fgs = fit_fooof_3d(tfg, xs, ys) + tfg = SpectralGroupModel() + fgs = fit_models_3d(tfg, xs, ys) assert len(fgs) == n_groups == spectra_shape[0] for fg in fgs: diff --git a/fooof/tests/plts/__init__.py b/specparam/tests/plts/__init__.py similarity index 100% rename from fooof/tests/plts/__init__.py rename to specparam/tests/plts/__init__.py diff --git a/fooof/tests/plts/test_annotate.py b/specparam/tests/plts/test_annotate.py similarity index 65% rename from fooof/tests/plts/test_annotate.py rename to specparam/tests/plts/test_annotate.py index 84f3848d..f99c1048 100644 --- a/fooof/tests/plts/test_annotate.py +++ b/specparam/tests/plts/test_annotate.py @@ -1,11 +1,11 @@ -"""Tests for fooof.plts.annotate.""" +"""Tests for specparam.plts.annotate.""" import numpy as np -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH -from fooof.plts.annotate import * +from specparam.plts.annotate import * ################################################################################################### ################################################################################################### @@ -13,7 +13,7 @@ @plot_test def test_plot_annotated_peak_search(tfm, skip_if_no_mpl): - plot_annotated_peak_search(tfm, save_fig=True, file_path=TEST_PLOTS_PATH, + plot_annotated_peak_search(tfm, file_path=TEST_PLOTS_PATH, file_name='test_plot_annotated_peak_search.png') @plot_test @@ -21,5 +21,5 @@ def test_plot_annotated_model(tfm, skip_if_no_mpl): # Make sure model has been fit & then plot annotated model tfm.fit() - plot_annotated_model(tfm, save_fig=True, file_path=TEST_PLOTS_PATH, + plot_annotated_model(tfm, file_path=TEST_PLOTS_PATH, file_name='test_plot_annotated_model.png') diff --git a/fooof/tests/plts/test_aperiodic.py b/specparam/tests/plts/test_aperiodic.py similarity index 78% rename from fooof/tests/plts/test_aperiodic.py rename to specparam/tests/plts/test_aperiodic.py index 477b2205..d0b3574e 100644 --- a/fooof/tests/plts/test_aperiodic.py +++ b/specparam/tests/plts/test_aperiodic.py @@ -1,11 +1,11 @@ -"""Tests for fooof.plts.aperiodic.""" +"""Tests for specparam.plts.aperiodic.""" import numpy as np -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH -from fooof.plts.aperiodic import * +from specparam.plts.aperiodic import * ################################################################################################### ################################################################################################### @@ -22,7 +22,7 @@ def test_plot_aperiodic_params(skip_if_no_mpl): # Test for 'knee' mode: offset, knee exponent aps = np.array([[1, 100, 1], [0.5, 150, 0.5], [2, 200, 2]]) - plot_aperiodic_params(aps, save_fig=True, file_path=TEST_PLOTS_PATH, + plot_aperiodic_params(aps, file_path=TEST_PLOTS_PATH, file_name='test_plot_aperiodic_params.png') @plot_test @@ -38,5 +38,5 @@ def test_plot_aperiodic_fits(skip_if_no_mpl): # Test for 'knee' mode: offset, knee exponent aps = np.array([[1, 100, 1], [0.5, 150, 0.5], [2, 200, 2]]) - plot_aperiodic_fits(aps, [1, 50], save_fig=True, file_path=TEST_PLOTS_PATH, + plot_aperiodic_fits(aps, [1, 50], file_path=TEST_PLOTS_PATH, file_name='test_plot_aperiodic_fits.png') diff --git a/fooof/tests/plts/test_error.py b/specparam/tests/plts/test_error.py similarity index 63% rename from fooof/tests/plts/test_error.py rename to specparam/tests/plts/test_error.py index 3e8b817b..7299aaca 100644 --- a/fooof/tests/plts/test_error.py +++ b/specparam/tests/plts/test_error.py @@ -1,11 +1,11 @@ -"""Tests for fooof.plts.error.""" +"""Tests for specparam.plts.error.""" import numpy as np -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH -from fooof.plts.error import * +from specparam.plts.error import * ################################################################################################### ################################################################################################### @@ -16,5 +16,5 @@ def test_plot_spectral_error(skip_if_no_mpl): fs = np.arange(3, 41, 1) errs = np.ones(len(fs)) - plot_spectral_error(fs, errs, save_fig=True, file_path=TEST_PLOTS_PATH, + plot_spectral_error(fs, errs, file_path=TEST_PLOTS_PATH, file_name='test_plot_spectral_error.png') diff --git a/specparam/tests/plts/test_group.py b/specparam/tests/plts/test_group.py new file mode 100644 index 00000000..9aaf5587 --- /dev/null +++ b/specparam/tests/plts/test_group.py @@ -0,0 +1,43 @@ +"""Tests for specparam.plts.group.""" + +from pytest import raises + +from specparam import SpectralGroupModel +from specparam.core.errors import NoModelError + +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH + +from specparam.plts.group import * + +################################################################################################### +################################################################################################### + +@plot_test +def test_plot_group(tfg, skip_if_no_mpl): + + plot_group(tfg, file_path=TEST_PLOTS_PATH, + file_name='test_plot_group.png') + + # Test error if no data available to plot + tfg = SpectralGroupModel() + with raises(NoModelError): + tfg.plot() + +@plot_test +def test_plot_group_aperiodic(tfg, skip_if_no_mpl): + + plot_group_aperiodic(tfg, file_path=TEST_PLOTS_PATH, + file_name='test_plot_group_aperiodic.png') + +@plot_test +def test_plot_group_goodness(tfg, skip_if_no_mpl): + + plot_group_goodness(tfg, file_path=TEST_PLOTS_PATH, + file_name='test_plot_group_goodness.png') + +@plot_test +def test_plot_group_peak_frequencies(tfg, skip_if_no_mpl): + + plot_group_peak_frequencies(tfg, file_path=TEST_PLOTS_PATH, + file_name='test_plot_group_peak_frequencies.png') diff --git a/fooof/tests/plts/test_fm.py b/specparam/tests/plts/test_model.py similarity index 50% rename from fooof/tests/plts/test_fm.py rename to specparam/tests/plts/test_model.py index d650f84c..e3c330b7 100644 --- a/fooof/tests/plts/test_fm.py +++ b/specparam/tests/plts/test_model.py @@ -1,26 +1,25 @@ -"""Tests for fooof.plts.fm.""" +"""Tests for specparam.plts.model.""" import numpy as np -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH -from fooof.plts.fm import * +from specparam.plts.model import * ################################################################################################### ################################################################################################### @plot_test -def test_plot_fm(tfm, skip_if_no_mpl): +def test_plot_model(tfm, skip_if_no_mpl): # Make sure model has been fit tfm.fit() - plot_fm(tfm, save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_fm.png') + plot_model(tfm, file_path=TEST_PLOTS_PATH, file_name='test_plot_model.png') @plot_test -def test_plot_fm_custom(tfm, skip_if_no_mpl): +def test_plot_model_custom(tfm, skip_if_no_mpl): # Extract broader range of data available in the object custom_freqs = tfm.freqs @@ -29,18 +28,17 @@ def test_plot_fm_custom(tfm, skip_if_no_mpl): # Make sure model has been fit - set custom frequency range tfm.fit(custom_freqs, custom_power_spectrum, freq_range=[5, 35]) - plot_fm(tfm, freqs=custom_freqs, power_spectrum=custom_power_spectrum, - freq_range=[1, 55], save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_fm_custom.png') + plot_model(tfm, freqs=custom_freqs, power_spectrum=custom_power_spectrum, + freq_range=[1, 55], save_fig=True, file_path=TEST_PLOTS_PATH, + file_name='test_plot_fm_custom.png') @plot_test -def test_plot_fm_add_peaks(tfm, skip_if_no_mpl): +def test_plot_model_add_peaks(tfm, skip_if_no_mpl): # Make sure model has been fit tfm.fit() # Test run each of the add peak approaches for add_peak in ['shade', 'dot', 'outline', 'line', 'shade-dot', 'outline-line']: - file_name = 'test_plot_fm_add_peaks_' + add_peak + '.png' - plot_fm(tfm, plot_peaks=add_peak, save_fig=True, - file_path=TEST_PLOTS_PATH, file_name=file_name) + file_name = 'test_plot_model_add_peaks_' + add_peak + '.png' + plot_model(tfm, plot_peaks=add_peak, file_path=TEST_PLOTS_PATH, file_name=file_name) diff --git a/fooof/tests/plts/test_periodic.py b/specparam/tests/plts/test_periodic.py similarity index 71% rename from fooof/tests/plts/test_periodic.py rename to specparam/tests/plts/test_periodic.py index 83e77daf..46d120a6 100644 --- a/fooof/tests/plts/test_periodic.py +++ b/specparam/tests/plts/test_periodic.py @@ -1,11 +1,11 @@ -"""Tests for fooof.plts.periodic.""" +"""Tests for specparam.plts.periodic.""" import numpy as np -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH -from fooof.plts.periodic import * +from specparam.plts.periodic import * ################################################################################################### ################################################################################################### @@ -19,7 +19,7 @@ def test_plot_peak_params(skip_if_no_mpl): plot_peak_params(peaks) # Test with multiple set of params - plot_peak_params([peaks, peaks], save_fig=True, file_path=TEST_PLOTS_PATH, + plot_peak_params([peaks, peaks], file_path=TEST_PLOTS_PATH, file_name='test_plot_peak_params.png') @plot_test @@ -31,5 +31,5 @@ def test_plot_peak_fits(skip_if_no_mpl): plot_peak_fits(peaks) # Test with multiple set of params - plot_peak_fits([peaks, peaks], save_fig=True, file_path=TEST_PLOTS_PATH, + plot_peak_fits([peaks, peaks], file_path=TEST_PLOTS_PATH, file_name='test_plot_peak_fits.png') diff --git a/fooof/tests/plts/test_spectra.py b/specparam/tests/plts/test_spectra.py similarity index 66% rename from fooof/tests/plts/test_spectra.py rename to specparam/tests/plts/test_spectra.py index 6a181340..87c1ccbb 100644 --- a/fooof/tests/plts/test_spectra.py +++ b/specparam/tests/plts/test_spectra.py @@ -1,13 +1,13 @@ -"""Tests for fooof.plts.spectra.""" +"""Tests for specparam.plts.spectra.""" from pytest import raises import numpy as np -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH -from fooof.plts.spectra import * +from specparam.plts.spectra import * ################################################################################################### ################################################################################################### @@ -17,45 +17,45 @@ def test_plot_spectra(tfm, tfg, skip_if_no_mpl): # Test with 1d inputs - 1d freq array & list of 1d power spectra plot_spectra(tfm.freqs, tfm.power_spectrum, - save_fig=True, file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_1d.png') + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_1d.png') # Test with 1d inputs - 1d freq array & list of 1d power spectra plot_spectra(tfg.freqs, [tfg.power_spectra[0, :], tfg.power_spectra[1, :]], - save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_spectra_list_1d.png') + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_list_1d.png') + + # Test with multiple freq inputs - list of 1d freq array and list of 1d power spectra + plot_spectra([tfg.freqs, tfg.freqs], [tfg.power_spectra[0, :], tfg.power_spectra[1, :]], + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_list_1d_freqs.png') # Test with multiple lists - list of 1d freqs & list of 1d power spectra (different f ranges) plot_spectra([tfg.freqs, tfg.freqs[:-5]], [tfg.power_spectra[0, :], tfg.power_spectra[1, :-5]], - save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_spectra_lists_1d.png') + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_lists_1d.png') # Test with 2d array inputs plot_spectra(np.vstack([tfg.freqs, tfg.freqs]), np.vstack([tfg.power_spectra[0, :], tfg.power_spectra[1, :]]), - save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_spectra_2d.png') + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_2d.png') # Test with labels plot_spectra(tfg.freqs, [tfg.power_spectra[0, :], tfg.power_spectra[1, :]], labels=['A', 'B'], - save_fig=True, file_path=TEST_PLOTS_PATH, - file_name='test_plot_spectra_labels.png') + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_labels.png') @plot_test def test_plot_spectra_shading(tfm, tfg, skip_if_no_mpl): plot_spectra_shading(tfm.freqs, tfm.power_spectrum, shades=[8, 12], add_center=True, - save_fig=True, file_path=TEST_PLOTS_PATH, + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectrum_shading1.png') plot_spectra_shading(tfg.freqs, [tfg.power_spectra[0, :], tfg.power_spectra[1, :]], - shades=[8, 12], add_center=True, save_fig=True, file_path=TEST_PLOTS_PATH, + shades=[8, 12], add_center=True, file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_shading2.png') # Test with **kwargs that pass into plot_spectra plot_spectra_shading(tfg.freqs, [tfg.power_spectra[0, :], tfg.power_spectra[1, :]], shades=[8, 12], add_center=True, log_freqs=True, log_powers=True, - labels=['A', 'B'], save_fig=True, file_path=TEST_PLOTS_PATH, + labels=['A', 'B'], file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_shading_kwargs.png') @plot_test @@ -70,24 +70,24 @@ def test_plot_spectra_yshade(skip_if_no_mpl, tfg): # Plot with 2d array plot_spectra_yshade(freqs, powers, shade='std', - save_fig=True, file_path=TEST_PLOTS_PATH, + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_yshade1.png') # Plot shade with given 1d array plot_spectra_yshade(freqs, np.mean(powers, axis=0), shade=np.std(powers, axis=0), - save_fig=True, file_path=TEST_PLOTS_PATH, + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_yshade2.png') # Plot shade with different average and shade approaches plot_spectra_yshade(freqs, powers, shade='sem', average='median', - save_fig=True, file_path=TEST_PLOTS_PATH, + file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_yshade3.png') # Plot shade with custom average and shade callables def _average_callable(powers): return np.mean(powers, axis=0) def _shade_callable(powers): return np.std(powers, axis=0) - plot_spectra_yshade(freqs, powers, shade=_shade_callable, average=_average_callable, - log_powers=True, save_fig=True, file_path=TEST_PLOTS_PATH, + plot_spectra_yshade(freqs, powers, shade=_shade_callable, average=_average_callable, + log_powers=True, file_path=TEST_PLOTS_PATH, file_name='test_plot_spectra_yshade4.png') diff --git a/fooof/tests/plts/test_styles.py b/specparam/tests/plts/test_styles.py similarity index 93% rename from fooof/tests/plts/test_styles.py rename to specparam/tests/plts/test_styles.py index 9ac25c75..a4c32420 100644 --- a/fooof/tests/plts/test_styles.py +++ b/specparam/tests/plts/test_styles.py @@ -1,7 +1,7 @@ -"""Tests for fooof.plts.styles.""" +"""Tests for specparam.plts.styles.""" -from fooof.tests.tutils import plot_test -from fooof.plts.style import * +from specparam.tests.tutils import plot_test +from specparam.plts.style import * ################################################################################################### ################################################################################################### @@ -13,7 +13,7 @@ def test_check_style_options(): def test_style_spectrum_plot(skip_if_no_mpl): # Create a dummy plot and style it - from fooof.core.modutils import safe_import + from specparam.core.modutils import safe_import plt = safe_import('.pyplot', 'matplotlib') _, ax = plt.subplots() style_spectrum_plot(ax, False, False) diff --git a/fooof/tests/plts/test_templates.py b/specparam/tests/plts/test_templates.py similarity index 84% rename from fooof/tests/plts/test_templates.py rename to specparam/tests/plts/test_templates.py index f2faa680..0cf5d687 100644 --- a/fooof/tests/plts/test_templates.py +++ b/specparam/tests/plts/test_templates.py @@ -1,10 +1,10 @@ -"""Tests for fooof.plts.templates.""" +"""Tests for specparam.plts.templates.""" import numpy as np -from fooof.tests.tutils import plot_test +from specparam.tests.tutils import plot_test -from fooof.plts.templates import * +from specparam.plts.templates import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/plts/test_utils.py b/specparam/tests/plts/test_utils.py similarity index 93% rename from fooof/tests/plts/test_utils.py rename to specparam/tests/plts/test_utils.py index 90d4687f..816508fa 100644 --- a/fooof/tests/plts/test_utils.py +++ b/specparam/tests/plts/test_utils.py @@ -1,13 +1,13 @@ -"""Tests for fooof.plts.utils.""" +"""Tests for specparam.plts.utils.""" import os -from fooof.core.modutils import safe_import +from specparam.core.modutils import safe_import -from fooof.tests.tutils import plot_test -from fooof.tests.settings import TEST_PLOTS_PATH +from specparam.tests.tutils import plot_test +from specparam.tests.settings import TEST_PLOTS_PATH -from fooof.plts.utils import * +from specparam.plts.utils import * mpl = safe_import('matplotlib') diff --git a/fooof/tests/settings.py b/specparam/tests/settings.py similarity index 90% rename from fooof/tests/settings.py rename to specparam/tests/settings.py index 74b6c67f..8f6e468c 100644 --- a/fooof/tests/settings.py +++ b/specparam/tests/settings.py @@ -1,4 +1,4 @@ -"""Settings for testing fooof.""" +"""Settings for testing spectral parameterization.""" import os from pathlib import Path diff --git a/fooof/tests/sim/__init__.py b/specparam/tests/sim/__init__.py similarity index 100% rename from fooof/tests/sim/__init__.py rename to specparam/tests/sim/__init__.py diff --git a/specparam/tests/sim/test_gen.py b/specparam/tests/sim/test_gen.py new file mode 100644 index 00000000..03db83db --- /dev/null +++ b/specparam/tests/sim/test_gen.py @@ -0,0 +1,94 @@ +"""Test functions for specparam.sim.gen""" + +import numpy as np +from numpy import array_equal + +from specparam.sim.gen import * + +################################################################################################### +################################################################################################### + +def test_gen_freqs(): + + f_range = [3, 40] + fs = 0.5 + + freqs = gen_freqs(f_range, fs) + + assert freqs.min() == f_range[0] + assert freqs.max() == f_range[1] + assert np.mean(np.diff(freqs)) == fs + +def test_gen_aperiodic(): + + xs = gen_freqs([3, 50], 0.5) + + ap_nk = [50, 2] + apv_nk = gen_aperiodic(xs, ap_nk, 'fixed') + assert np.all(apv_nk) + + ap_kn = [50, 1, 1] + apv_kn = gen_aperiodic(xs, ap_kn, 'knee') + assert np.all(apv_kn) + + # Check without specifying aperiodic mode + apv_nk_2 = gen_aperiodic(xs, ap_nk) + assert np.array_equal(apv_nk, apv_nk_2) + apv_kn_2 = gen_aperiodic(xs, ap_kn) + assert np.array_equal(apv_kn, apv_kn_2) + +def test_gen_periodic(): + + xs = gen_freqs([3, 50], 0.5) + pe_params = [10, 2, 1] + + pe_vals = gen_periodic(xs, pe_params) + + assert np.all(np.invert(np.isnan(pe_vals))) + assert xs[np.argmax(pe_vals)] == 10 + +def test_gen_noise(): + + xs = gen_freqs([3, 50], 0.5) + + nlv = 0.1 + noise = gen_noise(xs, nlv) + assert np.all(np.invert(np.isnan(noise))) + assert np.isclose(np.std(noise), nlv, 0.25) + + nlv = 0.5 + noise = gen_noise(xs, nlv) + assert np.all(np.invert(np.isnan(noise))) + assert np.isclose(np.std(noise), nlv, 0.25) + +def test_gen_power_values(): + + xs = gen_freqs([3, 50], 0.5) + + ap_params = [50, 2] + pe_params = [10, 2, 1] + nlv = 0.1 + + ys = gen_power_vals(xs, ap_params, pe_params, nlv) + + assert np.all(ys) + +def test_gen_rotated_power_vals(): + + xs = gen_freqs([3, 50], 0.5) + + ap_params = [50, 2] + pe_params = [10, 2, 1] + nlv = 0.1 + f_rotation = 12 + + ys = gen_rotated_power_vals(xs, ap_params, pe_params, nlv, f_rotation) + + assert np.all(ys) + +def test_gen_model(): + + xs = gen_freqs([3, 50], 0.5) + ys = gen_model(xs, np.array([1, 1]), np.array([10, 0.5, 1])) + + assert np.all(ys) diff --git a/fooof/tests/sim/test_params.py b/specparam/tests/sim/test_params.py similarity index 96% rename from fooof/tests/sim/test_params.py rename to specparam/tests/sim/test_params.py index c8ccb6ff..e8e50dc1 100644 --- a/fooof/tests/sim/test_params.py +++ b/specparam/tests/sim/test_params.py @@ -1,12 +1,12 @@ -"""Test functions for fooof.sim.params.""" +"""Test functions for specparam.sim.params.""" from pytest import raises from numpy import array_equal -from fooof.core.errors import InconsistentDataError +from specparam.core.errors import InconsistentDataError -from fooof.sim.params import * +from specparam.sim.params import * ################################################################################################### ################################################################################################### diff --git a/specparam/tests/sim/test_sim.py b/specparam/tests/sim/test_sim.py new file mode 100644 index 00000000..be6d70f8 --- /dev/null +++ b/specparam/tests/sim/test_sim.py @@ -0,0 +1,87 @@ +"""Test functions for specparam.sim.sim""" + +import numpy as np +from numpy import array_equal + +from specparam.tests.tutils import default_group_params + +from specparam.sim.sim import * + +################################################################################################### +################################################################################################### + +def test_sim_power_spectrum(): + + freq_range = [3, 50] + ap_params = [50, 2] + pe_params = [10, 0.5, 2, 20, 0.3, 4] + + xs, ys = sim_power_spectrum(freq_range, ap_params, pe_params) + + assert np.all(xs) + assert np.all(ys) + assert len(xs) == len(ys) + + # Test with a rotation applied returned + f_rotation = 20 + xs, ys = sim_power_spectrum(freq_range, ap_params, pe_params, f_rotation=f_rotation) + + assert np.all(xs) + assert np.all(ys) + assert len(xs) == len(ys) + +def test_sim_power_spectrum_return_params(): + + freq_range = [3, 50] + ap_params = [50, 2] + pe_params = [[10, 0.5, 2], [20, 0.3, 4]] + nlv = 0.01 + + xs, ys, sp = sim_power_spectrum(freq_range, ap_params, pe_params, + nlv, return_params=True) + + # Test returning parameters + assert array_equal(sp.aperiodic_params, ap_params) + assert array_equal(sp.periodic_params, pe_params) + assert sp.nlv == nlv + +def test_sim_group_power_spectra(): + + n_spectra = 3 + + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params()) + + assert np.all(xs) + assert np.all(ys) + assert ys.ndim == 2 + assert ys.shape[0] == n_spectra + + # Test the case in which periodic params are an empty list + xs, ys = sim_group_power_spectra(2, [3, 50], [1, 1], []) + + assert np.all(xs) + assert np.all(ys) + + # Test with a rotation applied returned + f_rotation = 20 + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params(), f_rotation=f_rotation) + + assert np.all(xs) + assert np.all(ys) + +def test_sim_group_power_spectra_return_params(): + + n_spectra = 3 + + aps = [1, 1] + pes = [10, 0.5, 1] + nlv = 0.01 + + xs, ys, sim_params = sim_group_power_spectra(n_spectra, [1, 50], aps, pes, nlv, + return_params=True) + + assert n_spectra == ys.shape[0] == len(sim_params) + sp = sim_params[0] + assert array_equal(sp.aperiodic_params, aps) + assert array_equal(sp.periodic_params, [pes]) + assert sp.nlv == nlv diff --git a/fooof/tests/sim/test_transform.py b/specparam/tests/sim/test_transform.py similarity index 83% rename from fooof/tests/sim/test_transform.py rename to specparam/tests/sim/test_transform.py index 648c9663..36fbe270 100644 --- a/fooof/tests/sim/test_transform.py +++ b/specparam/tests/sim/test_transform.py @@ -1,11 +1,11 @@ -"""Test functions for fooof.sim.transform""" +"""Test functions for specparam.sim.transform""" import numpy as np -from fooof.sim.gen import gen_power_spectrum -from fooof.sim.params import SimParams +from specparam.sim import sim_power_spectrum +from specparam.sim.params import SimParams -from fooof.sim.transform import * +from specparam.sim.transform import * ################################################################################################### ################################################################################################### @@ -13,7 +13,7 @@ def test_rotate_spectrum(): # Create a spectrum to use for test rotations - freqs, spectrum = gen_power_spectrum([1, 100], [1, 1], []) + freqs, spectrum = sim_power_spectrum([1, 100], [1, 1], []) # Check that rotation transforms the power spectrum rotated_spectrum = rotate_spectrum(freqs, spectrum, delta_exponent=0.5, f_rotation=25.) @@ -26,7 +26,7 @@ def test_rotate_spectrum(): def test_translate_spectrum(): # Create a spectrum to use for test translation - freqs, spectrum = gen_power_spectrum([1, 100], [1, 1], []) + freqs, spectrum = sim_power_spectrum([1, 100], [1, 1], []) # Check that translation transforms the power spectrum translated_spectrum = translate_spectrum(spectrum, delta_offset=1.) @@ -39,7 +39,7 @@ def test_translate_spectrum(): def test_rotate_sim_spectrum(): sim_params = SimParams([1, 1], [10, 0.5, 1], 0) - freqs, spectrum = gen_power_spectrum([3, 40], *sim_params) + freqs, spectrum = sim_power_spectrum([3, 40], *sim_params) rotated_spectrum, new_sim_params = rotate_sim_spectrum(freqs, spectrum, 0.5, 20, sim_params) @@ -49,7 +49,7 @@ def test_rotate_sim_spectrum(): def test_translate_sim_spectrum(): sim_params = SimParams([1, 1], [10, 0.5, 1], 0) - freqs, spectrum = gen_power_spectrum([3, 40], *sim_params) + freqs, spectrum = sim_power_spectrum([3, 40], *sim_params) translated_spectrum, new_sim_params = translate_sim_spectrum(spectrum, 0.5, sim_params) assert not np.all(translated_spectrum == spectrum) diff --git a/fooof/tests/sim/test_utils.py b/specparam/tests/sim/test_utils.py similarity index 76% rename from fooof/tests/sim/test_utils.py rename to specparam/tests/sim/test_utils.py index 808f2596..ee6d45be 100644 --- a/fooof/tests/sim/test_utils.py +++ b/specparam/tests/sim/test_utils.py @@ -1,6 +1,6 @@ -"""Test functions for fooof.sim.utils.""" +"""Test functions for specparam.sim.utils.""" -from fooof.sim.utils import * +from specparam.sim.utils import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/tutils.py b/specparam/tests/tutils.py similarity index 62% rename from fooof/tests/tutils.py rename to specparam/tests/tutils.py index 04a56ae1..9d571d52 100644 --- a/fooof/tests/tutils.py +++ b/specparam/tests/tutils.py @@ -1,15 +1,15 @@ -"""Utilities for testing fooof.""" +"""Utilities for testing spectral parameterization.""" from functools import wraps import numpy as np -from fooof.bands import Bands -from fooof.data import FOOOFResults -from fooof.objs import FOOOF, FOOOFGroup -from fooof.core.modutils import safe_import -from fooof.sim.params import param_sampler -from fooof.sim.gen import gen_power_spectrum, gen_group_power_spectra +from specparam.bands import Bands +from specparam.data import FitResults +from specparam.objs import SpectralModel, SpectralGroupModel +from specparam.core.modutils import safe_import +from specparam.sim.params import param_sampler +from specparam.sim.sim import sim_power_spectrum, sim_group_power_spectra plt = safe_import('.pyplot', 'matplotlib') @@ -17,26 +17,26 @@ ################################################################################################### def get_tfm(): - """Get a FOOOF object, with a fit power spectrum, for testing.""" + """Get a model object, with a fit power spectrum, for testing.""" freq_range = [3, 50] ap_params = [50, 2] gaussian_params = [10, 0.5, 2, 20, 0.3, 4] - xs, ys = gen_power_spectrum(freq_range, ap_params, gaussian_params) + xs, ys = sim_power_spectrum(freq_range, ap_params, gaussian_params) - tfm = FOOOF(verbose=False) + tfm = SpectralModel(verbose=False) tfm.fit(xs, ys) return tfm def get_tfg(): - """Get a FOOOFGroup object, with some fit power spectra, for testing.""" + """Get a group object, with some fit power spectra, for testing.""" n_spectra = 3 - xs, ys = gen_group_power_spectra(n_spectra, *default_group_params()) + xs, ys = sim_group_power_spectra(n_spectra, *default_group_params()) - tfg = FOOOFGroup(verbose=False) + tfg = SpectralGroupModel(verbose=False) tfg.fit(xs, ys) return tfg @@ -47,12 +47,12 @@ def get_tbands(): return Bands({'theta' : (4, 8), 'alpha' : (8, 12), 'beta' : (13, 30)}) def get_tresults(): - """Get a FOOOFResults objet, for testing.""" + """Get a FitResults object, for testing.""" - return FOOOFResults(aperiodic_params=np.array([1.0, 1.00]), - peak_params=np.array([[10.0, 1.25, 2.0], [20.0, 1.0, 3.0]]), - r_squared=0.97, error=0.01, - gaussian_params=np.array([[10.0, 1.25, 1.0], [20.0, 1.0, 1.5]])) + return FitResults(aperiodic_params=np.array([1.0, 1.00]), + peak_params=np.array([[10.0, 1.25, 2.0], [20.0, 1.0, 3.0]]), + r_squared=0.97, error=0.01, + gaussian_params=np.array([[10.0, 1.25, 1.0], [20.0, 1.0, 1.5]])) def get_tdocstring(): """Get an example docstring, for testing.""" @@ -76,7 +76,7 @@ def get_tdocstring(): return docstring def default_group_params(): - """Create default parameters for generating a test group of power spectra.""" + """Create default parameters for simulating a test group of power spectra.""" freq_range = [3, 50] ap_opts = param_sampler([[20, 2], [50, 2.5], [35, 1.5]]) diff --git a/fooof/tests/utils/__init__.py b/specparam/tests/utils/__init__.py similarity index 100% rename from fooof/tests/utils/__init__.py rename to specparam/tests/utils/__init__.py diff --git a/fooof/tests/utils/test_data.py b/specparam/tests/utils/test_data.py similarity index 89% rename from fooof/tests/utils/test_data.py rename to specparam/tests/utils/test_data.py index f27cd9bd..2d54c174 100644 --- a/fooof/tests/utils/test_data.py +++ b/specparam/tests/utils/test_data.py @@ -1,10 +1,10 @@ -"""Test functions for fooof.utils.data.""" +"""Test functions for specparam.utils.data.""" import numpy as np -from fooof.sim.gen import gen_power_spectrum, gen_group_power_spectra +from specparam.sim import sim_power_spectrum, sim_group_power_spectra -from fooof.utils.data import * +from specparam.utils.data import * ################################################################################################### ################################################################################################### @@ -22,7 +22,7 @@ def test_trim_spectrum(): def test_interpolate_spectrum(): # Test with single buffer exclusion zone - freqs, powers = gen_power_spectrum(\ + freqs, powers = sim_power_spectrum(\ [1, 75], [1, 1], [[10, 0.5, 1.0], [60, 2, 0.1]]) exclude = [58, 62] @@ -36,7 +36,7 @@ def test_interpolate_spectrum(): assert powers[mask].sum() > powers_out[mask].sum() # Test with multiple buffer exclusion zones - freqs, powers = gen_power_spectrum(\ + freqs, powers = sim_power_spectrum(\ [1, 150], [1, 100, 1], [[10, 0.5, 1.0], [60, 1, 0.1], [120, 0.5, 0.1]]) exclude = [[58, 62], [118, 122]] @@ -52,7 +52,7 @@ def test_interpolate_spectrum(): def test_interpolate_spectra(): - freqs, powers = gen_group_power_spectra(\ + freqs, powers = sim_group_power_spectra(\ 5, [1, 150], [1, 100, 1], [[10, 0.5, 1.0], [60, 1, 0.1], [120, 0.5, 0.1]]) exclude = [[58, 62], [118, 122]] @@ -70,8 +70,7 @@ def test_subsample_spectra(): # Simulate spectra, each with unique osc peak (for checking) n_sim = 10 oscs = [[10 + ind, 0.25, 0.5] for ind in range(n_sim)] - freqs, powers = gen_group_power_spectra(\ - n_sim, [1, 50], [1, 1], oscs) + freqs, powers = sim_group_power_spectra(n_sim, [1, 50], [1, 1], oscs) # Test with int input n_select = 2 diff --git a/fooof/tests/utils/test_debug.py b/specparam/tests/utils/test_debug.py similarity index 74% rename from fooof/tests/utils/test_debug.py rename to specparam/tests/utils/test_debug.py index 46de3c1b..1d169fc9 100644 --- a/fooof/tests/utils/test_debug.py +++ b/specparam/tests/utils/test_debug.py @@ -1,6 +1,6 @@ -"""Test functions for fooof.utils.debug""" +"""Test functions for specparam.utils.debug""" -from fooof.utils.debug import * +from specparam.utils.debug import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/utils/test_download.py b/specparam/tests/utils/test_download.py similarity index 79% rename from fooof/tests/utils/test_download.py rename to specparam/tests/utils/test_download.py index dd4f86f4..c24962dc 100644 --- a/fooof/tests/utils/test_download.py +++ b/specparam/tests/utils/test_download.py @@ -1,11 +1,11 @@ -"""Test functions for fooof.utils.download.""" +"""Test functions for specparam.utils.download.""" import os import shutil import numpy as np -from fooof.utils.download import * +from specparam.utils.download import * ################################################################################################### ################################################################################################### @@ -31,20 +31,20 @@ def test_check_data_file(): check_data_file(filename, TEST_FOLDER) assert os.path.isfile(os.path.join(TEST_FOLDER, filename)) -def test_fetch_fooof_data(): +def test_fetch_example_data(): filename = 'spectrum.npy' - fetch_fooof_data(filename, folder=TEST_FOLDER) + fetch_example_data(filename, folder=TEST_FOLDER) assert os.path.isfile(os.path.join(TEST_FOLDER, filename)) clean_up_downloads() -def test_load_fooof_data(): +def test_load_example_data(): filename = 'freqs.npy' - data = load_fooof_data(filename, folder=TEST_FOLDER) + data = load_example_data(filename, folder=TEST_FOLDER) assert isinstance(data, np.ndarray) clean_up_downloads() diff --git a/fooof/tests/utils/test_io.py b/specparam/tests/utils/test_io.py similarity index 64% rename from fooof/tests/utils/test_io.py rename to specparam/tests/utils/test_io.py index 83c5a76c..fc602e0f 100644 --- a/fooof/tests/utils/test_io.py +++ b/specparam/tests/utils/test_io.py @@ -1,24 +1,24 @@ -"""Test functions for fooof.utils.io.""" +"""Test functions for specparam.utils.io.""" import numpy as np -from fooof.core.items import OBJ_DESC -from fooof.objs import FOOOF, FOOOFGroup +from specparam.core.items import OBJ_DESC +from specparam.objs import SpectralModel, SpectralGroupModel -from fooof.tests.settings import TEST_DATA_PATH +from specparam.tests.settings import TEST_DATA_PATH -from fooof.utils.io import * +from specparam.utils.io import * ################################################################################################### ################################################################################################### -def test_load_fooof(): +def test_load_model(): - file_name = 'test_fooof_all' + file_name = 'test_all' - tfm = load_fooof(file_name, TEST_DATA_PATH) + tfm = load_model(file_name, TEST_DATA_PATH) - assert isinstance(tfm, FOOOF) + assert isinstance(tfm, SpectralModel) # Check that all elements get loaded for result in OBJ_DESC['results']: @@ -30,12 +30,12 @@ def test_load_fooof(): for meta_dat in OBJ_DESC['meta_data']: assert getattr(tfm, meta_dat) is not None -def test_load_fooofgroup(): +def test_load_group(): - file_name = 'test_fooofgroup_all' - tfg = load_fooofgroup(file_name, TEST_DATA_PATH) + file_name = 'test_group_all' + tfg = load_group(file_name, TEST_DATA_PATH) - assert isinstance(tfg, FOOOFGroup) + assert isinstance(tfg, SpectralGroupModel) # Check that all elements get loaded assert len(tfg.group_results) > 0 diff --git a/fooof/tests/utils/test_params.py b/specparam/tests/utils/test_params.py similarity index 85% rename from fooof/tests/utils/test_params.py rename to specparam/tests/utils/test_params.py index b8de80e8..6e7bf3fe 100644 --- a/fooof/tests/utils/test_params.py +++ b/specparam/tests/utils/test_params.py @@ -1,8 +1,8 @@ -"""Test functions for fooof.utils.params.""" +"""Test functions for specparam.utils.params.""" import numpy as np -from fooof.utils.params import * +from specparam.utils.params import * ################################################################################################### ################################################################################################### diff --git a/fooof/tests/utils/test_reports.py b/specparam/tests/utils/test_reports.py similarity index 66% rename from fooof/tests/utils/test_reports.py rename to specparam/tests/utils/test_reports.py index 22bff559..0723ea09 100644 --- a/fooof/tests/utils/test_reports.py +++ b/specparam/tests/utils/test_reports.py @@ -1,18 +1,18 @@ -"""Test functions for fooof.utils.reports""" +"""Test functions for specparam.utils.reports""" -from fooof.utils.reports import * +from specparam.utils.reports import * ################################################################################################### ################################################################################################### def test_methods_report_info(tfm): - # Test with and without passing in a FOOOF object + # Test with and without passing in a model object methods_report_info() methods_report_info(tfm) def test_methods_report_text(tfm): - # Test with and without passing in a FOOOF object + # Test with and without passing in a model object methods_report_text() methods_report_text(tfm) diff --git a/fooof/utils/__init__.py b/specparam/utils/__init__.py similarity index 71% rename from fooof/utils/__init__.py rename to specparam/utils/__init__.py index d0b74f08..ebef0de8 100644 --- a/fooof/utils/__init__.py +++ b/specparam/utils/__init__.py @@ -1,3 +1,3 @@ -"""Utilities sub-module for FOOOF.""" +"""Utilities sub-module.""" from .data import trim_spectrum, interpolate_spectrum, interpolate_spectra, subsample_spectra diff --git a/fooof/utils/data.py b/specparam/utils/data.py similarity index 92% rename from fooof/utils/data.py rename to specparam/utils/data.py index d64cd09a..e30ad206 100644 --- a/fooof/utils/data.py +++ b/specparam/utils/data.py @@ -5,7 +5,7 @@ import numpy as np -from fooof.core.modutils import docs_get_section, replace_docstring_sections +from specparam.core.modutils import docs_get_section, replace_docstring_sections ################################################################################################### ################################################################################################### @@ -39,8 +39,8 @@ def trim_spectrum(freqs, power_spectra, f_range): -------- Using a simulated spectrum, extract a frequency range: - >>> from fooof.sim import gen_power_spectrum - >>> freqs, powers = gen_power_spectrum([1, 50], [1, 1], [10, 0.5, 1.0]) + >>> from specparam.sim import sim_power_spectrum + >>> freqs, powers = sim_power_spectrum([1, 50], [1, 1], [10, 0.5, 1.0]) >>> freqs, powers = trim_spectrum(freqs, powers, [3, 30]) """ @@ -102,8 +102,8 @@ def interpolate_spectrum(freqs, powers, interp_range, buffer=3): -------- Using a simulated spectrum, interpolate away a line noise peak: - >>> from fooof.sim import gen_power_spectrum - >>> freqs, powers = gen_power_spectrum([1, 75], [1, 1], [[10, 0.5, 1.0], [60, 2, 0.1]]) + >>> from specparam.sim import sim_power_spectrum + >>> freqs, powers = sim_power_spectrum([1, 75], [1, 1], [[10, 0.5, 1.0], [60, 2, 0.1]]) >>> freqs, powers = interpolate_spectrum(freqs, powers, [58, 62]) """ @@ -178,8 +178,8 @@ def interpolate_spectra(freqs, powers, interp_range, buffer=3): -------- Using simulated spectra, interpolate away line noise peaks: - >>> from fooof.sim import gen_group_power_spectra - >>> freqs, powers = gen_group_power_spectra(5, [1, 75], [1, 1], [[10, 0.5, 1.0], [60, 2, 0.1]]) + >>> from specparam.sim import sim_group_power_spectra + >>> freqs, powers = sim_group_power_spectra(5, [1, 75], [1, 1], [[10, 0.5, 1.0], [60, 2, 0.1]]) >>> freqs, powers = interpolate_spectra(freqs, powers, [58, 62]) """ @@ -215,14 +215,14 @@ def subsample_spectra(spectra, selection, return_inds=False): -------- Using a group of simulated spectra, subsample a specific number: - >>> from fooof.sim import gen_group_power_spectra - >>> freqs, powers = gen_group_power_spectra(10, [1, 50], [1, 1], [10, 0.5, 1.0]) + >>> from specparam.sim import sim_group_power_spectra + >>> freqs, powers = sim_group_power_spectra(10, [1, 50], [1, 1], [10, 0.5, 1.0]) >>> subsample = subsample_spectra(powers, 5) Using a group of simulated spectra, subsample a proportion: - >>> from fooof.sim import gen_group_power_spectra - >>> freqs, powers = gen_group_power_spectra(10, [1, 50], [1, 1], [10, 0.5, 1.0]) + >>> from specparam.sim import sim_group_power_spectra + >>> freqs, powers = sim_group_power_spectra(10, [1, 50], [1, 1], [10, 0.5, 1.0]) >>> subsample = subsample_spectra(powers, 0.25) """ diff --git a/fooof/utils/debug.py b/specparam/utils/debug.py similarity index 100% rename from fooof/utils/debug.py rename to specparam/utils/debug.py diff --git a/fooof/utils/download.py b/specparam/utils/download.py similarity index 87% rename from fooof/utils/download.py rename to specparam/utils/download.py index d5b8a4b2..7ad98532 100644 --- a/fooof/utils/download.py +++ b/specparam/utils/download.py @@ -1,11 +1,11 @@ -"""Functions and utilities for downloading example data for fooof.""" +"""Functions and utilities for downloading example data.""" import os from urllib.request import urlretrieve import numpy as np -from fooof.core.io import fpath +from specparam.core.io import fpath ################################################################################################### ################################################################################################### @@ -44,8 +44,8 @@ def check_data_file(filename, folder, url=DATA_URL): urlretrieve(url + filename, filename=filepath) -def fetch_fooof_data(filename, folder='data', url=DATA_URL): - """Download a data file for FOOOF. +def fetch_example_data(filename, folder='data', url=DATA_URL): + """Download an example data file. Parameters ---------- @@ -66,8 +66,8 @@ def fetch_fooof_data(filename, folder='data', url=DATA_URL): check_data_file(filename, folder, url) -def load_fooof_data(filename, folder='data', url=DATA_URL): - """Download, if not already available, and load an example data file for fooof. +def load_example_data(filename, folder='data', url=DATA_URL): + """Download, if not already available, and load an example data file. Parameters ---------- @@ -88,7 +88,7 @@ def load_fooof_data(filename, folder='data', url=DATA_URL): This function assumes that data files are numpy (npy) files. """ - fetch_fooof_data(filename, folder, url) + fetch_example_data(filename, folder, url) data = np.load(os.path.join(folder, filename)) return data diff --git a/fooof/utils/io.py b/specparam/utils/io.py similarity index 62% rename from fooof/utils/io.py rename to specparam/utils/io.py index e71673a4..450ef5d1 100644 --- a/fooof/utils/io.py +++ b/specparam/utils/io.py @@ -3,8 +3,8 @@ ################################################################################################### ################################################################################################### -def load_fooof(file_name, file_path=None, regenerate=True): - """Load a FOOOF file into a FOOOF object. +def load_model(file_name, file_path=None, regenerate=True): + """Load a model file. Parameters ---------- @@ -17,22 +17,22 @@ def load_fooof(file_name, file_path=None, regenerate=True): Returns ------- - fm : FOOOF + model : SpectralModel Object with the loaded data. """ - # Initialize a FOOOF object (imported locally to avoid circular imports) - from fooof.objs import FOOOF - fm = FOOOF() + # Initialize a model object (imported locally to avoid circular imports) + from specparam.objs import SpectralModel + model = SpectralModel() # Load data into object - fm.load(file_name, file_path, regenerate) + model.load(file_name, file_path, regenerate) - return fm + return model -def load_fooofgroup(file_name, file_path=None): - """Load data from file into a FOOOFGroup object. +def load_group(file_name, file_path=None): + """Load a group file. Parameters ---------- @@ -43,15 +43,15 @@ def load_fooofgroup(file_name, file_path=None): Returns ------- - fg : FOOOFGroup + group : SpectralGroupModel Object with the loaded data. """ - # Initialize a FOOOFGroup object (imported locally to avoid circular imports) - from fooof.objs import FOOOFGroup - fg = FOOOFGroup() + # Initialize a group object (imported locally to avoid circular imports) + from specparam.objs import SpectralGroupModel + group = SpectralGroupModel() # Load data into object - fg.load(file_name, file_path) + group.load(file_name, file_path) - return fg + return group diff --git a/fooof/utils/params.py b/specparam/utils/params.py similarity index 100% rename from fooof/utils/params.py rename to specparam/utils/params.py diff --git a/fooof/utils/reports.py b/specparam/utils/reports.py similarity index 66% rename from fooof/utils/reports.py rename to specparam/utils/reports.py index 90ba8159..b7ce8544 100644 --- a/fooof/utils/reports.py +++ b/specparam/utils/reports.py @@ -1,17 +1,17 @@ """Utilities to create reports and useful print outs.""" -from fooof.core.strings import (gen_version_str, gen_settings_str, gen_freq_range_str, - gen_methods_report_str, gen_methods_text_str) +from specparam.core.strings import (gen_version_str, gen_settings_str, gen_freq_range_str, + gen_methods_report_str, gen_methods_text_str) ################################################################################################### ################################################################################################### -def methods_report_info(fooof_obj=None, concise=False): +def methods_report_info(model_obj=None, concise=False): """Prints out a report of information required for methods reporting. Parameters ---------- - fooof_obj : FOOOF or FOOOFGroup, optional + model_obj : SpectralModel or SpectralGroupModel, optional An object with setting information available. If provided, is used to collect and print information to be reported. concise : bool, optional, default: False @@ -24,17 +24,17 @@ def methods_report_info(fooof_obj=None, concise=False): print(gen_methods_report_str(concise)) - if fooof_obj: + if model_obj: print(gen_version_str(concise)) - print(gen_settings_str(fooof_obj, concise=concise)) - print(gen_freq_range_str(fooof_obj, concise=concise)) + print(gen_settings_str(model_obj, concise=concise)) + print(gen_freq_range_str(model_obj, concise=concise)) -def methods_report_text(fooof_obj=None): +def methods_report_text(model_obj=None): """Prints out a text template of methods reporting information. Parameters ---------- - fooof_obj : FOOOF or FOOOFGroup, optional + model_obj : SpectralModel or SpectralGroupModel, optional An object with setting information available. If None, the text is returned as a template, without values. @@ -43,4 +43,4 @@ def methods_report_text(fooof_obj=None): Any missing values (information that is not available) will be printed out as 'XX'. """ - print(gen_methods_text_str(fooof_obj)) + print(gen_methods_text_str(model_obj)) diff --git a/specparam/version.py b/specparam/version.py new file mode 100644 index 00000000..34f0285d --- /dev/null +++ b/specparam/version.py @@ -0,0 +1 @@ +__version__ = '2.0.0rc0' \ No newline at end of file diff --git a/tutorials/plot_01-ModelDescription.py b/tutorials/plot_01-ModelDescription.py index 36046fab..1ed46627 100644 --- a/tutorials/plot_01-ModelDescription.py +++ b/tutorials/plot_01-ModelDescription.py @@ -16,7 +16,7 @@ # # Keep in mind as you go, that if you want more information that describes, motivates, and # justifies our modeling approach, you can also check out the associated -# `paper `_, +# `paper `_, # and/or the # `motivations `_ # section of the site. @@ -38,28 +38,28 @@ # sphinx_gallery_thumbnail_number = 5 # Import required code for visualizing example models -from fooof import FOOOF -from fooof.sim.gen import gen_power_spectrum -from fooof.sim.utils import set_random_seed -from fooof.plts.spectra import plot_spectra -from fooof.plts.annotate import plot_annotated_model +from specparam import SpectralModel +from specparam.sim import sim_power_spectrum +from specparam.sim.utils import set_random_seed +from specparam.plts.spectra import plot_spectra +from specparam.plts.annotate import plot_annotated_model ################################################################################################### -# Set random seed, for consistency generating simulated data +# Set random seed, for consistency simulating data set_random_seed(21) # Simulate example power spectra -freqs1, powers1 = gen_power_spectrum([3, 40], [1, 1], +freqs1, powers1 = sim_power_spectrum([3, 40], [1, 1], [[10, 0.2, 1.25], [30, 0.15, 2]]) -freqs2, powers2 = gen_power_spectrum([1, 150], [1, 125, 1.25], +freqs2, powers2 = sim_power_spectrum([1, 150], [1, 125, 1.25], [[8, 0.15, 1.], [30, 0.1, 2]]) ################################################################################################### # Initialize power spectrum model objects and fit the power spectra -fm1 = FOOOF(min_peak_height=0.05, verbose=False) -fm2 = FOOOF(min_peak_height=0.05, aperiodic_mode='knee', verbose=False) +fm1 = SpectralModel(min_peak_height=0.05, verbose=False) +fm2 = SpectralModel(min_peak_height=0.05, aperiodic_mode='knee', verbose=False) fm1.fit(freqs1, powers1) fm2.fit(freqs2, powers2) @@ -395,7 +395,7 @@ # `center frequency`, `power` and `bandwidth` of putative periodic activity # # For more technical details on the model formulation and fitting process, check out the -# `paper `_. +# `paper `_. # # In the next tutorial, we will start to use this model. # diff --git a/tutorials/plot_02-FOOOF.py b/tutorials/plot_02-PSDModel.py similarity index 82% rename from tutorials/plot_02-FOOOF.py rename to tutorials/plot_02-PSDModel.py index ac893ba0..c79f2bde 100644 --- a/tutorials/plot_02-FOOOF.py +++ b/tutorials/plot_02-PSDModel.py @@ -2,28 +2,28 @@ 02: Fitting Power Spectrum Models ================================= -Introduction to the module, beginning with the FOOOF object. +Introduction to the module, beginning with the model object. """ ################################################################################################### -# Import the FOOOF object -from fooof import FOOOF +# Import the model object +from specparam import SpectralModel # Import a utility to download and load example data -from fooof.utils.download import load_fooof_data +from specparam.utils.download import load_example_data ################################################################################################### # Download example data files needed for this example -freqs = load_fooof_data('freqs.npy', folder='data') -spectrum = load_fooof_data('spectrum.npy', folder='data') +freqs = load_example_data('freqs.npy', folder='data') +spectrum = load_example_data('spectrum.npy', folder='data') ################################################################################################### -# FOOOF Object +# Model Object # ------------ # -# At the core of the module is the :class:`~fooof.FOOOF` object, which holds relevant data +# At the core of the module is the :class:`~specparam.SpectralModel` object, which holds relevant data # and settings as attributes, and contains methods to run the algorithm to parameterize # neural power spectra. # @@ -38,9 +38,9 @@ # Calculating Power Spectra # ~~~~~~~~~~~~~~~~~~~~~~~~~ # -# The :class:`~fooof.FOOOF` object fits models to power spectra. The module itself does not +# The :class:`~specparam.SpectralModel` object fits models to power spectra. The module itself does not # compute power spectra. Computing power spectra needs to be done prior to using -# the FOOOF module. +# the specparam module. # # The model is broadly agnostic to exactly how power spectra are computed. Common # methods, such as Welch's method, can be used to compute the spectrum. @@ -48,7 +48,7 @@ # If you need a module in Python that has functionality for computing power spectra, try # `NeuroDSP `_. # -# Note that FOOOF objects require frequency and power values passed in as inputs to +# Note that model objects require frequency and power values passed in as inputs to # be in linear spacing. Passing in non-linear spaced data (such logged values) may # produce erroneous results. # @@ -62,8 +62,8 @@ ################################################################################################### -# Initialize a FOOOF object -fm = FOOOF() +# Initialize a model object +fm = SpectralModel() # Set the frequency range to fit the model freq_range = [2, 40] @@ -77,16 +77,16 @@ # # The above method 'report', is a convenience method that calls a series of methods: # -# - :meth:`~fooof.FOOOF.fit`: fits the power spectrum model -# - :meth:`~fooof.FOOOF.print_results`: prints out the results -# - :meth:`~fooof.FOOOF.plot`: plots the data and model fit +# - :meth:`~specparam.SpectralModel.fit`: fits the power spectrum model +# - :meth:`~specparam.SpectralModel.print_results`: prints out the results +# - :meth:`~specparam.SpectralModel.plot`: plots the data and model fit # # Each of these methods can also be called individually. # ################################################################################################### -# Alternatively, just fit the model with FOOOF.fit() (without printing anything) +# Alternatively, just fit the model with SpectralModel.fit() (without printing anything) fm.fit(freqs, spectrum, freq_range) # After fitting, plotting and parameter fitting can be called independently: @@ -112,7 +112,7 @@ ################################################################################################### # -# Access model fit parameters from FOOOF object, after fitting: +# Access model fit parameters from specparam object, after fitting: # ################################################################################################### @@ -135,7 +135,7 @@ # Selecting Parameters # ~~~~~~~~~~~~~~~~~~~~ # -# You can also select parameters using the :meth:`~fooof.FOOOF.get_params` +# You can also select parameters using the :meth:`~specparam.SpectralModel.get_params` # method, which can be used to specify which parameters you want to extract. # @@ -149,14 +149,14 @@ cfs = fm.get_params('peak_params', 'CF') # Print out a custom parameter report -template = ("With an error level of {error:1.2f}, FOOOF fit an exponent " - "of {exponent:1.2f} and peaks of {cfs:s} Hz.") +template = ("With an error level of {error:1.2f}, an exponent " + "of {exponent:1.2f} and peaks of {cfs:s} Hz were fit.") print(template.format(error=err, exponent=exp, cfs=' & '.join(map(str, [round(cf, 2) for cf in cfs])))) ################################################################################################### # -# For a full description of how you can access data with :meth:`~fooof.FOOOF.get_params`, +# For a full description of how you can access data with :meth:`~specparam.SpectralModel.get_params`, # check the method's documentation. # # As a reminder, you can access the documentation for a function using '?' in a @@ -195,7 +195,7 @@ ################################################################################################### # -# The underlying gaussian parameters are also available from the FOOOF object, +# The underlying gaussian parameters are also available from the model object, # in the ``gaussian_params_`` attribute. # @@ -207,23 +207,23 @@ print('{:5.2f} {:5.2f} {:5.2f} \t {:5.2f} {:5.2f} {:5.2f}'.format(*peak, *gauss)) #################################################################################################### -# FOOOFResults -# ~~~~~~~~~~~~ +# FitResults +# ~~~~~~~~~~ # # There is also a convenience method to return all model fit results: -# :func:`~fooof.FOOOF.get_results`. +# :func:`~specparam.SpectralModel.get_results`. # # This method returns all the model fit parameters, including the underlying Gaussian -# parameters, collected together into a FOOOFResults object. +# parameters, collected together into a FitResults object. # -# The FOOOFResults object, which in Python terms is a named tuple, is a standard data -# object used with FOOOF to organize and collect parameter data. +# The FitResults object, which in Python terms is a named tuple, is a standard data +# object used to organize and collect parameter data. # ################################################################################################### # Grab each model fit result with `get_results` to gather all results together -# Note that this returns a FOOOFResult object +# Note that this returns a FitResults object fres = fm.get_results() # You can also unpack all fit parameters when using `get_results` @@ -231,10 +231,10 @@ ################################################################################################### -# Print out the FOOOFResults +# Print out the FitResults print(fres, '\n') -# From FOOOFResults, you can access the different results +# from specparamResults, you can access the different results print('Aperiodic Parameters: \n', fres.aperiodic_params) # Check the R^2 and error of the model fit @@ -245,7 +245,7 @@ # Conclusion # ---------- # -# In this tutorial, we have explored the basics of the :class:`~fooof.FOOOF` object, +# In this tutorial, we have explored the basics of the :class:`~specparam.SpectralModel` object, # fitting power spectrum models, and extracting parameters. # # In the next tutorial, we will explore how this algorithm actually works to fit the model. diff --git a/tutorials/plot_03-FOOOFAlgorithm.py b/tutorials/plot_03-Algorithm.py similarity index 91% rename from tutorials/plot_03-FOOOFAlgorithm.py rename to tutorials/plot_03-Algorithm.py index 5ad2a08c..65d47987 100644 --- a/tutorials/plot_03-FOOOFAlgorithm.py +++ b/tutorials/plot_03-Algorithm.py @@ -34,18 +34,18 @@ # General imports import matplotlib.pyplot as plt -# Import the FOOOF object -from fooof import FOOOF +# Import the model object +from specparam import SpectralModel # Import some internal functions # These are used here to demonstrate the algorithm # You do not need to import these functions for standard usage of the module -from fooof.sim.gen import gen_aperiodic -from fooof.plts.spectra import plot_spectra -from fooof.plts.annotate import plot_annotated_peak_search +from specparam.sim.gen import gen_aperiodic +from specparam.plts.spectra import plot_spectra +from specparam.plts.annotate import plot_annotated_peak_search # Import a utility to download and load example data -from fooof.utils.download import load_fooof_data +from specparam.utils.download import load_example_data ################################################################################################### @@ -55,19 +55,19 @@ ################################################################################################### # Load example data files needed for this example -freqs = load_fooof_data('freqs_2.npy', folder='data') -spectrum = load_fooof_data('spectrum_2.npy', folder='data') +freqs = load_example_data('freqs_2.npy', folder='data') +spectrum = load_example_data('spectrum_2.npy', folder='data') ################################################################################################### -# Initialize a FOOOF object, with some settings +# Initialize a model object, with some settings # These settings will be more fully described later in the tutorials -fm = FOOOF(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.15) +fm = SpectralModel(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.15) ################################################################################################### # -# Note that data can be added to a FOOOF object independent of fitting the model, using the -# :meth:`~fooof.FOOOF.add_data` method. FOOOF objects can also be used to plot data, +# Note that data can be added to a SpectralModel object independent of fitting the model, using the +# :meth:`~specparam.SpectralModel.add_data` method. Model objects can also be used to plot data, # prior to fitting any models. # @@ -83,7 +83,7 @@ ################################################################################################### # -# The FOOOF object stores most of the intermediate steps internally. +# The model object stores most of the intermediate steps internally. # # For this notebook, we will first fit the full model, as normal, but then step through, # and visualize each step the algorithm took to come to that final fit. @@ -105,7 +105,7 @@ ################################################################################################### # Do an initial aperiodic fit - a robust fit, that excludes outliers -# This recreates an initial fit that isn't ultimately stored in the FOOOF object +# This recreates an initial fit that isn't ultimately stored in the model object init_ap_fit = gen_aperiodic(fm.freqs, fm._robust_ap_fit(fm.freqs, fm.power_spectrum)) # Plot the initial aperiodic fit @@ -226,7 +226,7 @@ ################################################################################################### # Plot full model, created by combining the peak and aperiodic fits -plot_spectra(fm.freqs, fm.fooofed_spectrum_, plt_log, +plot_spectra(fm.freqs, fm.modeled_spectrum_, plt_log, label='Full Model', color='red') ################################################################################################### @@ -259,11 +259,11 @@ # Addendum: Data & Model Component Attributes # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # -# As you may have noticed through this tutorial, the :class:`~fooof.FOOOF` object keeps +# As you may have noticed through this tutorial, the :class:`~specparam.SpectralModel` object keeps # track of some versions of the original data as well as individual model components fits, # as well as the final model fit, the ultimate outcome of the fitting procedure. # -# These attributes in the FOOOF object are kept at the end of the fitting procedure. +# These attributes in the SpectralModel object are kept at the end of the fitting procedure. # Though they are primarily computed for internal use (hence being considered 'private' # attributes, with the leading underscore), they are accessible and potentially # useful for some analyses, and so are briefly described here. @@ -300,6 +300,6 @@ # In this tutorial we have stepped through the parameterization algorithm for fitting # power spectrum models. # -# Next, we will continue to explore the FOOOF object by properly introducing and more +# Next, we will continue to explore the model object by properly introducing and more # fully describing the settings for the algorithm. # diff --git a/tutorials/plot_04-MoreFOOOF.py b/tutorials/plot_04-ModelObject.py similarity index 86% rename from tutorials/plot_04-MoreFOOOF.py rename to tutorials/plot_04-ModelObject.py index 8b916fc4..f38c1992 100644 --- a/tutorials/plot_04-MoreFOOOF.py +++ b/tutorials/plot_04-ModelObject.py @@ -1,28 +1,28 @@ """ -04: Exploring the FOOOF Object +04: Exploring the model object ============================== -Further exploring the FOOOF object, including algorithm settings and available methods. +Further exploring the SpectralModel object, including algorithm settings and available methods. """ ################################################################################################### -# Import the FOOOF object -from fooof import FOOOF +# Import the model object +from specparam import SpectralModel # Import utility to download and load example data -from fooof.utils.download import load_fooof_data +from specparam.utils.download import load_example_data ################################################################################################### -# Initialize a FOOOF object -fm = FOOOF() +# Initialize a model object +fm = SpectralModel() ################################################################################################### # Description of methods and attributes # ------------------------------------- # -# The :class:`~fooof.FOOOF` object contents consist of 4 main components (groups of data / code): +# The :class:`~specparam.SpectralModel` object contents consist of 4 main components (groups of data / code): # # - 1) settings attributes, that control the algorithm fitting # - 2) data attributes, that contain and describe the data @@ -31,7 +31,7 @@ # # Each these which are described in more detail below. # -# The FOOOF module follows the following Python conventions: +# The `specparam` module follows the following Python conventions: # # - all user exposed settings, data, and methods are directly accessible through the object # - 'hidden' (internal) settings and methods have a leading underscore @@ -44,7 +44,7 @@ # ^^^^^^^^^^^^^^^^^^^^^^^^ # # There are a number of settings that control the fitting algorithm, that -# can be set by the user when initializing the :class:`~fooof.FOOOF` object. +# can be set by the user when initializing the :class:`~specparam.SpectralModel` object. # # There are some internal settings that are not exposed at initialization. # These settings are unlikely to need to be accessed by the user, but can be if desired - @@ -131,25 +131,25 @@ # ~~~~~~~~~~~~~~~~~ # # Note that if you wish to change settings, then you should re-initialize -# a new :class:`~fooof.FOOOF` object with new settings. +# a new :class:`~specparam.SpectralModel` object with new settings. # # Simply changing the value of the relevant attribute may not appropriately propagate # the value, and thus may lead to a failure, either creating an error, or not applying # the settings properly during fit and returning erroneous results. # -# Here we will re-initialize a new FOOOF object, with some new settings. +# Here we will re-initialize a new SpectralModel object, with some new settings. # ################################################################################################### -# Re-initialize a new FOOOF object, with some new specified settings -fm = FOOOF(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.15) +# Re-initialize a new model object, with some new specified settings +fm = SpectralModel(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.15) ################################################################################################### # 2) Data (attributes) # ^^^^^^^^^^^^^^^^^^^^ # -# The :class:`~fooof.FOOOF` object stores the following data attributes: +# The :class:`~specparam.SpectralModel` object stores the following data attributes: # # - ``freqs``: the frequency values of the power spectrum # - ``power_spectrum``: the power values of the power spectrum @@ -165,8 +165,8 @@ ################################################################################################### # Load example data files needed for this example -freqs = load_fooof_data('freqs_2.npy', folder='data') -spectrum = load_fooof_data('spectrum_2.npy', folder='data') +freqs = load_example_data('freqs_2.npy', folder='data') +spectrum = load_example_data('spectrum_2.npy', folder='data') ################################################################################################### @@ -215,10 +215,10 @@ # # Other attributes which store outputs from the model are: # -# - ``fooofed_spectrum_``: the full model reconstruction +# - ``modeled_spectrum_``: the full model reconstruction # - ``n_peaks_``: a helper attribute which indicates how many peaks were fit in the model # -# The :class:`~fooof.FOOOF` object also has an indicator attribute, ``has_model`` +# The :class:`~specparam.SpectralModel` object also has an indicator attribute, ``has_model`` # which indicates if the current object has model results available. # @@ -234,13 +234,13 @@ print('peak params: \t', fm.peak_params_) print('r-squared: \t', fm.r_squared_) print('fit error: \t', fm.error_) -print('fooofed spectrum: \t', fm.fooofed_spectrum_[0:5]) +print('modeled spectrum: \t', fm.modeled_spectrum_[0:5]) ################################################################################################### # 4) Methods # ^^^^^^^^^^ # -# The :class:`~fooof.FOOOF` object contains a number of methods that are either used +# The :class:`~specparam.SpectralModel` object contains a number of methods that are either used # to fit models and access data, and/or offer extra functionality. # # In addition to the exposed methods, there are some internal private methods, @@ -264,7 +264,7 @@ # # You have the option to specify which data to save. # -# - `results`: model fit results (same as is returned in FOOOFResult) +# - `results`: model fit results (same as is returned in FitResults) # - `settings`: all public settings (everything available at initialization) # - `data`: freqs & power spectrum # @@ -275,13 +275,13 @@ ################################################################################################### # Save out results, settings, and data -fm.save('FOOOF_results', save_results=True, save_settings=True, save_data=True) +fm.save('results', save_results=True, save_settings=True, save_data=True) ################################################################################################### # Load back in the saved out information -nfm = FOOOF() -nfm.load('FOOOF_results') +nfm = SpectralModel() +nfm.load('results') ################################################################################################### @@ -295,21 +295,21 @@ # There is also functionality to save out a 'report' of a particular model fit. # # This generates and saves a PDF which contains the same output as -# :meth:`~fooof.FOOOF.print_results`, -# :meth:`~fooof.FOOOF.plot`, and -# :meth:`~fooof.FOOOF.print_settings`. +# :meth:`~specparam.SpectralModel.print_results`, +# :meth:`~specparam.SpectralModel.plot`, and +# :meth:`~specparam.SpectralModel.print_settings`. # ################################################################################################### # Save out a report of the current model fit & results -fm.save_report('FOOOF_report') +fm.save_report('report') ################################################################################################### # Conclusion # ---------- # -# We have now fully explored the :class:`~fooof.FOOOF` object, and all it contains. +# We have now fully explored the :class:`~specparam.SpectralModel` object, and all it contains. # Next, we will take a deeper dive into how to choose different modes for fitting # the aperiodic component of power spectra. # diff --git a/tutorials/plot_05-AperiodicFitting.py b/tutorials/plot_05-AperiodicFitting.py index 500fbb75..59221b26 100644 --- a/tutorials/plot_05-AperiodicFitting.py +++ b/tutorials/plot_05-AperiodicFitting.py @@ -7,11 +7,11 @@ ################################################################################################### -# Import the FOOOF object -from fooof import FOOOF +# Import the model object +from specparam import SpectralModel # Import a utility to download and load example data -from fooof.utils.download import load_fooof_data +from specparam.utils.download import load_example_data ################################################################################################### # Aperiodic Fitting Approaches @@ -51,13 +51,13 @@ ################################################################################################### # Load example data files needed for this example -freqs = load_fooof_data('freqs_lfp.npy', folder='data') -spectrum = load_fooof_data('spectrum_lfp.npy', folder='data') +freqs = load_example_data('freqs_lfp.npy', folder='data') +spectrum = load_example_data('spectrum_lfp.npy', folder='data') ################################################################################################### -# Initialize a FOOOF object, setting the aperiodic mode to use a 'knee' fit -fm = FOOOF(peak_width_limits=[2, 8], aperiodic_mode='knee') +# Initialize a model object, setting the aperiodic mode to use a 'knee' fit +fm = SpectralModel(peak_width_limits=[2, 8], aperiodic_mode='knee') ################################################################################################### @@ -103,7 +103,7 @@ ################################################################################################### # Create and fit a power spectrum model in fixed mode to the same data as above -fm = FOOOF(peak_width_limits=[2, 8], aperiodic_mode='fixed') +fm = SpectralModel(peak_width_limits=[2, 8], aperiodic_mode='fixed') fm.report(freqs, spectrum, [2, 70], plt_log=True) ################################################################################################### @@ -154,7 +154,7 @@ # Conclusion # ---------- # -# We have now explored the :class:`~fooof.FOOOF` object, and different fitting +# We have now explored the :class:`~specparam.SpectralModel` object, and different fitting # approaches for the aperiodic component. Next up, we will be introducing how # to scale the fitting to apply across multiple power spectra. # diff --git a/tutorials/plot_06-FOOOFGroup.py b/tutorials/plot_06-GroupFits.py similarity index 70% rename from tutorials/plot_06-FOOOFGroup.py rename to tutorials/plot_06-GroupFits.py index a5f6e3f3..31be0b6e 100644 --- a/tutorials/plot_06-FOOOFGroup.py +++ b/tutorials/plot_06-GroupFits.py @@ -1,35 +1,35 @@ """ -06: FOOOFGroup -============== +06: Fitting group of spectra +============================ -Using FOOOFGroup to run fit models across multiple power spectra. +Using the group model object to run fit models across multiple power spectra. """ ################################################################################################### -# Import the FOOOFGroup object -from fooof import FOOOFGroup +# Import the group model object +from specparam import SpectralGroupModel # Import a utility to download and load example data -from fooof.utils.download import load_fooof_data +from specparam.utils.download import load_example_data ################################################################################################### # Fitting Multiple Spectra # ------------------------ # -# So far, we have explored using the :class:`~fooof.FOOOF` object to fit individual power spectra. +# So far, we have explored using the :class:`~specparam.SpectralModel` object to fit individual power spectra. # # However, many potential analyses will including many power spectra that need to be fit. # -# To support this, here we will introduce the :class:`~fooof.FOOOFGroup` object, which +# To support this, here we will introduce the :class:`~specparam.SpectralGroupModel` object, which # applies the model fitting procedure across multiple power spectra. # ################################################################################################### # Load examples data files needed for this example -freqs = load_fooof_data('group_freqs.npy', folder='data') -spectra = load_fooof_data('group_powers.npy', folder='data') +freqs = load_example_data('group_freqs.npy', folder='data') +spectra = load_example_data('group_powers.npy', folder='data') ################################################################################################### # @@ -46,11 +46,11 @@ print(spectra.shape) ################################################################################################### -# FOOOFGroup -# ---------- +# SpectralGroupModel +# ------------------ # -# The :class:`~fooof.FOOOFGroup` object is very similar to the FOOOF object (programmatically, -# it inherits from the FOOOF object), and can be used in the same way. +# The :class:`~specparam.SpectralGroupModel` object is very similar to the SpectralModel object (programmatically, +# it inherits from the SpectralModel object), and can be used in the same way. # # The main difference is that instead of running across a single power spectrum, it # operates across 2D matrices containing multiple power spectra. @@ -61,25 +61,25 @@ # be spectra from across channels, or across trials, or across subjects, or # whatever organization makes sense for the analysis at hand. # -# The main differences with the :class:`~fooof.FOOOFGroup` object, are that it uses a +# The main differences with the :class:`~specparam.SpectralGroupModel` object, are that it uses a # `power_spectra` attribute, which stores the matrix of power-spectra to be fit, # and collects fit results into a `group_results` attribute. # -# Otherwise, :class:`~fooof.FOOOFGroup` supports all the same functionality, -# accessed in the same way as the :class:`~fooof.FOOOF` object. +# Otherwise, :class:`~specparam.SpectralGroupModel` supports all the same functionality, +# accessed in the same way as the :class:`~specparam.SpectralModel` object. # -# Internally, it runs the exact same fitting procedure, per spectrum, as the FOOOF object. +# Internally, it runs the exact same fitting procedure, per spectrum, as the SpectralModel object. # ################################################################################################### -# Initialize a FOOOFGroup object, which accepts all the same settings as FOOOF -fg = FOOOFGroup(peak_width_limits=[1, 8], min_peak_height=0.05, max_n_peaks=6) +# Initialize a SpectralGroupModel object, which accepts all the same settings as SpectralModel +fg = SpectralGroupModel(peak_width_limits=[1, 8], min_peak_height=0.05, max_n_peaks=6) ################################################################################################### # Fit a group of power spectra with the .fit() method -# The key difference (compared to FOOOF) is that it takes a 2D array of spectra +# The key difference (compared to SpectralModel) is that it takes a 2D array of spectra # This matrix should have the shape of [n_spectra, n_freqs] fg.fit(freqs, spectra, [3, 30]) @@ -95,21 +95,21 @@ ################################################################################################### # -# Just as with the FOOOF object, you can call the convenience method -# :meth:`fooof.FOOOFGroup.report` to run the fitting, and then print the results and plots. +# Just as with the SpectralModel object, you can call the convenience method +# :meth:`specparam.SpectralGroupModel.report` to run the fitting, and then print the results and plots. # ################################################################################################### -# You can also save out PDF reports of the FOOOFGroup fits, same as with FOOOF -fg.save_report('FOOOFGroup_report') +# You can also save out PDF reports of the group fits, same as for an individual model +fg.save_report('group_report') ################################################################################################### -# FOOOFGroup Results -# ------------------ +# Group Results +# ------------- # -# FOOOFGroup collects fits across power spectra, and stores them in an attribute -# called ``group_results``, which is a list of FOOOFResults objects. +# The group model object collects fits across power spectra, and stores them in an attribute +# called ``group_results``, which is a list of FitResults objects. # ################################################################################################### @@ -122,17 +122,17 @@ # ~~~~~~~~~~ # # To collect results from across all model fits, and to select specific parameters -# you can use the :func:`~fooof.FOOOFGroup.get_params` method. +# you can use the :func:`~specparam.SpectralGroupModel.get_params` method. # -# This method works the same as in the :class:`~fooof.FOOOF` object, and lets you extract +# This method works the same as in the :class:`~specparam.SpectralModel` object, and lets you extract # specific results by specifying a field, as a string, and (optionally) a specific column # to extract. # -# Since the :class:`~fooof.FOOOFGroup` object collects results from across multiple model fits, -# you should always use :func:`~fooof.FOOOFGroup.get_params` to access model parameters. -# The results attributes introduced with the FOOOF object (such as `aperiodic_params_` or +# Since the :class:`~specparam.SpectralGroupModel` object collects results from across multiple model fits, +# you should always use :func:`~specparam.SpectralGroupModel.get_params` to access model parameters. +# The results attributes introduced with the SpectralModel object (such as `aperiodic_params_` or # `peak_params_`) do not store results across the group, as they are defined for individual -# model fits (and used internally as such by the FOOOFGroup object). +# model fits (and used internally as such by the SpectralGroupModel object). # ################################################################################################### @@ -157,16 +157,16 @@ ################################################################################################### # # More information about the parameters you can extract is also documented in the -# FOOOFResults object. +# FitResults object. # ################################################################################################### -# Grab a particular FOOOFResults item -# Note that as a shortcut, you can index the FOOOFGroup object directly to access 'group_results' +# Grab a particular FitResults data object +# Note that as a shortcut, you can index the SpectralGroupModel object directly to access 'group_results' f_res = fg[0] -# Check the documentation for the FOOOFResults, which has descriptions of the parameters +# Check the documentation for the FitResults, which has descriptions of the parameters print(f_res.__doc__) ################################################################################################### @@ -184,26 +184,26 @@ print(cfs[0:10, :]) ################################################################################################### -# Saving & Loading with FOOOFGroup -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Saving & Loading Group Objects +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# FOOOFGroup also support saving and loading, with the same options for saving out -# different things as defined and described for the FOOOF object. +# The group object also support saving and loading, with the same options for saving out +# different things as defined and described for the SpectralModel object. # -# The only difference in saving FOOOFGroup, is that it saves out a 'jsonlines' file, +# The only difference in saving SpectralGroupModel, is that it saves out a 'jsonlines' file, # in which each line is a JSON object, saving the specified data, settings, and results for # a single power spectrum. # ################################################################################################### -# Save out FOOOFGroup settings & results +# Save out group settings & results fg.save('FG_results', save_settings=True, save_results=True) ################################################################################################### # You can then reload this group -nfg = FOOOFGroup() +nfg = SpectralGroupModel() nfg.load('FG_results') ################################################################################################### @@ -215,7 +215,7 @@ # Parallel Support # ~~~~~~~~~~~~~~~~ # -# FOOOFGroup also has support for running in parallel, which can speed things up, since +# SpectralGroupModel also has support for running in parallel, which can speed things up, since # each power spectrum can be fit independently. # # The fit method includes an optional parameter ``n_jobs``, which if set at 1 (as default), @@ -239,7 +239,7 @@ # Progress Bar # ~~~~~~~~~~~~ # -# If you have a large number of spectra to fit with a :class:`~fooof.FOOOFGroup`, and you +# If you have a large number of spectra to fit with a :class:`~specparam.SpectralGroupModel`, and you # want to monitor it's progress, you can also use a progress bar to print out fitting progress. # # Progress bar options are: @@ -258,19 +258,19 @@ # ~~~~~~~~~~~~~~~~~~~~~~~~~~ # # When fitting power spectrum models for a group of power spectra, results are stored -# in FOOOFResults objects, which store (only) the results of the model fit, +# in FitResults objects, which store (only) the results of the model fit, # not the full model fits themselves. # -# To examine individual model fits, :class:`~fooof.FOOOFGroup` can regenerate -# :class:`~fooof.FOOOF` objects for individual power spectra, with the full model available -# for visualization. To do so, you can use the :meth:`~fooof.FOOOFGroup.get_fooof` method. +# To examine individual model fits, :class:`~specparam.SpectralGroupModel` can regenerate +# :class:`~specparam.SpectralModel` objects for individual power spectra, with the full model available +# for visualization. To do so, you can use the :meth:`~specparam.SpectralGroupModel.get_model` method. # ################################################################################################### # Extract a particular spectrum, specified by index # Here we also specify to regenerate the the full model fit, from the results -fm = fg.get_fooof(ind=2, regenerate=True) +fm = fg.get_model(ind=2, regenerate=True) ################################################################################################### diff --git a/tutorials/plot_07-TroubleShooting.py b/tutorials/plot_07-TroubleShooting.py index d7052061..eca45189 100644 --- a/tutorials/plot_07-TroubleShooting.py +++ b/tutorials/plot_07-TroubleShooting.py @@ -10,13 +10,13 @@ # General imports import numpy as np -# Import the FOOOF and FOOOFGroup objects -from fooof import FOOOF, FOOOFGroup +# Import the model objects +from specparam import SpectralModel, SpectralGroupModel # Import some utilities for creating simulated power-spectra -from fooof.sim.params import param_sampler -from fooof.sim.gen import gen_power_spectrum, gen_group_power_spectra -from fooof.sim.utils import set_random_seed +from specparam.sim import sim_power_spectrum, sim_group_power_spectra +from specparam.sim.params import param_sampler +from specparam.sim.utils import set_random_seed #################################################################################################### # Algorithm Settings @@ -74,7 +74,7 @@ # # After model fitting, some goodness of fit metrics are calculated to assist with assessing # the quality of the model fits. It calculates both the model fit error, as the mean absolute -# error (MAE) between the full model fit (``fooofed_spectrum_``) and the original power spectrum, +# error (MAE) between the full model fit (``modeled_spectrum_``) and the original power spectrum, # as well as the R-squared correspondence between the original spectrum and the full model. # # These scores can be used to assess how the model is performing. However interpreting these @@ -115,34 +115,34 @@ # Simulating Power Spectra # ------------------------ # -# For this example, we will use simulated data. The FOOOF module includes utilities -# for creating simulated power-spectra. To do so, we can use the :func:`~.gen_power_spectrum` +# For this example, we will use simulated data. The `specparam` module includes utilities +# for creating simulated power-spectra. To do so, we can use the :func:`~.sim_power_spectrum` # function to simulate individual power spectra, following the power spectrum model. # -# First, we will start by generating a noisy simulated power spectrum +# First, we will start by simulating a noisy power spectrum. # ################################################################################################### -# Set the frequency range to generate the power spectrum +# Set the frequency range to simualate the power spectrum f_range = [1, 50] # Set aperiodic component parameters, as [offset, exponent] ap_params = [20, 2] # Gaussian peak parameters gauss_params = [[10, 1.0, 2.5], [20, 0.8, 2], [32, 0.6, 1]] -# Set the level of noise to generate the power spectrum with +# Set the level of noise to simulate the power spectrum with nlv = 0.1 -# Set random seed, for consistency generating simulated data +# Set random seed, for consistency creating simulated data set_random_seed(21) # Create a simulated power spectrum -freqs, spectrum = gen_power_spectrum(f_range, ap_params, gauss_params, nlv) +freqs, spectrum = sim_power_spectrum(f_range, ap_params, gauss_params, nlv) ################################################################################################### # Fit an (unconstrained) model, liable to overfit -fm = FOOOF() +fm = SpectralModel() fm.report(freqs, spectrum) ################################################################################################### @@ -159,7 +159,7 @@ ################################################################################################### # Update settings to fit a more constrained model, to reduce overfitting -fm = FOOOF(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.4) +fm = SpectralModel(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.4) fm.report(freqs, spectrum) ################################################################################################### @@ -212,22 +212,22 @@ ################################################################################################### -# Set the frequency range to generate the power spectrum +# Set the frequency range to simulate the power spectrum f_range = [1, 50] # Define aperiodic parameters, as [offset, exponent] ap_params = [20, 2] # Define peak parameters, each peak defined as [CF, PW, BW] gauss_params = [[10, 1.0, 1.0], [20, 0.3, 1.5], [32, 0.25, 1]] -# Set the level of noise to generate the power spectrum with +# Set the level of noise to simulate the power spectrum with nlv = 0.025 # Create a simulated power spectrum -freqs, spectrum = gen_power_spectrum([1, 50], ap_params, gauss_params, nlv=nlv) +freqs, spectrum = sim_power_spectrum([1, 50], ap_params, gauss_params, nlv=nlv) ################################################################################################### # Update settings to make sure they are sensitive to smaller peaks in smoother power spectra -fm = FOOOF(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.2) +fm = SpectralModel(peak_width_limits=[1, 8], max_n_peaks=6, min_peak_height=0.2) fm.report(freqs, spectrum) ################################################################################################### @@ -245,7 +245,7 @@ # a new analysis, or working with a new dataset, we do recommend starting by # trying some individual fits like this. # -# If and when you move to using :class:`~fooof.FOOOFGroup` to fit groups of power spectra, +# If and when you move to using :class:`~specparam.SpectralGroupModel` to fit groups of power spectra, # there are some slightly different ways to investigate groups of fits, # which we'll step through now, using some simulated data. # @@ -256,7 +256,7 @@ # # We will continue using simulated data, this time simulating a group of power spectra. # -# To simulate a group of power spectra, we will use the :func:`~.gen_group_power_spectra` +# To simulate a group of power spectra, we will use the :func:`~.sim_group_power_spectra` # in combination with called :func:`~.param_sampler` that is used to sample across # possible parameters. # @@ -276,13 +276,13 @@ gauss_opts = param_sampler([[], [10, 0.5, 2], [10, 0.5, 2, 20, 0.3, 4]]) # Simulate a group of power spectra -freqs, power_spectra = gen_group_power_spectra(n_spectra, sim_freq_range, +freqs, power_spectra = sim_group_power_spectra(n_spectra, sim_freq_range, ap_opts, gauss_opts, nlv) ################################################################################################### -# Initialize a FOOOFGroup object -fg = FOOOFGroup(peak_width_limits=[1, 6]) +# Initialize a group model object +fg = SpectralGroupModel(peak_width_limits=[1, 6]) ################################################################################################### @@ -291,7 +291,7 @@ ################################################################################################### # -# In the :class:`~fooof.FOOOFGroup` report we can get a sense of the overall performance +# In the :class:`~specparam.SpectralGroupModel` report we can get a sense of the overall performance # by looking at the information about the goodness of fit metrics, and also things like # the distribution of peaks. # @@ -300,7 +300,7 @@ # # To do so, we will typically still want to visualize some example fits, to see # what is happening. To do so, next we will find which fits have the most error, -# and select these fits from the :class:`~fooof.FOOOFGroup` object to visualize. +# and select these fits from the :class:`~specparam.SpectralGroupModel` object to visualize. # ################################################################################################### @@ -309,7 +309,7 @@ worst_fit_ind = np.argmax(fg.get_params('error')) # Extract this model fit from the group -fm = fg.get_fooof(worst_fit_ind, regenerate=True) +fm = fg.get_model(worst_fit_ind, regenerate=True) ################################################################################################### @@ -319,7 +319,7 @@ ################################################################################################### # -# You can also loop through all the results in a :class:`~fooof.FOOOFGroup`, extracting +# You can also loop through all the results in a :class:`~specparam.SpectralGroupModel`, extracting # all fits that meet some criterion that makes them worth checking. # # This might be checking for fits above some error threshold, as below, but note @@ -336,10 +336,10 @@ to_check = [] for ind, res in enumerate(fg): if res.error > error_threshold: - to_check.append(fg.get_fooof(ind, regenerate=True)) + to_check.append(fg.get_model(ind, regenerate=True)) # A more condensed version of the procedure above can be written like this: -#to_check = [fg.get_fooof(ind, True) for ind, res in enumerate(fg) if res.error > error_threshold] +#to_check = [fg.get_model(ind, True) for ind, res in enumerate(fg) if res.error > error_threshold] ################################################################################################### @@ -377,14 +377,14 @@ ################################################################################################### # Print out instructions to report bad fits -# Note you can also call this from FOOOFGroup, and from instances (ex: `fm.print_report_issue()`) -FOOOF.print_report_issue() +# Note you can also call this from SpectralGroupModel, and from instances (ex: `fm.print_report_issue()`) +SpectralModel.print_report_issue() ################################################################################################### # Conclusion # ---------- # # We have now stepped through the full work-flow of fitting power spectrum models, using -# FOOOF objects, picking settings, and troubleshooting model fits. In the next -# and final tutorial, we will introduce how to start analyzing FOOOF results. +# model objects, picking settings, and troubleshooting model fits. In the next +# and final tutorial, we will introduce how to start analyzing model results. # diff --git a/tutorials/plot_08-FurtherAnalysis.py b/tutorials/plot_08-FurtherAnalysis.py index 97f6d020..45641fff 100644 --- a/tutorials/plot_08-FurtherAnalysis.py +++ b/tutorials/plot_08-FurtherAnalysis.py @@ -29,22 +29,22 @@ # General imports import numpy as np -# Import the FOOOF and FOOOFGroup objects -from fooof import FOOOF, FOOOFGroup +# Import the model objects +from specparam import SpectralModel, SpectralGroupModel # Import the Bands object, which is used to define frequency bands -from fooof.bands import Bands +from specparam.bands import Bands # Import simulation code and utilities -from fooof.sim.params import param_sampler -from fooof.sim.gen import gen_group_power_spectra -from fooof.sim.utils import set_random_seed +from specparam.sim import sim_group_power_spectra +from specparam.sim.params import param_sampler +from specparam.sim.utils import set_random_seed # Import some analysis functions -from fooof.analysis import get_band_peak_fm, get_band_peak_fg +from specparam.analysis import get_band_peak, get_band_peak_group # Import a utility to download and load example data -from fooof.utils.download import load_fooof_data +from specparam.utils.download import load_example_data ################################################################################################### # Load and Fit Example Data @@ -56,13 +56,13 @@ ################################################################################################### # Load example data files needed for this example -freqs = load_fooof_data('freqs.npy', folder='data') -spectrum = load_fooof_data('spectrum.npy', folder='data') +freqs = load_example_data('freqs.npy', folder='data') +spectrum = load_example_data('spectrum.npy', folder='data') ################################################################################################### # Fit a power spectrum model -fm = FOOOF(peak_width_limits=[2, 8]) +fm = SpectralModel(peak_width_limits=[2, 8]) fm.fit(freqs, spectrum, [3, 30]) ################################################################################################### @@ -74,19 +74,19 @@ ################################################################################################### -# Set random seed, for consistency generating simulated data +# Set random seed, for consistency creating simulated data set_random_seed(21) -# Generate some simulated power spectra -freqs, spectra = gen_group_power_spectra(n_spectra=10, +# Create some simulated power spectra +freqs, spectra = sim_group_power_spectra(n_spectra=10, freq_range=[3, 40], aperiodic_params=param_sampler([[20, 2], [35, 1.5]]), periodic_params=param_sampler([[], [10, 0.5, 2]])) ################################################################################################### -# Initialize a FOOOFGroup object with some settings -fg = FOOOFGroup(peak_width_limits=[1, 8], min_peak_height=0.05, +# Initialize a group model object with some settings +fg = SpectralGroupModel(peak_width_limits=[1, 8], min_peak_height=0.05, max_n_peaks=6, verbose=False) # Fit power spectrum models across the group of simulated power spectra @@ -96,7 +96,7 @@ # Analysis Utilities # ------------------ # -# The FOOOF module includes some analysis functions. +# The `specparam` module includes some analysis functions. # # Note that these utilities are generally relatively simple utilities that assist in # accessing and investigating the model parameters. @@ -129,11 +129,11 @@ 'beta' : [15, 30]}) ################################################################################################### -# Extracting peaks from FOOOF Objects -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Extracting peaks from SpectralModel Objects +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# The :func:`~.get_band_peak_fm` function takes in a -# :class:`~.FOOOF` object and extracts peak(s) from a requested frequency range. +# The :func:`~.get_band_peak` function takes in a +# :class:`~.SpectralModel` object and extracts peak(s) from a requested frequency range. # # You can optionally specify: # @@ -146,25 +146,25 @@ ################################################################################################### # Extract any alpha band peaks from the power spectrum model -alpha = get_band_peak_fm(fm, bands.alpha) +alpha = get_band_peak(fm, bands.alpha) print(alpha) ################################################################################################### -# Extracting peaks from FOOOFGroup Objects -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Extracting peaks from SpectralGroupModel Objects +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# Similarly, the :func:`~.get_band_peak_fg` function can be used -# to select peaks from specified frequency ranges, from :class:`~fooof.FOOOFGroup` objects. +# Similarly, the :func:`~.get_band_peak_group` function can be used +# to select peaks from specified frequency ranges, from :class:`~specparam.SpectralGroupModel` objects. # # Note that you can also apply a threshold to extract group peaks but, as discussed below, # this approach will always only extract at most one peak per individual model fit from -# the FOOOFGroup object. +# the SpectralGroupModel object. # ################################################################################################### # Get all alpha peaks from a group of power spectrum models -alphas = get_band_peak_fg(fg, bands.alpha) +alphas = get_band_peak_group(fg, bands.alpha) # Check out some of the alpha parameters print(alphas[0:5, :]) @@ -174,10 +174,10 @@ # When selecting peaks from a group of model fits, we want to retain information about # which model each peak comes from. # -# To do so, the output of :func:`~.get_band_peak_fg` is organized such that each row +# To do so, the output of :func:`~.get_band_peak_group` is organized such that each row # corresponds to a specific model fit. This means that returned array has the shape # [n_models, 3], and so the index of each row corresponds to the index of the model -# from the FOOOFGroup object. +# from the SpectralGroupModel object. # # For this to work, at most 1 peak is extracted for each model fit within the specified band. # If more than 1 peak are found within the band, the peak with the highest power is extracted. @@ -196,7 +196,7 @@ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # If you want to do more customized extraction of peaks, for example, extracting all peaks -# in a frequency band from each model in a FOOOFGroup object, you may need to use the +# in a frequency band from each model in a SpectralGroupModel object, you may need to use the # underlying functions that operate on arrays of peak parameters. To explore these functions, # check the listings in the API page. # @@ -222,12 +222,12 @@ # --------------------------------- # # Typically, for analyzing the aperiodic component of the data, aperiodic parameters -# just need to be extracted from FOOOF objects and fit into analyses of interest. +# just need to be extracted from specparam objects and fit into analyses of interest. # ################################################################################################### -# Plot from the FOOOFGroup, to visualize the parameters +# Plot from the group object, to visualize the parameters fg.plot() ################################################################################################### diff --git a/tutorials/plot_09-Reporting.py b/tutorials/plot_09-Reporting.py index 8d530da9..ffe132a1 100644 --- a/tutorials/plot_09-Reporting.py +++ b/tutorials/plot_09-Reporting.py @@ -10,19 +10,19 @@ ################################################################################################### -# Import FOOOF model objects -from fooof import FOOOF, FOOOFGroup +# Import model objects +from specparam import SpectralModel, SpectralGroupModel # Import simulation functions to create some example data -from fooof.sim import gen_power_spectrum, gen_group_power_spectra +from specparam.sim import sim_power_spectrum, sim_group_power_spectra # Import utilities to print out information for reporting -from fooof.utils.reports import methods_report_info, methods_report_text +from specparam.utils.reports import methods_report_info, methods_report_text # sphinx_gallery_start_ignore # Note: this code gets hidden, but serves to create the text plot for the icon -from fooof.core.strings import gen_methods_report_str -from fooof.core.reports import REPORT_FONT +from specparam.core.strings import gen_methods_report_str +from specparam.core.reports import REPORT_FONT import matplotlib.pyplot as plt text = gen_methods_report_str(concise=True) text = text[0:142] + '\n' + text[142:] @@ -49,8 +49,8 @@ ################################################################################################### # Check the version of the module -from fooof import __version__ as fooof_version -print('Current fooof version:', fooof_version) +from specparam import __version__ as specparam_version +print('Current specparam version:', specparam_version) ################################################################################################### # Getting Model Reporting Information @@ -74,12 +74,12 @@ ################################################################################################### # Initialize model object -fooof_obj = FOOOF() +model = SpectralModel() ################################################################################################### # Print out all the methods information for reporting -methods_report_info(fooof_obj) +methods_report_info(model) ################################################################################################### # @@ -87,7 +87,7 @@ # some of which might look familiar. # # The settings information, for example, is the same as printed using the -# # - :meth:`~fooof.FOOOF.print_settings` method. +# # - :meth:`~specparam.SpectralModel.print_settings` method. # # Next, let's check out the text version of the methods report. # @@ -95,7 +95,7 @@ ################################################################################################### # Generate methods text, with methods information inserted -methods_report_text(fooof_obj) +methods_report_text(model) ################################################################################################### # Additional Examples @@ -110,10 +110,10 @@ ################################################################################################### # Simulate an example power spectrum -freqs, powers = gen_power_spectrum([1, 50], [0, 10, 1], [10, 0.25, 2], freq_res=0.25) +freqs, powers = sim_power_spectrum([1, 50], [0, 10, 1], [10, 0.25, 2], freq_res=0.25) # Initialize model object -fm = FOOOF(min_peak_height=0.1, peak_width_limits=[1, 6], aperiodic_mode='knee') +fm = SpectralModel(min_peak_height=0.1, peak_width_limits=[1, 6], aperiodic_mode='knee') fm.fit(freqs, powers) ################################################################################################### @@ -140,18 +140,18 @@ # # Note that the reporting functions work with any model object. # -# For example, next we will use them on a :class:`~fooof.FOOOFGroup` object. +# For example, next we will use them on a :class:`~specparam.SpectralGroupModel` object. # ################################################################################################### # Simulate an example group of power spectra -freqs, powers = gen_group_power_spectra(10, [1, 75], [0, 1], [10, 0.25, 2]) +freqs, powers = sim_group_power_spectra(10, [1, 75], [0, 1], [10, 0.25, 2]) ################################################################################################### # Initialize and fit group model object -fg = FOOOFGroup(max_n_peaks=4, peak_threshold=1.75) +fg = SpectralGroupModel(max_n_peaks=4, peak_threshold=1.75) fg.fit(freqs, powers) ###################################################################################################