From 1b24def41a43e0f2b588f07cbc55726b44c7a498 Mon Sep 17 00:00:00 2001 From: Cyrille Rossant Date: Fri, 26 Jul 2019 14:27:42 +0200 Subject: [PATCH] Show channel labels depending on channel mapping in waveform, feature, trace views --- docs/api.md | 1647 +++++++++++++++++++--- phy/apps/__init__.py | 16 +- phy/apps/base.py | 21 +- phy/apps/template/gui.py | 2 +- phy/cluster/views/tests/test_waveform.py | 1 + tools/api.py | 2 +- 6 files changed, 1483 insertions(+), 206 deletions(-) diff --git a/docs/api.md b/docs/api.md index 3df321450..b46affeea 100644 --- a/docs/api.md +++ b/docs/api.md @@ -106,6 +106,23 @@ phy: interactive visualization and manual spike sorting of large-scale ephys dat * [phy.cluster.WaveformView](#phyclusterwaveformview) +### [phy.apps](#phyapps) + +* [phy.apps.add_default_handler](#phyappsadd_default_handler) +* [phy.apps.capture_exceptions](#phyappscapture_exceptions) +* [phy.apps.contextmanager](#phyappscontextmanager) +* [phy.apps.exceptionHandler](#phyappsexceptionhandler) +* [phy.apps.format_exception](#phyappsformat_exception) +* [phy.apps.BaseController](#phyappsbasecontroller) +* [phy.apps.FeatureMixin](#phyappsfeaturemixin) +* [phy.apps.FiringRateView](#phyappsfiringrateview) +* [phy.apps.ISIView](#phyappsisiview) +* [phy.apps.QtDialogLogger](#phyappsqtdialoglogger) +* [phy.apps.TemplateMixin](#phyappstemplatemixin) +* [phy.apps.TraceMixin](#phyappstracemixin) +* [phy.apps.WaveformMixin](#phyappswaveformmixin) + + ### [phy.apps.template](#phyappstemplate) * [phy.apps.template.from_sparse](#phyappstemplatefrom_sparse) @@ -117,6 +134,13 @@ phy: interactive visualization and manual spike sorting of large-scale ephys dat * [phy.apps.template.TemplateModel](#phyappstemplatetemplatemodel) +### [phy.apps.kwik](#phyappskwik) + +* [phy.apps.kwik.kwik_describe](#phyappskwikkwik_describe) +* [phy.apps.kwik.kwik_gui](#phyappskwikkwik_gui) +* [phy.apps.kwik.KwikController](#phyappskwikkwikcontroller) + + ## phy.utils @@ -9082,73 +9106,90 @@ GUI is closed. To be overriden. --- -## phy.apps.template +## phy.apps -Template GUI. +CLI tool. --- -#### phy.apps.template.from_sparse +#### phy.apps.add_default_handler -**`phy.apps.template.from_sparse(data, cols, channel_ids)`** +**`phy.apps.add_default_handler(level='INFO', logger=)`** -Convert a sparse structure into a dense one. -**Parameters** +--- -* `data : array-like` - A (n_spikes, n_channels_loc, ...) array with the data. +#### phy.apps.capture_exceptions -* `cols : array-like` - A (n_spikes, n_channels_loc) array with the channel indices of - every row in data. -* `channel_ids : array-like` - List of requested channel ids (columns). +**`phy.apps.capture_exceptions()`** + +Log exceptions instead of crashing the GUI, and display an error dialog on errors. --- -#### phy.apps.template.get_template_params +#### phy.apps.contextmanager -**`phy.apps.template.get_template_params(params_path)`** +**`phy.apps.contextmanager(func)`** -Get a dictionary of parameters from a `params.py` file. +@contextmanager decorator. ---- +Typical usage: -#### phy.apps.template.load_model + @contextmanager + def some_generator(): + + try: + yield + finally: + +This makes this: -**`phy.apps.template.load_model(params_path)`** + with some_generator() as : + -Return a TemplateModel instance from a path to a `params.py` file. +equivalent to this: + + + try: + = + + finally: + --- -#### phy.apps.template.template_describe +#### phy.apps.exceptionHandler -**`phy.apps.template.template_describe(params_path)`** +**`phy.apps.exceptionHandler(exception_type, exception, traceback)`** + -Describe a template dataset. --- -#### phy.apps.template.template_gui +#### phy.apps.format_exception -**`phy.apps.template.template_gui(params_path, **kwargs)`** +**`phy.apps.format_exception(etype, value, tb, limit=None, chain=True)`** -Launch the Template GUI. +Format a stack trace and the exception information. + +The arguments have the same meaning as the corresponding arguments +to print_exception(). The return value is a list of strings, each +ending in a newline and some containing internal newlines. When +these lines are concatenated and printed, exactly the same text is +printed as does print_exception(). --- -### phy.apps.template.TemplateController +### phy.apps.BaseController -Controller for the Template GUI. +Base controller for manual clustering GUI. **Constructor** @@ -9168,412 +9209,1348 @@ Controller for the Template GUI. * `clear_cache : boolean` Whether to clear the cache on startup. +* `clear_state : boolean` + Whether to clear the GUI state files on startup. + * `enable_threading : boolean` Whether to enable threading in the views when selecting clusters. ---- - -#### TemplateController.at_least_one_view - - -**`TemplateController.at_least_one_view(self, view_name)`** +**Methods to override** -Add a view of a given type if there is not already one. +The main methods that can be overriden when implementing a custom `Controller` are: -To be called before creating a GUI. ---- +* `_create_model() : None => object` + Return a Model instance (any object, see below) from the controller constructor's + parameters. -#### TemplateController.create_amplitude_view +* `_set_view_creator() : None => None` + Populate the `self.view_creator` dictionary with custom views. +* `get_best_channels(cluster_id) : int => list` + Return the list of best channels for any given cluster, sorted by decreasing match. -**`TemplateController.create_amplitude_view(self)`** +**Model** +The Model can be any object, but it needs to implement the following properties and methods +in order to work with the BaseController: ---- +* `channel_mapping : array-like` + A `(n_channels,)` array with the column index in the raw data array of every channel. + The displayed channel label of channel `channel_id` is `channel_mapping[channel_id]`. -#### TemplateController.create_correlogram_view +* `channel_positions : array-like` + A `(n_channels, 2)` array with the x, y coordinates of the electrode sites, + in any unit (e.g. μm). +* `channel_shanks : array-like` + A `(n_channels,)` array with the shank index of every channel. +channel_vertical_order = array-like + Permutation of the channels for display in the trace view. The shape is `(n_channels,)`. -**`TemplateController.create_correlogram_view(self)`** +* `duration : float` + The total duration of the recording, in seconds. -Create a correlogram view. +* `features : array-like` + The object containing the features. The feature view is shown if this object is not None. ---- +* `metadata : dict` + Cluster metadata. Map metadata field names to dictionaries {cluster_id: value}. + It is only expected to hold information representative of the state of the dataset + on disk, not during a live clustering session. + The special metadata field name `group` is reserved to cluster groups. -#### TemplateController.create_feature_view +* `n_channels : int` + Total number of channels in the recording (number of columns in the raw data array). +* `n_samples_waveforms : int` + Number of time samples to use when extracting raw waveforms. -**`TemplateController.create_feature_view(self)`** +* `sample_rate : float` + The sampling rate of the raw data. +* `spike_attributes : dict` + Map attribute names to spike attributes, arrays of shape `(n_spikes,)`. +* `spike_clusters : array-like` + Initial spike-cluster assignments, shape `(n_spikes,)`. ---- +* `spike_times : array-like` + Spike times, in seconds, shape `(n_spikes,)`. -#### TemplateController.create_gui +* `traces : array-like` + Array (can be virtual/memmapped) of shape `(n_samples_total, n_channels)` with the + raw data. The trace view is shown if this object is not None. +get_features(spike_ids, channel_ids) : array-like, array-like => array-like + Return spike features of specified spikes on the specified channels. Optional. +get_waveforms(spike_ids, channel_ids) : array-like, array-like => array-like + Return raw spike waveforms of specified spikes on the specified channels. Optional. -**`TemplateController.create_gui(self, default_views=None, **kwargs)`** -Create the GUI. +* `save_spike_clusters(spike_clusters) : array-like => None` + Save spike clusters assignments back to disk. +save_metadata(name, values) : str, dict => None + Save cluster metadata, where name is the metadata field name, and values a dictionary + `{cluster_id: value}`. -**Constructor** +**Note** +The Model represents data as it is stored on disk. When cluster data changes during +a manual clustering session (like spike-cluster assignments), the data in the model +is not expected to change (it is rather the responsability of the controller). -* `default_views : list` - List of views to add in the GUI, optional. By default, all views from the view - count are added. +The model implements saving option for spike cluster assignments and cluster metadata. --- -#### TemplateController.create_ipython_view +#### BaseController.at_least_one_view -**`TemplateController.create_ipython_view(self)`** +**`BaseController.at_least_one_view(self, view_name)`** -Create an IPython View. +Add a view of a given type if there is not already one. + +To be called before creating a GUI. --- -#### TemplateController.create_probe_view +#### BaseController.create_amplitude_view -**`TemplateController.create_probe_view(self)`** +**`BaseController.create_amplitude_view(self)`** -Create a probe view. +Create the amplitude view. --- -#### TemplateController.create_raster_view +#### BaseController.create_correlogram_view -**`TemplateController.create_raster_view(self)`** +**`BaseController.create_correlogram_view(self)`** -Create a raster view. +Create a correlogram view. --- -#### TemplateController.create_template_feature_view - - -**`TemplateController.create_template_feature_view(self)`** - +#### BaseController.create_gui ---- +**`BaseController.create_gui(self, default_views=None, **kwargs)`** -#### TemplateController.create_template_view +Create the GUI. +**Constructor** -**`TemplateController.create_template_view(self)`** -Create a template view. +* `default_views : list` + List of views to add in the GUI, optional. By default, all views from the view + count are added. --- -#### TemplateController.create_trace_view +#### BaseController.create_ipython_view -**`TemplateController.create_trace_view(self)`** +**`BaseController.create_ipython_view(self)`** -Create a trace view. +Create an IPython View. --- -#### TemplateController.create_waveform_view - +#### BaseController.create_probe_view -**`TemplateController.create_waveform_view(self)`** +**`BaseController.create_probe_view(self)`** +Create a probe view. --- -#### TemplateController.get_amplitudes +#### BaseController.create_raster_view -**`TemplateController.get_amplitudes(self, cluster_id, load_all=False)`** +**`BaseController.create_raster_view(self)`** -Return the spike amplitudes found in `amplitudes.npy`, for a given cluster. +Create a raster view. --- -#### TemplateController.get_background_spike_ids +#### BaseController.get_background_spike_ids -**`TemplateController.get_background_spike_ids(self, n=None)`** +**`BaseController.get_background_spike_ids(self, n=None)`** Return regularly spaced spikes. --- -#### TemplateController.get_best_channel +#### BaseController.get_best_channel -**`TemplateController.get_best_channel(self, cluster_id)`** +**`BaseController.get_best_channel(self, cluster_id)`** Return the best channel of a given cluster. This is the first channel returned by `get_best_channels()`. --- -#### TemplateController.get_best_channels +#### BaseController.get_best_channels -**`TemplateController.get_best_channels(self, cluster_id)`** +**`BaseController.get_best_channels(self, cluster_id)`** -Return the best channels of a given cluster. +Return the best channels of a given cluster. To be overriden. --- -#### TemplateController.get_channel_shank +#### BaseController.get_channel_shank -**`TemplateController.get_channel_shank(self, cluster_id)`** +**`BaseController.get_channel_shank(self, cluster_id)`** Return the shank of a cluster's best channel, if the channel_shanks array is available. --- -#### TemplateController.get_cluster_amplitude +#### BaseController.get_clusters_on_channel -**`TemplateController.get_cluster_amplitude(self, cluster_id)`** +**`BaseController.get_clusters_on_channel(self, channel_id)`** -Return the amplitude of the best template of a cluster. +Return all clusters which have the specified channel among their best channels. --- -#### TemplateController.get_clusters_on_channel +#### BaseController.get_mean_firing_rate -**`TemplateController.get_clusters_on_channel(self, channel_id)`** +**`BaseController.get_mean_firing_rate(self, cluster_id)`** -Return all clusters which have the specified channel among their best channels. +Return the mean firing rate of a cluster. --- -#### TemplateController.get_mean_firing_rate +#### BaseController.get_probe_depth -**`TemplateController.get_mean_firing_rate(self, cluster_id)`** +**`BaseController.get_probe_depth(self, cluster_id)`** -Return the mean firing rate of a cluster. +Return the depth of a cluster. --- -#### TemplateController.get_mean_spike_raw_amplitudes +#### BaseController.get_spike_ids -**`TemplateController.get_mean_spike_raw_amplitudes(self, cluster_id)`** +**`BaseController.get_spike_ids(self, cluster_id, n=None)`** -Return the average of the spike raw amplitudes. +Return part or all of spike ids belonging to a given cluster. --- -#### TemplateController.get_mean_spike_template_amplitudes +#### BaseController.get_spike_times -**`TemplateController.get_mean_spike_template_amplitudes(self, cluster_id)`** +**`BaseController.get_spike_times(self, cluster_id, n=None)`** -Return the average of the spike template amplitudes. +Return the spike times of spikes returned by `get_spike_ids(cluster_id, n)`. --- -#### TemplateController.get_probe_depth +#### BaseController.on_save_clustering -**`TemplateController.get_probe_depth(self, cluster_id)`** +**`BaseController.on_save_clustering(self, sender, spike_clusters, groups, *labels)`** -Return the depth of a cluster. +Save the modified data. --- -#### TemplateController.get_spike_feature_amplitudes - +#### BaseController.peak_channel_similarity -**`TemplateController.get_spike_feature_amplitudes(self, spike_ids, channel_id=None, channel_ids=None, pc=None, **kwargs)`** -Return the features for the specified channel and PC. +**`BaseController.peak_channel_similarity(self, cluster_id)`** ---- +Return the list of similar clusters to a given cluster, just on the basis of the +peak channel. -#### TemplateController.get_spike_ids +**Parameters** +* `cluster_id : int` -**`TemplateController.get_spike_ids(self, cluster_id, n=None)`** +**Returns** -Return part or all of spike ids belonging to a given cluster. +* `similarities : list` + List of tuples `(other_cluster_id, similarity_value)` sorted by decreasing + similarity value. --- -#### TemplateController.get_spike_raw_amplitudes - +### phy.apps.FeatureMixin -**`TemplateController.get_spike_raw_amplitudes(self, spike_ids, channel_ids=None, **kwargs)`** -Return the maximum amplitude of the raw waveforms across all channels. --- -#### TemplateController.get_spike_template_amplitudes +#### FeatureMixin.create_amplitude_view -**`TemplateController.get_spike_template_amplitudes(self, spike_ids, **kwargs)`** +**`FeatureMixin.create_amplitude_view(self)`** + -Return the template amplitudes multiplied by the spike's amplitude. --- -#### TemplateController.get_spike_times +#### FeatureMixin.create_feature_view -**`TemplateController.get_spike_times(self, cluster_id, n=None)`** +**`FeatureMixin.create_feature_view(self)`** + -Return the spike times of spikes returned by `get_spike_ids(cluster_id, n)`. --- -#### TemplateController.get_template_amplitude +#### FeatureMixin.get_spike_feature_amplitudes -**`TemplateController.get_template_amplitude(self, template_id)`** +**`FeatureMixin.get_spike_feature_amplitudes(self, spike_ids, channel_id=None, channel_ids=None, pc=None, **kwargs)`** -Return the maximum amplitude of a template's waveforms across all channels. +Return the features for the specified channel and PC. --- -#### TemplateController.get_template_counts - - -**`TemplateController.get_template_counts(self, cluster_id)`** +### phy.apps.FiringRateView -Return a histogram of the number of spikes in each template for a given cluster. +Histogram view showing the time-dependent firing rate. --- -#### TemplateController.get_template_for_cluster +#### FiringRateView.attach -**`TemplateController.get_template_for_cluster(self, cluster_id)`** +**`FiringRateView.attach(self, gui)`** -Return the largest template associated to a cluster. +Attach the view to the GUI. --- -#### TemplateController.on_save_clustering +#### FiringRateView.close -**`TemplateController.on_save_clustering(self, sender, spike_clusters, groups, *labels)`** +**`FiringRateView.close(self)`** -Save the modified data. +Close the underlying canvas. --- -#### TemplateController.peak_channel_similarity +#### FiringRateView.decrease -**`TemplateController.peak_channel_similarity(self, cluster_id)`** +**`FiringRateView.decrease(self)`** -Return the list of similar clusters to a given cluster, just on the basis of the -peak channel. +Decrease the scaling parameter. -**Parameters** +--- -* `cluster_id : int` +#### FiringRateView.get_clusters_data -**Returns** -* `similarities : list` - List of tuples `(other_cluster_id, similarity_value)` sorted by decreasing - similarity value. +**`FiringRateView.get_clusters_data(self, load_all=None)`** + +Return a list of Bunch instances, with attributes pos and spike_ids. + +To override. --- -#### TemplateController.template_similarity +#### FiringRateView.increase -**`TemplateController.template_similarity(self, cluster_id)`** +**`FiringRateView.increase(self)`** -Return the list of similar clusters to a given cluster. +Increase the scaling parameter. --- -### phy.apps.template.TemplateModel - -Object holding all data of a KiloSort/phy dataset. +#### FiringRateView.on_cluster -**Constructor** +**`FiringRateView.on_cluster(self, up)`** -* `dir_path : str or Path` - Path to the dataset directory +Callback function when a clustering action occurs. May be overriden. -* `dat_path : str, Path, or list` - Path to the raw data files. +Note: this method is called *before* on_select() so as to give a chance to the view +to update itself before the selection of the new clusters. -* `dtype : NumPy dtype` - Data type of the raw data file +This method is mostly only useful to views that show all clusters and not just the +selected clusters (template view, raster view). -* `offset : int` - Header offset of the binary file +--- -* `n_channels_dat : int` - Number of channels in the dat file +#### FiringRateView.on_mouse_wheel -* `sample_rate : float` - Sampling rate of the data file. -* `filter_order : int` - Order of the filter used for waveforms +**`FiringRateView.on_mouse_wheel(self, e)`** -* `hp_filtered : bool` - Whether the raw data file is already high-pass filtered. In that case, disable the - filtering for the waveform extraction. +Change the scaling with the wheel. --- -#### TemplateModel.describe +#### FiringRateView.on_select -**`TemplateModel.describe(self)`** +**`FiringRateView.on_select(self, cluster_ids=None, **kwargs)`** -Display basic information about the dataset. +Callback function when clusters are selected. May be overriden. --- -#### TemplateModel.get_cluster_channels +#### FiringRateView.plot -**`TemplateModel.get_cluster_channels(self, cluster_id)`** +**`FiringRateView.plot(self, **kwargs)`** -Return the most relevant channels of a cluster. +Update the view with the selected clusters. --- -#### TemplateModel.get_cluster_spike_waveforms +#### FiringRateView.reset_scaling -**`TemplateModel.get_cluster_spike_waveforms(self, cluster_id)`** +**`FiringRateView.reset_scaling(self)`** -Return all spike waveforms of a cluster, on the most relevant channels. +Reset the scaling to the default value. --- -#### TemplateModel.get_cluster_spikes +#### FiringRateView.screenshot -**`TemplateModel.get_cluster_spikes(self, cluster_id)`** +**`FiringRateView.screenshot(self, dir=None)`** -Return the spike ids that belong to a given template. +Save a PNG screenshot of the view into a given directory. By default, the screenshots +are saved in `~/.phy/screenshots/`. --- -#### TemplateModel.get_features +#### FiringRateView.set_n_bins -**`TemplateModel.get_features(self, spike_ids, channel_ids)`** +**`FiringRateView.set_n_bins(self, n_bins)`** + +Set the number of bins in the histogram. + +--- + +#### FiringRateView.set_state + + +**`FiringRateView.set_state(self, state)`** + +Set the view state. + +The passed object is the persisted `self.state` bunch. + +May be overriden. + +--- + +#### FiringRateView.set_status + + +**`FiringRateView.set_status(self, message=None)`** + +Set the status bar message in the GUI. + +--- + +#### FiringRateView.set_x_max + + +**`FiringRateView.set_x_max(self, x_max)`** + +Set the maximum value on the x axis for the histogram. + +--- + +#### FiringRateView.set_x_min + + +**`FiringRateView.set_x_min(self, x_min)`** + +Set the minimum value on the x axis for the histogram. + +--- + +#### FiringRateView.show + + +**`FiringRateView.show(self)`** + +Show the underlying canvas. + +--- + +#### FiringRateView.toggle_auto_update + + +**`FiringRateView.toggle_auto_update(self, checked)`** + +When on, the view is automatically updated when the cluster selection changes. + +--- + +#### FiringRateView.state + + +**`FiringRateView.state`** + +View state, a Bunch instance automatically persisted in the GUI state when the +GUI is closed. To be overriden. + +--- + +### phy.apps.ISIView + +Histogram view showing the interspike intervals. + +--- + +#### ISIView.attach + + +**`ISIView.attach(self, gui)`** + +Attach the view to the GUI. + +--- + +#### ISIView.close + + +**`ISIView.close(self)`** + +Close the underlying canvas. + +--- + +#### ISIView.decrease + + +**`ISIView.decrease(self)`** + +Decrease the scaling parameter. + +--- + +#### ISIView.get_clusters_data + + +**`ISIView.get_clusters_data(self, load_all=None)`** + +Return a list of Bunch instances, with attributes pos and spike_ids. + +To override. + +--- + +#### ISIView.increase + + +**`ISIView.increase(self)`** + +Increase the scaling parameter. + +--- + +#### ISIView.on_cluster + + +**`ISIView.on_cluster(self, up)`** + +Callback function when a clustering action occurs. May be overriden. + +Note: this method is called *before* on_select() so as to give a chance to the view +to update itself before the selection of the new clusters. + +This method is mostly only useful to views that show all clusters and not just the +selected clusters (template view, raster view). + +--- + +#### ISIView.on_mouse_wheel + + +**`ISIView.on_mouse_wheel(self, e)`** + +Change the scaling with the wheel. + +--- + +#### ISIView.on_select + + +**`ISIView.on_select(self, cluster_ids=None, **kwargs)`** + +Callback function when clusters are selected. May be overriden. + +--- + +#### ISIView.plot + + +**`ISIView.plot(self, **kwargs)`** + +Update the view with the selected clusters. + +--- + +#### ISIView.reset_scaling + + +**`ISIView.reset_scaling(self)`** + +Reset the scaling to the default value. + +--- + +#### ISIView.screenshot + + +**`ISIView.screenshot(self, dir=None)`** + +Save a PNG screenshot of the view into a given directory. By default, the screenshots +are saved in `~/.phy/screenshots/`. + +--- + +#### ISIView.set_n_bins + + +**`ISIView.set_n_bins(self, n_bins)`** + +Set the number of bins in the histogram. + +--- + +#### ISIView.set_state + + +**`ISIView.set_state(self, state)`** + +Set the view state. + +The passed object is the persisted `self.state` bunch. + +May be overriden. + +--- + +#### ISIView.set_status + + +**`ISIView.set_status(self, message=None)`** + +Set the status bar message in the GUI. + +--- + +#### ISIView.set_x_max + + +**`ISIView.set_x_max(self, x_max)`** + +Set the maximum value on the x axis for the histogram. + +--- + +#### ISIView.set_x_min + + +**`ISIView.set_x_min(self, x_min)`** + +Set the minimum value on the x axis for the histogram. + +--- + +#### ISIView.show + + +**`ISIView.show(self)`** + +Show the underlying canvas. + +--- + +#### ISIView.toggle_auto_update + + +**`ISIView.toggle_auto_update(self, checked)`** + +When on, the view is automatically updated when the cluster selection changes. + +--- + +#### ISIView.state + + +**`ISIView.state`** + +View state, a Bunch instance automatically persisted in the GUI state when the +GUI is closed. To be overriden. + +--- + +### phy.apps.QtDialogLogger + +Display a message box for all errors. + +--- + +#### QtDialogLogger.emit + + +**`QtDialogLogger.emit(self, record)`** + +Do whatever it takes to actually log the specified logging record. + +This version is intended to be implemented by subclasses and so +raises a NotImplementedError. + +--- + +### phy.apps.TemplateMixin + +Support templates. + +The model needs to implement specific properties and methods. + +amplitudes : array-like + The template amplitude of every spike (only with TemplateMixin). +n_templates : int + Initial number of templates. +spike_templates : array-like + The template initial id of every spike. +get_template(template_id) : int => Bunch(template, channel_ids) + Return the template data as a `(n_samples, n_channels)` array, the corresponding + channel ids of the template. + +--- + +#### TemplateMixin.create_template_view + + +**`TemplateMixin.create_template_view(self)`** + +Create a template view. + +--- + +#### TemplateMixin.get_amplitudes + + +**`TemplateMixin.get_amplitudes(self, cluster_id, load_all=False)`** + +Return the spike amplitudes found in `amplitudes.npy`, for a given cluster. + +--- + +#### TemplateMixin.get_cluster_amplitude + + +**`TemplateMixin.get_cluster_amplitude(self, cluster_id)`** + +Return the amplitude of the best template of a cluster. + +--- + +#### TemplateMixin.get_mean_spike_template_amplitudes + + +**`TemplateMixin.get_mean_spike_template_amplitudes(self, cluster_id)`** + +Return the average of the spike template amplitudes. + +--- + +#### TemplateMixin.get_spike_template_amplitudes + + +**`TemplateMixin.get_spike_template_amplitudes(self, spike_ids, **kwargs)`** + +Return the template amplitudes multiplied by the spike's amplitude. + +--- + +#### TemplateMixin.get_template_amplitude + + +**`TemplateMixin.get_template_amplitude(self, template_id)`** + +Return the maximum amplitude of a template's waveforms across all channels. + +--- + +#### TemplateMixin.get_template_counts + + +**`TemplateMixin.get_template_counts(self, cluster_id)`** + +Return a histogram of the number of spikes in each template for a given cluster. + +--- + +#### TemplateMixin.get_template_for_cluster + + +**`TemplateMixin.get_template_for_cluster(self, cluster_id)`** + +Return the largest template associated to a cluster. + +--- + +### phy.apps.TraceMixin + + + +--- + +#### TraceMixin.create_trace_view + + +**`TraceMixin.create_trace_view(self)`** + +Create a trace view. + +--- + +### phy.apps.WaveformMixin + + + +--- + +#### WaveformMixin.create_waveform_view + + +**`WaveformMixin.create_waveform_view(self)`** + + + +--- + +#### WaveformMixin.get_mean_spike_raw_amplitudes + + +**`WaveformMixin.get_mean_spike_raw_amplitudes(self, cluster_id)`** + +Return the average of the spike raw amplitudes. + +--- + +#### WaveformMixin.get_spike_raw_amplitudes + + +**`WaveformMixin.get_spike_raw_amplitudes(self, spike_ids, channel_ids=None, **kwargs)`** + +Return the maximum amplitude of the raw waveforms across all channels. + +--- + +## phy.apps.template + +Template GUI. + +--- + +#### phy.apps.template.from_sparse + + +**`phy.apps.template.from_sparse(data, cols, channel_ids)`** + +Convert a sparse structure into a dense one. + +**Parameters** + + +* `data : array-like` + A (n_spikes, n_channels_loc, ...) array with the data. + +* `cols : array-like` + A (n_spikes, n_channels_loc) array with the channel indices of + every row in data. + +* `channel_ids : array-like` + List of requested channel ids (columns). + +--- + +#### phy.apps.template.get_template_params + + +**`phy.apps.template.get_template_params(params_path)`** + +Get a dictionary of parameters from a `params.py` file. + +--- + +#### phy.apps.template.load_model + + +**`phy.apps.template.load_model(params_path)`** + +Return a TemplateModel instance from a path to a `params.py` file. + +--- + +#### phy.apps.template.template_describe + + +**`phy.apps.template.template_describe(params_path)`** + +Describe a template dataset. + +--- + +#### phy.apps.template.template_gui + + +**`phy.apps.template.template_gui(params_path, **kwargs)`** + +Launch the Template GUI. + +--- + +### phy.apps.template.TemplateController + +Controller for the Template GUI. + +**Constructor** + +* `dir_path : str or Path` + Path to the data directory + +* `config_dir : str or Path` + Path to the configuration directory + +* `model : Model` + Model object, optional (it is automatically created otherwise) + +* `plugins : list` + List of plugins to manually activate, optional (the plugins are automatically loaded from + the user configuration directory). + +* `clear_cache : boolean` + Whether to clear the cache on startup. + +* `enable_threading : boolean` + Whether to enable threading in the views when selecting clusters. + +--- + +#### TemplateController.at_least_one_view + + +**`TemplateController.at_least_one_view(self, view_name)`** + +Add a view of a given type if there is not already one. + +To be called before creating a GUI. + +--- + +#### TemplateController.create_amplitude_view + + +**`TemplateController.create_amplitude_view(self)`** + + + +--- + +#### TemplateController.create_correlogram_view + + +**`TemplateController.create_correlogram_view(self)`** + +Create a correlogram view. + +--- + +#### TemplateController.create_feature_view + + +**`TemplateController.create_feature_view(self)`** + + + +--- + +#### TemplateController.create_gui + + +**`TemplateController.create_gui(self, default_views=None, **kwargs)`** + +Create the GUI. + +**Constructor** + + +* `default_views : list` + List of views to add in the GUI, optional. By default, all views from the view + count are added. + +--- + +#### TemplateController.create_ipython_view + + +**`TemplateController.create_ipython_view(self)`** + +Create an IPython View. + +--- + +#### TemplateController.create_probe_view + + +**`TemplateController.create_probe_view(self)`** + +Create a probe view. + +--- + +#### TemplateController.create_raster_view + + +**`TemplateController.create_raster_view(self)`** + +Create a raster view. + +--- + +#### TemplateController.create_template_feature_view + + +**`TemplateController.create_template_feature_view(self)`** + + + +--- + +#### TemplateController.create_template_view + + +**`TemplateController.create_template_view(self)`** + +Create a template view. + +--- + +#### TemplateController.create_trace_view + + +**`TemplateController.create_trace_view(self)`** + +Create a trace view. + +--- + +#### TemplateController.create_waveform_view + + +**`TemplateController.create_waveform_view(self)`** + + + +--- + +#### TemplateController.get_amplitudes + + +**`TemplateController.get_amplitudes(self, cluster_id, load_all=False)`** + +Return the spike amplitudes found in `amplitudes.npy`, for a given cluster. + +--- + +#### TemplateController.get_background_spike_ids + + +**`TemplateController.get_background_spike_ids(self, n=None)`** + +Return regularly spaced spikes. + +--- + +#### TemplateController.get_best_channel + + +**`TemplateController.get_best_channel(self, cluster_id)`** + +Return the best channel of a given cluster. This is the first channel returned +by `get_best_channels()`. + +--- + +#### TemplateController.get_best_channels + + +**`TemplateController.get_best_channels(self, cluster_id)`** + +Return the best channels of a given cluster. + +--- + +#### TemplateController.get_channel_shank + + +**`TemplateController.get_channel_shank(self, cluster_id)`** + +Return the shank of a cluster's best channel, if the channel_shanks array is available. + +--- + +#### TemplateController.get_cluster_amplitude + + +**`TemplateController.get_cluster_amplitude(self, cluster_id)`** + +Return the amplitude of the best template of a cluster. + +--- + +#### TemplateController.get_clusters_on_channel + + +**`TemplateController.get_clusters_on_channel(self, channel_id)`** + +Return all clusters which have the specified channel among their best channels. + +--- + +#### TemplateController.get_mean_firing_rate + + +**`TemplateController.get_mean_firing_rate(self, cluster_id)`** + +Return the mean firing rate of a cluster. + +--- + +#### TemplateController.get_mean_spike_raw_amplitudes + + +**`TemplateController.get_mean_spike_raw_amplitudes(self, cluster_id)`** + +Return the average of the spike raw amplitudes. + +--- + +#### TemplateController.get_mean_spike_template_amplitudes + + +**`TemplateController.get_mean_spike_template_amplitudes(self, cluster_id)`** + +Return the average of the spike template amplitudes. + +--- + +#### TemplateController.get_probe_depth + + +**`TemplateController.get_probe_depth(self, cluster_id)`** + +Return the depth of a cluster. + +--- + +#### TemplateController.get_spike_feature_amplitudes + + +**`TemplateController.get_spike_feature_amplitudes(self, spike_ids, channel_id=None, channel_ids=None, pc=None, **kwargs)`** + +Return the features for the specified channel and PC. + +--- + +#### TemplateController.get_spike_ids + + +**`TemplateController.get_spike_ids(self, cluster_id, n=None)`** + +Return part or all of spike ids belonging to a given cluster. + +--- + +#### TemplateController.get_spike_raw_amplitudes + + +**`TemplateController.get_spike_raw_amplitudes(self, spike_ids, channel_ids=None, **kwargs)`** + +Return the maximum amplitude of the raw waveforms across all channels. + +--- + +#### TemplateController.get_spike_template_amplitudes + + +**`TemplateController.get_spike_template_amplitudes(self, spike_ids, **kwargs)`** + +Return the template amplitudes multiplied by the spike's amplitude. + +--- + +#### TemplateController.get_spike_times + + +**`TemplateController.get_spike_times(self, cluster_id, n=None)`** + +Return the spike times of spikes returned by `get_spike_ids(cluster_id, n)`. + +--- + +#### TemplateController.get_template_amplitude + + +**`TemplateController.get_template_amplitude(self, template_id)`** + +Return the maximum amplitude of a template's waveforms across all channels. + +--- + +#### TemplateController.get_template_counts + + +**`TemplateController.get_template_counts(self, cluster_id)`** + +Return a histogram of the number of spikes in each template for a given cluster. + +--- + +#### TemplateController.get_template_for_cluster + + +**`TemplateController.get_template_for_cluster(self, cluster_id)`** + +Return the largest template associated to a cluster. + +--- + +#### TemplateController.on_save_clustering + + +**`TemplateController.on_save_clustering(self, sender, spike_clusters, groups, *labels)`** + +Save the modified data. + +--- + +#### TemplateController.peak_channel_similarity + + +**`TemplateController.peak_channel_similarity(self, cluster_id)`** + +Return the list of similar clusters to a given cluster, just on the basis of the +peak channel. + +**Parameters** + +* `cluster_id : int` + +**Returns** + +* `similarities : list` + List of tuples `(other_cluster_id, similarity_value)` sorted by decreasing + similarity value. + +--- + +#### TemplateController.template_similarity + + +**`TemplateController.template_similarity(self, cluster_id)`** + +Return the list of similar clusters to a given cluster. + +--- + +### phy.apps.template.TemplateModel + +Object holding all data of a KiloSort/phy dataset. + +**Constructor** + + +* `dir_path : str or Path` + Path to the dataset directory + +* `dat_path : str, Path, or list` + Path to the raw data files. + +* `dtype : NumPy dtype` + Data type of the raw data file + +* `offset : int` + Header offset of the binary file + +* `n_channels_dat : int` + Number of channels in the dat file + +* `sample_rate : float` + Sampling rate of the data file. + +* `filter_order : int` + Order of the filter used for waveforms + +* `hp_filtered : bool` + Whether the raw data file is already high-pass filtered. In that case, disable the + filtering for the waveform extraction. + +--- + +#### TemplateModel.describe + + +**`TemplateModel.describe(self)`** + +Display basic information about the dataset. + +--- + +#### TemplateModel.get_cluster_channels + + +**`TemplateModel.get_cluster_channels(self, cluster_id)`** + +Return the most relevant channels of a cluster. + +--- + +#### TemplateModel.get_cluster_spike_waveforms + + +**`TemplateModel.get_cluster_spike_waveforms(self, cluster_id)`** + +Return all spike waveforms of a cluster, on the most relevant channels. + +--- + +#### TemplateModel.get_cluster_spikes + + +**`TemplateModel.get_cluster_spikes(self, cluster_id)`** + +Return the spike ids that belong to a given template. + +--- + +#### TemplateModel.get_features + + +**`TemplateModel.get_features(self, spike_ids, channel_ids)`** Return sparse features for given spikes. @@ -9669,3 +10646,297 @@ a TSV file. Save the spike clusters. --- + +## phy.apps.kwik + +Kwik GUI. + +--- + +#### phy.apps.kwik.kwik_describe + + +**`phy.apps.kwik.kwik_describe(path, channel_group=None, clustering=None)`** + +Describe a template dataset. + +--- + +#### phy.apps.kwik.kwik_gui + + +**`phy.apps.kwik.kwik_gui(path, channel_group=None, clustering=None, **kwargs)`** + +Launch the Kwik GUI. + +--- + +### phy.apps.kwik.KwikController + +Controller for the Kwik GUI. + +**Constructor** + +* `kwik_path : str or Path` + Path to the kwik file + +* `channel_group : int` + The default channel group to load + +* `clustering : str` + The default clustering to load + +* `config_dir : str or Path` + Path to the configuration directory + +* `model : Model` + Model object, optional (it is automatically created otherwise) + +* `plugins : list` + List of plugins to manually activate, optional (the plugins are automatically loaded from + the user configuration directory). + +* `clear_cache : boolean` + Whether to clear the cache on startup. + +* `enable_threading : boolean` + Whether to enable threading in the views when selecting clusters. + +--- + +#### KwikController.at_least_one_view + + +**`KwikController.at_least_one_view(self, view_name)`** + +Add a view of a given type if there is not already one. + +To be called before creating a GUI. + +--- + +#### KwikController.create_amplitude_view + + +**`KwikController.create_amplitude_view(self)`** + + + +--- + +#### KwikController.create_correlogram_view + + +**`KwikController.create_correlogram_view(self)`** + +Create a correlogram view. + +--- + +#### KwikController.create_feature_view + + +**`KwikController.create_feature_view(self)`** + + + +--- + +#### KwikController.create_gui + + +**`KwikController.create_gui(self, default_views=None, **kwargs)`** + +Create the GUI. + +**Constructor** + + +* `default_views : list` + List of views to add in the GUI, optional. By default, all views from the view + count are added. + +--- + +#### KwikController.create_ipython_view + + +**`KwikController.create_ipython_view(self)`** + +Create an IPython View. + +--- + +#### KwikController.create_probe_view + + +**`KwikController.create_probe_view(self)`** + +Create a probe view. + +--- + +#### KwikController.create_raster_view + + +**`KwikController.create_raster_view(self)`** + +Create a raster view. + +--- + +#### KwikController.create_trace_view + + +**`KwikController.create_trace_view(self)`** + +Create a trace view. + +--- + +#### KwikController.create_waveform_view + + +**`KwikController.create_waveform_view(self)`** + + + +--- + +#### KwikController.get_background_spike_ids + + +**`KwikController.get_background_spike_ids(self, n=None)`** + +Return regularly spaced spikes. + +--- + +#### KwikController.get_best_channel + + +**`KwikController.get_best_channel(self, cluster_id)`** + +Return the best channel of a given cluster. This is the first channel returned +by `get_best_channels()`. + +--- + +#### KwikController.get_best_channels + + +**`KwikController.get_best_channels(self, cluster_id)`** + +Get the best channels of a given cluster. + +--- + +#### KwikController.get_channel_shank + + +**`KwikController.get_channel_shank(self, cluster_id)`** + +Return the shank of a cluster's best channel, if the channel_shanks array is available. + +--- + +#### KwikController.get_clusters_on_channel + + +**`KwikController.get_clusters_on_channel(self, channel_id)`** + +Return all clusters which have the specified channel among their best channels. + +--- + +#### KwikController.get_mean_firing_rate + + +**`KwikController.get_mean_firing_rate(self, cluster_id)`** + +Return the mean firing rate of a cluster. + +--- + +#### KwikController.get_mean_spike_raw_amplitudes + + +**`KwikController.get_mean_spike_raw_amplitudes(self, cluster_id)`** + +Return the average of the spike raw amplitudes. + +--- + +#### KwikController.get_probe_depth + + +**`KwikController.get_probe_depth(self, cluster_id)`** + +Return the depth of a cluster. + +--- + +#### KwikController.get_spike_feature_amplitudes + + +**`KwikController.get_spike_feature_amplitudes(self, spike_ids, channel_id=None, channel_ids=None, pc=None, **kwargs)`** + +Return the features for the specified channel and PC. + +--- + +#### KwikController.get_spike_ids + + +**`KwikController.get_spike_ids(self, cluster_id, n=None)`** + +Return part or all of spike ids belonging to a given cluster. + +--- + +#### KwikController.get_spike_raw_amplitudes + + +**`KwikController.get_spike_raw_amplitudes(self, spike_ids, channel_ids=None, **kwargs)`** + +Return the maximum amplitude of the raw waveforms across all channels. + +--- + +#### KwikController.get_spike_times + + +**`KwikController.get_spike_times(self, cluster_id, n=None)`** + +Return the spike times of spikes returned by `get_spike_ids(cluster_id, n)`. + +--- + +#### KwikController.on_save_clustering + + +**`KwikController.on_save_clustering(self, sender, spike_clusters, groups, *labels)`** + +Save the modified data. + +--- + +#### KwikController.peak_channel_similarity + + +**`KwikController.peak_channel_similarity(self, cluster_id)`** + +Return the list of similar clusters to a given cluster, just on the basis of the +peak channel. + +**Parameters** + +* `cluster_id : int` + +**Returns** + +* `similarities : list` + List of tuples `(other_cluster_id, similarity_value)` sorted by decreasing + similarity value. + +--- diff --git a/phy/apps/__init__.py b/phy/apps/__init__.py index 4b26e665b..3be9657ed 100644 --- a/phy/apps/__init__.py +++ b/phy/apps/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# flake8: noqa """CLI tool.""" @@ -21,6 +22,10 @@ from phy.gui.qt import QtDialogLogger from phy.utils.profiling import _enable_profiler, _enable_pdb +from .base import ( + BaseController, ISIView, FiringRateView, + WaveformMixin, FeatureMixin, TemplateMixin, TraceMixin) + logger = logging.getLogger(__name__) @@ -83,17 +88,6 @@ def capture_exceptions(): # pragma: no cover logger.debug("Stop capturing exceptions.") -def _add_log_file(filename): # pragma: no cover - """Create a `phy.log` log file with DEBUG level in the - current directory.""" - handler = logging.FileHandler(filename) - - handler.setLevel(logging.DEBUG) - formatter = _Formatter(fmt=_logger_fmt, datefmt=_logger_date_fmt) - handler.setFormatter(formatter) - logging.getLogger('phy').addHandler(handler) - - #------------------------------------------------------------------------------ # Root CLI tool #------------------------------------------------------------------------------ diff --git a/phy/apps/base.py b/phy/apps/base.py index 2aa9895f5..5591e5de5 100644 --- a/phy/apps/base.py +++ b/phy/apps/base.py @@ -16,6 +16,7 @@ import numpy as np +from phylib import _add_log_file from phylib.io.array import Selector, _index_of, _flatten from phylib.stats import correlograms, firing_rate from phylib.utils import Bunch, emit, connect, unconnect @@ -27,7 +28,6 @@ WaveformView, FeatureView, TraceView, CorrelogramView, AmplitudeView, ScatterView, ProbeView, RasterView, TemplateView, HistogramView, select_traces) from phy.cluster.views.trace import _iter_spike_waveforms -from . import _add_log_file from phy.gui import GUI from phy.gui.gui import _prompt_save from phy.gui.qt import AsyncCaller @@ -137,9 +137,13 @@ def _get_waveforms_with_n_spikes(self, cluster_id, n_spikes_waveforms, batch_siz spike_ids = self.selector.select_spikes( [cluster_id], n_spikes_waveforms, batch_size_waveforms) channel_ids = self.get_best_channels(cluster_id) + channel_labels = ['%d' % ch for ch in self.model.channel_mapping[channel_ids]] data = self.model.get_waveforms(spike_ids, channel_ids) data = data - data.mean() if data is not None else None - return Bunch(data=data, channel_ids=channel_ids, channel_positions=pos[channel_ids]) + return Bunch( + data=data, channel_ids=channel_ids, + channel_labels=channel_labels, + channel_positions=pos[channel_ids]) def _get_waveforms(self, cluster_id): """Return a selection of waveforms for a cluster.""" @@ -261,7 +265,9 @@ def _get_spike_features(self, spike_ids, channel_ids): data[np.isnan(data)] = 0 assert data.shape[:2] == (len(spike_ids), len(channel_ids)) assert np.isnan(data).sum() == 0 - return Bunch(data=data, spike_ids=spike_ids, channel_ids=channel_ids) + channel_labels = ['%d' % ch for ch in self.model.channel_mapping[channel_ids]] + return Bunch( + data=data, spike_ids=spike_ids, channel_ids=channel_ids, channel_labels=channel_labels) def _get_features(self, cluster_id=None, channel_ids=None, load_all=False): """Return the features of a given cluster on specified channels.""" @@ -427,8 +433,8 @@ def _get_all_templates(self, cluster_ids): return { cluster_id: Bunch( template=bunchs[cluster_id].data[0, ...] * mean_amp[cluster_id], - channel_ids=bunchs[cluster_id].channel_ids) - for cluster_id in cluster_ids} + channel_ids=bunchs[cluster_id].channel_ids, + ) for cluster_id in cluster_ids} def _set_view_creator(self): super(TemplateMixin, self)._set_view_creator() @@ -439,6 +445,7 @@ def create_template_view(self): view = TemplateView( templates=self._get_all_templates, channel_ids=np.arange(self.model.n_channels), + channel_labels=['%d' % ch for ch in self.model.channel_mapping], cluster_color_selector=self.supervisor.color_selector, ) self._attach_global_view(view) @@ -490,6 +497,7 @@ def create_trace_view(self): traces=self._get_traces, spike_times=self._trace_spike_times, n_channels=self.model.n_channels, + channel_labels=['%d' % ch for ch in self.model.channel_mapping], sample_rate=self.model.sample_rate, duration=self.model.duration, channel_vertical_order=getattr(self.model, 'channel_vertical_order', None), @@ -570,6 +578,9 @@ class BaseController(object): The Model can be any object, but it needs to implement the following properties and methods in order to work with the BaseController: + channel_mapping : array-like + A `(n_channels,)` array with the column index in the raw data array of every channel. + The displayed channel label of channel `channel_id` is `channel_mapping[channel_id]`. channel_positions : array-like A `(n_channels, 2)` array with the x, y coordinates of the electrode sites, in any unit (e.g. μm). diff --git a/phy/apps/template/gui.py b/phy/apps/template/gui.py index f27872d8a..8ccdda3a3 100644 --- a/phy/apps/template/gui.py +++ b/phy/apps/template/gui.py @@ -13,12 +13,12 @@ import numpy as np +from phylib import _add_log_file from phylib.io.model import TemplateModel, get_template_params, load_model from phylib.utils import Bunch, connect from phy.cluster.views import ScatterView from phy.gui import create_app, run_app -from .. import _add_log_file from ..base import WaveformMixin, FeatureMixin, TemplateMixin, TraceMixin, BaseController logger = logging.getLogger(__name__) diff --git a/phy/cluster/views/tests/test_waveform.py b/phy/cluster/views/tests/test_waveform.py index 1c33ed42e..06ce25bf0 100644 --- a/phy/cluster/views/tests/test_waveform.py +++ b/phy/cluster/views/tests/test_waveform.py @@ -31,6 +31,7 @@ def get_waveforms(cluster_id): return Bunch( data=w, channel_ids=np.arange(nc), + channel_labels=['%d' % (ch * 10) for ch in range(nc)], waveform_duration=1000, channel_positions=staggered_positions(nc)) diff --git a/tools/api.py b/tools/api.py index 015361d53..3d38683d2 100644 --- a/tools/api.py +++ b/tools/api.py @@ -246,7 +246,7 @@ def generate_api_doc(package, subpackages, path=None): if __name__ == '__main__': package = 'phy' - subpackages = ['utils', 'gui', 'plot', 'cluster', 'apps.template'] + subpackages = ['utils', 'gui', 'plot', 'cluster', 'apps', 'apps.template', 'apps.kwik'] curdir = op.dirname(op.realpath(__file__)) path = op.join(curdir, '../docs/api.md')