From 4183451e3209c370792a2ba4742ee241ef2c1213 Mon Sep 17 00:00:00 2001 From: Jonah Pearl Date: Tue, 13 Aug 2024 12:23:41 -0400 Subject: [PATCH 1/4] allow quality and template metrics in unit table --- src/spikeinterface/widgets/sorting_summary.py | 9 ++- .../widgets/utils_sortingview.py | 57 +++++++++++++++++-- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/spikeinterface/widgets/sorting_summary.py b/src/spikeinterface/widgets/sorting_summary.py index 6f60e9ab9a..4f9a9b7615 100644 --- a/src/spikeinterface/widgets/sorting_summary.py +++ b/src/spikeinterface/widgets/sorting_summary.py @@ -43,7 +43,12 @@ class SortingSummaryWidget(BaseWidget): List of labels to be added to the curation table (sortingview backend) unit_table_properties : list or None, default: None - List of properties to be added to the unit table + List of properties to be added to the unit table. + These may be drawn from the sorting extractor, and, if available, + the quality_metrics and template_metrics extensions of the SortingAnalyzer. + See all properties available with sorting.get_property_keys(), and, if available, + analyzer.get_extension("quality_metrics").get_data().columns and + analyzer.get_extension("template_metrics").get_data().columns. (sortingview backend) """ @@ -151,7 +156,7 @@ def plot_sortingview(self, data_plot, **backend_kwargs): # unit ids v_units_table = generate_unit_table_view( - dp.sorting_analyzer.sorting, dp.unit_table_properties, similarity_scores=similarity_scores + dp.sorting_analyzer, dp.unit_table_properties, similarity_scores=similarity_scores ) if dp.curation: diff --git a/src/spikeinterface/widgets/utils_sortingview.py b/src/spikeinterface/widgets/utils_sortingview.py index ab926a0104..1dbd4b9879 100644 --- a/src/spikeinterface/widgets/utils_sortingview.py +++ b/src/spikeinterface/widgets/utils_sortingview.py @@ -3,7 +3,7 @@ import numpy as np from ..core.core_tools import check_json - +from warnings import warn def make_serializable(*args): dict_to_serialize = {int(i): a for i, a in enumerate(args)} @@ -45,9 +45,30 @@ def handle_display_and_url(widget, view, **backend_kwargs): return url -def generate_unit_table_view(sorting, unit_properties=None, similarity_scores=None): +def generate_unit_table_view(analyzer, unit_properties=None, similarity_scores=None): import sortingview.views as vv + sorting = analyzer.sorting + + # Find available unit properties from all sources + sorting_props = sorting.get_property_keys() + if analyzer.get_extension("quality_metrics") is not None: + qm_props = analyzer.get_extension("quality_metrics").get_data().columns + qm_data = analyzer.get_extension("quality_metrics").get_data() + else: + qm_props = [] + if analyzer.get_extension("template_metrics") is not None: + tm_props = analyzer.get_extension("template_metrics").get_data().columns + tm_data = analyzer.get_extension("template_metrics").get_data() + else: + tm_props = [] + # Check for any overlaps and warn user if any + all_props = sorting_props + qm_props + tm_props + overlap_props = [prop for prop in all_props if all_props.count(prop) > 1] + if len(overlap_props) > 0: + warn(f"Warning: Overlapping properties found in sorting, quality_metrics, and template_metrics: {overlap_props}") + + # Get unit properties if unit_properties is None: ut_columns = [] ut_rows = [vv.UnitsTableRow(unit_id=u, values={}) for u in sorting.unit_ids] @@ -56,8 +77,22 @@ def generate_unit_table_view(sorting, unit_properties=None, similarity_scores=No ut_rows = [] values = {} valid_unit_properties = [] + + # Create columns for each property for prop_name in unit_properties: - property_values = sorting.get_property(prop_name) + + # Get property values from correct location + # import pdb + # pdb.set_trace() + if prop_name in sorting_props: + property_values = sorting.get_property(prop_name) + elif prop_name in qm_props: + property_values = qm_data[prop_name].values + elif prop_name in tm_props: + property_values = tm_data[prop_name].values + else: + raise ValueError(f"Property '{prop_name}' not found in sorting, quality_metrics, or template_metrics") + # make dtype available val0 = np.array(property_values[0]) if val0.dtype.kind in ("i", "u"): @@ -74,14 +109,26 @@ def generate_unit_table_view(sorting, unit_properties=None, similarity_scores=No ut_columns.append(vv.UnitsTableColumn(key=prop_name, label=prop_name, dtype=dtype)) valid_unit_properties.append(prop_name) + # Create rows for each unit for ui, unit in enumerate(sorting.unit_ids): for prop_name in valid_unit_properties: - property_values = sorting.get_property(prop_name) + + # Get property values from correct location + if prop_name in sorting_props: + property_values = sorting.get_property(prop_name) + elif prop_name in qm_props: + property_values = qm_data[prop_name].values + elif prop_name in tm_props: + property_values = tm_data[prop_name].values + else: + raise ValueError(f"Property '{prop_name}' not found in sorting, quality_metrics, or template_metrics") + + # Check for NaN values val0 = np.array(property_values[0]) if val0.dtype.kind == "f": if np.isnan(property_values[ui]): continue - values[prop_name] = property_values[ui] + values[prop_name] = np.format_float_positional(property_values[ui], precision=4, fractional=False) ut_rows.append(vv.UnitsTableRow(unit_id=unit, values=check_json(values))) v_units_table = vv.UnitsTable(rows=ut_rows, columns=ut_columns, similarity_scores=similarity_scores) From 1335ecfa1c14db8ae4c433dd79ced8f901891eee Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:29:40 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/spikeinterface/widgets/sorting_summary.py | 6 +++--- src/spikeinterface/widgets/utils_sortingview.py | 16 +++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/spikeinterface/widgets/sorting_summary.py b/src/spikeinterface/widgets/sorting_summary.py index 4f9a9b7615..a113298851 100644 --- a/src/spikeinterface/widgets/sorting_summary.py +++ b/src/spikeinterface/widgets/sorting_summary.py @@ -43,11 +43,11 @@ class SortingSummaryWidget(BaseWidget): List of labels to be added to the curation table (sortingview backend) unit_table_properties : list or None, default: None - List of properties to be added to the unit table. - These may be drawn from the sorting extractor, and, if available, + List of properties to be added to the unit table. + These may be drawn from the sorting extractor, and, if available, the quality_metrics and template_metrics extensions of the SortingAnalyzer. See all properties available with sorting.get_property_keys(), and, if available, - analyzer.get_extension("quality_metrics").get_data().columns and + analyzer.get_extension("quality_metrics").get_data().columns and analyzer.get_extension("template_metrics").get_data().columns. (sortingview backend) """ diff --git a/src/spikeinterface/widgets/utils_sortingview.py b/src/spikeinterface/widgets/utils_sortingview.py index 1dbd4b9879..d7fd222921 100644 --- a/src/spikeinterface/widgets/utils_sortingview.py +++ b/src/spikeinterface/widgets/utils_sortingview.py @@ -5,6 +5,7 @@ from ..core.core_tools import check_json from warnings import warn + def make_serializable(*args): dict_to_serialize = {int(i): a for i, a in enumerate(args)} serializable_dict = check_json(dict_to_serialize) @@ -47,8 +48,9 @@ def handle_display_and_url(widget, view, **backend_kwargs): def generate_unit_table_view(analyzer, unit_properties=None, similarity_scores=None): import sortingview.views as vv + sorting = analyzer.sorting - + # Find available unit properties from all sources sorting_props = sorting.get_property_keys() if analyzer.get_extension("quality_metrics") is not None: @@ -66,7 +68,9 @@ def generate_unit_table_view(analyzer, unit_properties=None, similarity_scores=N all_props = sorting_props + qm_props + tm_props overlap_props = [prop for prop in all_props if all_props.count(prop) > 1] if len(overlap_props) > 0: - warn(f"Warning: Overlapping properties found in sorting, quality_metrics, and template_metrics: {overlap_props}") + warn( + f"Warning: Overlapping properties found in sorting, quality_metrics, and template_metrics: {overlap_props}" + ) # Get unit properties if unit_properties is None: @@ -92,7 +96,7 @@ def generate_unit_table_view(analyzer, unit_properties=None, similarity_scores=N property_values = tm_data[prop_name].values else: raise ValueError(f"Property '{prop_name}' not found in sorting, quality_metrics, or template_metrics") - + # make dtype available val0 = np.array(property_values[0]) if val0.dtype.kind in ("i", "u"): @@ -121,8 +125,10 @@ def generate_unit_table_view(analyzer, unit_properties=None, similarity_scores=N elif prop_name in tm_props: property_values = tm_data[prop_name].values else: - raise ValueError(f"Property '{prop_name}' not found in sorting, quality_metrics, or template_metrics") - + raise ValueError( + f"Property '{prop_name}' not found in sorting, quality_metrics, or template_metrics" + ) + # Check for NaN values val0 = np.array(property_values[0]) if val0.dtype.kind == "f": From 3dc9da2d13ec13bf6b1b6c25111326181dc72cc9 Mon Sep 17 00:00:00 2001 From: Jonah Pearl Date: Thu, 15 Aug 2024 10:17:18 -0400 Subject: [PATCH 3/4] bug fix: convert props to lists --- src/spikeinterface/widgets/utils_sortingview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/spikeinterface/widgets/utils_sortingview.py b/src/spikeinterface/widgets/utils_sortingview.py index d7fd222921..0ce372efc4 100644 --- a/src/spikeinterface/widgets/utils_sortingview.py +++ b/src/spikeinterface/widgets/utils_sortingview.py @@ -52,14 +52,14 @@ def generate_unit_table_view(analyzer, unit_properties=None, similarity_scores=N sorting = analyzer.sorting # Find available unit properties from all sources - sorting_props = sorting.get_property_keys() + sorting_props = list(sorting.get_property_keys()) if analyzer.get_extension("quality_metrics") is not None: - qm_props = analyzer.get_extension("quality_metrics").get_data().columns + qm_props = list(analyzer.get_extension("quality_metrics").get_data().columns) qm_data = analyzer.get_extension("quality_metrics").get_data() else: qm_props = [] if analyzer.get_extension("template_metrics") is not None: - tm_props = analyzer.get_extension("template_metrics").get_data().columns + tm_props = list(analyzer.get_extension("template_metrics").get_data().columns) tm_data = analyzer.get_extension("template_metrics").get_data() else: tm_props = [] From 54959577e2b0cda69eb370fc25c1534c14c92c95 Mon Sep 17 00:00:00 2001 From: Garcia Samuel Date: Wed, 28 Aug 2024 12:53:39 +0200 Subject: [PATCH 4/4] Update src/spikeinterface/widgets/utils_sortingview.py --- src/spikeinterface/widgets/utils_sortingview.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/spikeinterface/widgets/utils_sortingview.py b/src/spikeinterface/widgets/utils_sortingview.py index 0ce372efc4..269193b341 100644 --- a/src/spikeinterface/widgets/utils_sortingview.py +++ b/src/spikeinterface/widgets/utils_sortingview.py @@ -86,8 +86,6 @@ def generate_unit_table_view(analyzer, unit_properties=None, similarity_scores=N for prop_name in unit_properties: # Get property values from correct location - # import pdb - # pdb.set_trace() if prop_name in sorting_props: property_values = sorting.get_property(prop_name) elif prop_name in qm_props: