"""Event model object and associated code for fitting the model to spectrograms across events."""
import numpy as np
from specparam.objs import SpectralModel
from specparam.objs.base import BaseObject3D
from specparam.objs.algorithm import SpectralFitAlgorithm
from specparam.plts.event import plot_event_model
from specparam.data.conversions import event_group_to_dataframe, dict_to_df
from specparam.data.utils import flatten_results_dict
from specparam.core.modutils import (copy_doc_func_to_method, docs_get_section,
replace_docstring_sections)
from specparam.core.reports import save_event_report
from specparam.core.strings import gen_event_results_str
###################################################################################################
###################################################################################################
[docs]@replace_docstring_sections([docs_get_section(SpectralModel.__doc__, 'Parameters'),
docs_get_section(SpectralModel.__doc__, 'Notes')])
class SpectralTimeEventModel(SpectralFitAlgorithm, BaseObject3D):
"""Model a set of event as a combination of aperiodic and periodic components.
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 SpectralModel object
Attributes
----------
freqs : 1d array
Frequency values for the power spectra.
spectrograms : 3d array
Power values for the spectrograms, organized as [n_events, n_freqs, n_time_windows].
Power values are stored internally in log10 scale.
freq_range : list of [float, float]
Frequency range of the power spectra, as [lowest_freq, highest_freq].
freq_res : float
Frequency resolution of the power spectra.
event_group_results : list of list of FitResults
Full model results collected across all events and models.
event_time_results : dict
Results of the model fit across each time window, collected across events.
Each value in the dictionary stores a model fit parameter, as [n_events, n_time_windows].
Notes
-----
%copied in from SpectralModel object
- The event object inherits from the time model, which in turn inherits from the
group object, etc. As such it also has data attributes defined on the underlying
objects (see notes and attribute lists in inherited objects for details).
"""
[docs] def __init__(self, *args, **kwargs):
"""Initialize object with desired settings."""
BaseObject3D.__init__(self,
aperiodic_mode=kwargs.pop('aperiodic_mode', 'fixed'),
periodic_mode=kwargs.pop('periodic_mode', 'gaussian'),
debug_mode=kwargs.pop('debug_mode', False),
verbose=kwargs.pop('verbose', True))
SpectralFitAlgorithm.__init__(self, *args, **kwargs)
self._reset_event_results()
[docs] def report(self, freqs=None, spectrograms=None, freq_range=None,
peak_org=None, n_jobs=1, progress=None):
"""Fit a set of events and display a report, with a plot and printed results.
Parameters
----------
freqs : 1d array, optional
Frequency values for the power_spectra, in linear space.
spectrograms : 3d array or list of 2d array
Matrix of power values, in linear space.
If a list of 2d arrays, each should be have the same shape of [n_freqs, n_time_windows].
If a 3d array, should have shape [n_events, n_freqs, n_time_windows].
freq_range : list of [float, float], optional
Frequency range to fit the model to. If not provided, fits the entire given range.
peak_org : int or Bands
How to organize peaks.
If int, extracts the first n peaks.
If Bands, extracts peaks based on band definitions.
n_jobs : int, optional, default: 1
Number of jobs to run in parallel.
1 is no parallelization. -1 uses all available cores.
progress : {None, 'tqdm', 'tqdm.notebook'}, optional
Which kind of progress bar to use. If None, no progress bar is used.
Notes
-----
Data is optional, if data has already been added to the object.
"""
self.fit(freqs, spectrograms, freq_range, peak_org, n_jobs, progress)
self.plot()
self.print_results()
[docs] def print_results(self, concise=False):
"""Print out SpectralTimeEventModel results.
Parameters
----------
concise : bool, optional, default: False
Whether to print the report in a concise mode, or not.
"""
print(gen_event_results_str(self, concise))
[docs] @copy_doc_func_to_method(plot_event_model)
def plot(self, save_fig=False, file_name=None, file_path=None, **plot_kwargs):
plot_event_model(self, save_fig=save_fig, file_name=file_name,
file_path=file_path, **plot_kwargs)
[docs] @copy_doc_func_to_method(save_event_report)
def save_report(self, file_name, file_path=None, add_settings=True):
save_event_report(self, file_name, file_path, add_settings)
[docs] def get_model(self, event_ind, window_ind, regenerate=True):
"""Get a model fit object for a specified index.
Parameters
----------
event_ind : int
Index for which event to extract from.
window_ind : int
Index for which time window to extract from.
regenerate : bool, optional, default: False
Whether to regenerate the model fits for the requested model.
Returns
-------
model : SpectralModel
The FitResults data loaded into a model object.
"""
# Initialize model object, with same settings, metadata, & check mode as current object
model = SpectralModel(**self.get_settings()._asdict(), verbose=self.verbose)
model.add_meta_data(self.get_meta_data())
model.set_run_modes(*self.get_run_modes())
# Add data for specified single power spectrum, if available
if self.has_data:
model.power_spectrum = self.spectrograms[event_ind][:, window_ind]
# Add results for specified power spectrum, regenerating full fit if requested
model.add_results(self.event_group_results[event_ind][window_ind])
if regenerate:
model._regenerate_model()
return model
[docs] def save_model_report(self, event_index, window_index, file_name,
file_path=None, add_settings=True, **plot_kwargs):
""""Save out an individual model report for a specified model fit.
Parameters
----------
event_ind : int
Index for which event to extract from.
window_ind : int
Index for which time window to extract from.
file_name : str
Name to give the saved out file.
file_path : str, optional
Path to directory to save to. If None, saves to current directory.
add_settings : bool, optional, default: True
Whether to add a print out of the model settings to the end of the report.
plot_kwargs : keyword arguments
Keyword arguments to pass into the plot method.
"""
self.get_model(event_index, window_index, regenerate=True).save_report(\
file_name, file_path, add_settings, **plot_kwargs)
[docs] def to_df(self, peak_org=None):
"""Convert and extract the model results as a pandas object.
Parameters
----------
peak_org : int or Bands, optional
How to organize peaks.
If int, extracts the first n peaks.
If Bands, extracts peaks based on band definitions.
If provided, re-extracts peak features; if not provided, converts from `time_results`.
Returns
-------
pd.DataFrame
Model results organized into a pandas object.
"""
if peak_org is not None:
df = event_group_to_dataframe(self.event_group_results, peak_org)
else:
df = dict_to_df(flatten_results_dict(self.get_results()))
return df
def _check_width_limits(self):
"""Check and warn about bandwidth limits / frequency resolution interaction."""
# Only check & warn on first spectrum
# This is to avoid spamming standard output for every spectrogram in the set
if np.all(self.power_spectrum == self.spectrograms[0, :, 0]):
super()._check_width_limits()