Skip to content

Commit db448f7

Browse files
author
Thinh Nguyen
committed
add QC to ephys_no_curation
1 parent ef486ed commit db448f7

File tree

1 file changed

+70
-1
lines changed

1 file changed

+70
-1
lines changed

element_array_ephys/ephys_no_curation.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import importlib
77
import gc
88
from decimal import Decimal
9+
import pandas as pd
910

1011
from element_interface.utils import find_root_directory, find_full_path, dict_to_uuid
1112

@@ -683,7 +684,7 @@ class Unit(dj.Part):
683684
spike_count: int # how many spikes in this recording for this unit
684685
spike_times: longblob # (s) spike times of this unit, relative to the start of the EphysRecording
685686
spike_sites : longblob # array of electrode associated with each spike
686-
spike_depths : longblob # (um) array of depths associated with each spike, relative to the (0, 0) of the probe
687+
spike_depths=null : longblob # (um) array of depths associated with each spike, relative to the (0, 0) of the probe
687688
"""
688689

689690
def make(self, key):
@@ -845,6 +846,74 @@ def yield_unit_waveforms():
845846
self.Waveform.insert(unit_electrode_waveforms, ignore_extra_fields=True)
846847

847848

849+
@schema
850+
class QualityMetrics(dj.Imported):
851+
definition = """
852+
# Clusters and waveforms metrics
853+
-> CuratedClustering
854+
"""
855+
856+
class Cluster(dj.Part):
857+
definition = """
858+
# Cluster metrics for a particular unit
859+
-> master
860+
-> CuratedClustering.Unit
861+
---
862+
firing_rate=null: float # (Hz) firing rate for a unit
863+
snr=null: float # signal-to-noise ratio for a unit
864+
presence_ratio=null: float # fraction of time in which spikes are present
865+
isi_violation=null: float # rate of ISI violation as a fraction of overall rate
866+
number_violation=null: int # total number of ISI violations
867+
amplitude_cutoff=null: float # estimate of miss rate based on amplitude histogram
868+
isolation_distance=null: float # distance to nearest cluster in Mahalanobis space
869+
l_ratio=null: float #
870+
d_prime=null: float # Classification accuracy based on LDA
871+
nn_hit_rate=null: float # Fraction of neighbors for target cluster that are also in target cluster
872+
nn_miss_rate=null: float # Fraction of neighbors outside target cluster that are in target cluster
873+
silhouette_score=null: float # Standard metric for cluster overlap
874+
max_drift=null: float # Maximum change in spike depth throughout recording
875+
cumulative_drift=null: float # Cumulative change in spike depth throughout recording
876+
contamination_rate=null: float #
877+
"""
878+
879+
class Waveform(dj.Part):
880+
definition = """
881+
# Waveform metrics for a particular unit
882+
-> master
883+
-> CuratedClustering.Unit
884+
---
885+
amplitude: float # (uV) absolute difference between waveform peak and trough
886+
duration: float # (ms) time between waveform peak and trough
887+
halfwidth=null: float # (ms) spike width at half max amplitude
888+
pt_ratio=null: float # absolute amplitude of peak divided by absolute amplitude of trough relative to 0
889+
repolarization_slope=null: float # the repolarization slope was defined by fitting a regression line to the first 30us from trough to peak
890+
recovery_slope=null: float # the recovery slope was defined by fitting a regression line to the first 30us from peak to tail
891+
spread=null: float # (um) the range with amplitude above 12% of the maximum amplitude along the probe
892+
velocity_above=null: float # (s/m) inverse velocity of waveform propagation from the soma toward the top of the probe
893+
velocity_below=null: float # (s/m) inverse velocity of waveform propagation from the soma toward the bottom of the probe
894+
"""
895+
896+
def make(self, key):
897+
output_dir = (ClusteringTask & key).fetch1('clustering_output_dir')
898+
kilosort_dir = find_full_path(get_ephys_root_data_dir(), output_dir)
899+
900+
metric_fp = kilosort_dir / 'metrics.csv'
901+
902+
if not metric_fp.exists():
903+
raise FileNotFoundError(f'QC metrics file not found: {metric_fp}')
904+
905+
metrics_df = pd.read_csv(metric_fp)
906+
metrics_df.set_index('cluster_id', inplace=True)
907+
908+
metrics_list = [
909+
dict(metrics_df.loc[unit_key['unit']], **unit_key)
910+
for unit_key in (CuratedClustering.Unit & key).fetch('KEY')]
911+
912+
self.insert1(key)
913+
self.Cluster.insert(metrics_list, ignore_extra_fields=True)
914+
self.Waveform.insert(metrics_list, ignore_extra_fields=True)
915+
916+
848917
# ---------------- HELPER FUNCTIONS ----------------
849918

850919
def get_spikeglx_meta_filepath(ephys_recording_key):

0 commit comments

Comments
 (0)