Source code for neuroconv.datainterfaces.ecephys.phy.phydatainterface
import warnings
from pydantic import DirectoryPath, validate_call
from ..basesortingextractorinterface import BaseSortingExtractorInterface
from ....utils import DeepDict
[docs]
class PhySortingInterface(BaseSortingExtractorInterface):
"""
Primary data interface class for converting Phy data.
Uses :py:func:`~spikeinterface.extractors.read_phy` from SpikeInterface.
"""
display_name = "Phy Sorting"
associated_suffixes = (".npy",)
info = "Interface for Phy sorting data."
[docs]
@classmethod
def get_source_schema(cls) -> dict:
source_schema = super().get_source_schema()
source_schema["properties"]["exclude_cluster_groups"]["items"] = dict(type="string")
source_schema["properties"]["folder_path"][
"description"
] = "Path to the output Phy folder (containing the params.py)."
return source_schema
[docs]
@classmethod
def get_extractor_class(cls):
from spikeinterface.extractors.extractor_classes import read_phy
return read_phy
@validate_call
def __init__(
self,
folder_path: DirectoryPath,
*args, # TODO: change to * (keyword only) on or after August 2026
exclude_cluster_groups: list[str] | None = None,
verbose: bool = False,
):
"""
Initialize a PhySortingInterface.
Parameters
----------
folder_path : str or Path
Path to the output Phy folder (containing the params.py).
exclude_cluster_groups : str or list of str, optional
Cluster groups to exclude (e.g. "noise" or ["noise", "mua"]).
verbose : bool, default: False
"""
# Handle deprecated positional arguments
if args:
parameter_names = [
"exclude_cluster_groups",
"verbose",
]
num_positional_args_before_args = 1 # folder_path
if len(args) > len(parameter_names):
raise TypeError(
f"__init__() takes at most {len(parameter_names) + num_positional_args_before_args + 1} positional arguments but "
f"{len(args) + num_positional_args_before_args + 1} were given. "
"Note: Positional arguments are deprecated and will be removed on or after August 2026. "
"Please use keyword arguments."
)
positional_values = dict(zip(parameter_names, args))
passed_as_positional = list(positional_values.keys())
warnings.warn(
f"Passing arguments positionally to PhySortingInterface.__init__() is deprecated "
f"and will be removed on or after August 2026. "
f"The following arguments were passed positionally: {passed_as_positional}. "
"Please use keyword arguments instead.",
FutureWarning,
stacklevel=2,
)
exclude_cluster_groups = positional_values.get("exclude_cluster_groups", exclude_cluster_groups)
verbose = positional_values.get("verbose", verbose)
super().__init__(folder_path=folder_path, exclude_cluster_groups=exclude_cluster_groups, verbose=verbose)
[docs]
def get_metadata(self) -> DeepDict:
metadata = super().get_metadata()
# See Kilosort save_to_phy() docstring for more info on these fields: https://github.com/MouseLand/Kilosort/blob/main/kilosort/io.py
# Or see phy documentation: https://github.com/cortex-lab/phy/blob/master/phy/apps/base.py
metadata["Ecephys"]["UnitProperties"] = [
dict(name="n_spikes", description="Number of spikes recorded from each unit."),
dict(name="fr", description="Average firing rate of each unit."),
dict(name="depth", description="Estimated depth of each unit in micrometers."),
dict(name="Amplitude", description="Per-template amplitudes, computed as the L2 norm of the template."),
dict(
name="ContamPct",
description="Contamination rate for each template, computed as fraction of refractory period violations relative to expectation based on a Poisson process.",
),
dict(
name="KSLabel",
description="Label indicating whether each template is 'mua' (multi-unit activity) or 'good' (refractory).",
),
dict(name="original_cluster_id", description="Original cluster ID assigned by Kilosort."),
dict(
name="amp",
description="For every template, the maximum amplitude of the template waveforms across all channels.",
),
dict(name="ch", description="The channel label of the best channel, as defined by the user."),
dict(name="sh", description="The shank label of the best channel."),
]
return metadata