@@ -977,6 +977,9 @@ def compute_dot_product(
977977 The dot product gives a measure of the similarity between two spike trains. Each match is weighted by the
978978 delta_frames - abs(frame1 - frame2) where frame1 and frame2 are the frames of the matching spikes.
979979
980+ Note that the maximum weight of a match is delta_frames. This happens when the two spikes are exactly
981+ delta_frames apart. The minimum weight is 0 which happens when the two spikes are more than delta_frames appart.
982+
980983 Note the function assumes that the spike frames are sorted in ascending order.
981984
982985 Parameters
@@ -1115,10 +1118,53 @@ def compute_square_norm(sample_frames, unit_indices, num_units, delta_frames):
11151118 return compute_square_norm
11161119
11171120
1118- def _compute_spike_vector_squared_norm (spike_vector_per_segment , num_units , delta_frames ):
1121+ def _compute_spike_vector_squared_norm (
1122+ spike_vector_per_segment : list [np .ndarray ],
1123+ num_units : int ,
1124+ delta_frames : int ,
1125+ ) -> np .ndarray :
1126+ """
1127+ Computes the squared norm of spike vectors for each unit across multiple segments.
1128+
1129+ This function calculates the squared norm for each unit in the provided spike vectors,
1130+ summing across different segments.
1131+
1132+ The norm is defined in the context of spike trains considered as box-car functions with
1133+ a specified width (delta_frames). The squared norm represents the integral of the squared spike train
1134+ when viewed as such a function.
1135+
1136+ The squared norm comprises two components:
1137+
1138+ ||x||^2 = num_spikes * delta_frames + self_match_component
1139+
1140+ 1. A sum of the number of spikes for a given unit multiplied by delta_frames, representing the total 'active'
1141+ duration of the spike train.
1142+ 2. A weighted sum of 'self-matches' within spikes from the same unit, where each match's weight depends on
1143+ the proximity of the spikes.
1144+
1145+ If no two spikes in a train are closer than delta_frames apart, the squared norm simplifies to the number of
1146+ spikes multiplied by delta_frames: ||x||^2 = delta_frames * num_spikes.
1147+
1148+
1149+ Parameters
1150+ ----------
1151+ spike_vector_per_segment : list of np.ndarray
1152+ A list containing spike vectors for each segment. Each spike vector is a structured numpy array with fields 'sample_index' and 'unit_index'.
1153+ num_units : int
1154+ The total number of units represented in the spike vectors.
1155+ delta_frames : int
1156+ The width of the box-car function, used in defining the norm.
1157+
1158+ Returns
1159+ -------
1160+ np.ndarray
1161+ A 1D numpy array of length `num_units`, where each entry represents the squared norm of the corresponding unit across all segments.
1162+
1163+ """
11191164 compute_squared_norm = get_compute_square_norm_function ()
11201165
11211166 squared_norm = np .zeros (num_units , dtype = np .uint64 )
1167+
11221168 # Note that the squared norms are integrals and can be added over segments
11231169 for spike_vector in spike_vector_per_segment :
11241170 sample_frames = spike_vector ["sample_index" ]
@@ -1134,12 +1180,42 @@ def _compute_spike_vector_squared_norm(spike_vector_per_segment, num_units, delt
11341180
11351181
11361182def _compute_spike_vector_dot_product (
1137- spike_vector_per_segment1 ,
1138- spike_vector_per_segment2 ,
1139- num_units1 ,
1140- num_units2 ,
1141- delta_frames ,
1142- ):
1183+ spike_vector_per_segment1 : list [np .ndarray ], # TODO Add a propert type to spike vector that we can reference
1184+ spike_vector_per_segment2 : list [np .ndarray ],
1185+ num_units1 : int ,
1186+ num_units2 : int ,
1187+ delta_frames : int ,
1188+ ) -> np .ndarray :
1189+ """
1190+ This function calculates the dot product for each pair of units between two sets of spike trains,
1191+ summing the results across different segments.
1192+
1193+ The dot product gives a measure of the similarity between two spike trains. The dot product here is induced by the
1194+ L2 norm in the Hilbert space of the spikes viewed as a box-car functions with width delta frames. Each match is
1195+ weighted by the delta_frames - abs(frame1 - frame2) where frame1 and frame2 are the frames of the matching spikes.
1196+
1197+ Note that the maximum weight of a match is delta_frames. This happens when the two spikes are exactly
1198+ delta_frames apart. The minimum weight is 0 which happens when the two spikes are more than delta_frames appart.
1199+
1200+
1201+ Parameters
1202+ ----------
1203+ spike_vector_per_segment1 : list of ndarray
1204+ A list of spike vectors for each segment of the first spike_vector.
1205+ spike_vector_per_segment2 : list of ndarray
1206+ A list of spike vectors for each segment of the second spike_vector.
1207+ num_units1 : int
1208+ The number of units in the first spike_vectors.
1209+ num_units2 : int
1210+ The number of units in the second spike_vectors.
1211+ delta_frames : int
1212+ The frame width to consider for the dot product calculation.
1213+
1214+ Returns
1215+ -------
1216+ dot_product_matrix : ndarray
1217+ A matrix containing the dot product for each pair of units between the two spike_vectors.
1218+ """
11431219 dot_product_matrix = np .zeros ((num_units1 , num_units2 ), dtype = np .uint64 )
11441220
11451221 compute_dot_product = get_compute_dot_product_function ()
@@ -1165,7 +1241,31 @@ def _compute_spike_vector_dot_product(
11651241 return dot_product_matrix
11661242
11671243
1168- def compute_distance_matrix (sorting1 : BaseSorting , sorting2 : BaseSorting , delta_frames : int ):
1244+ def compute_distance_matrix (sorting1 : BaseSorting , sorting2 : BaseSorting , delta_frames : int ) -> np .ndarray :
1245+ """
1246+ Computes a distance matrix between two sorting objects
1247+
1248+ This function calculates the L2 distance matrix between the spike train corresponding to units of
1249+ of the sorting extractors.
1250+
1251+ Each spike is considered as a box-car function with width delta_frames. The distance between two units is the
1252+ L2 distance between the two spike trains viewed as box-car functions. The distance then can be interpreted as
1253+ the integral of the squared difference between the two spike trains.
1254+
1255+ Parameters
1256+ ----------
1257+ sorting1 : BaseSorting
1258+ The first spike train set to compare.
1259+ sorting2 : BaseSorting
1260+ The second spike train set to compare.
1261+ delta_frames : int
1262+ The frame width to consider in distance calculations.
1263+
1264+ Returns
1265+ -------
1266+ distance_matrix : (num_units1, num_units2) ndarray (float)
1267+ A matrix representing the pairwise L2 distances between units of sorting objects.
1268+ """
11691269 num_units1 = sorting1 .get_num_units ()
11701270 num_units2 = sorting2 .get_num_units ()
11711271
@@ -1198,7 +1298,44 @@ def compute_distance_matrix(sorting1: BaseSorting, sorting2: BaseSorting, delta_
11981298 return distance_metrix
11991299
12001300
1201- def calculate_generalized_metrics (sorting1 : BaseSorting , sorting2 : BaseSorting , delta_frames : int ) -> dict [np .ndarray ]:
1301+ def calculate_generalized_comparison_metrics (
1302+ sorting1 : BaseSorting , sorting2 : BaseSorting , delta_frames : int
1303+ ) -> dict [np .ndarray ]:
1304+ """
1305+ Calculates generalized metrics between two sorting objects.
1306+
1307+ This function computes several metrics, including generalized accuracy, recall, precision, and cosine similarity
1308+ between the spike trains of two sorting objects. The calculations are based on the dot product and squared norms
1309+ of the spike vectors, where spikes are viewed as box-car functions with a width of delta_frames.
1310+
1311+ The generalized accuracy is a measure of the overall match between two sets of spike trains. Generalized recall
1312+ and precision are useful in scenarios where one of the sortings is considered as ground truth, and the other is
1313+ being evaluated against it. Cosine similarity gives a normalized measure of similarity between two spike trains.
1314+
1315+ Parameters
1316+ ----------
1317+ sorting1 : BaseSorting
1318+ The first set of spike trains, can be considered as the ground truth in recall calculation.
1319+ sorting2 : BaseSorting
1320+ The second set of spike trains, typically the set being evaluated.
1321+ delta_frames : int
1322+ The width of the box-car function, used in defining the spike train representation.
1323+
1324+ Returns
1325+ -------
1326+ dict of np.ndarray
1327+ A dictionary containing the computed metrics:
1328+ - 'accuracy': Generalized accuracy between the two sets of spike trains.
1329+ - 'recall': Generalized recall, assuming sorting1 as ground truth.
1330+ - 'precision': Generalized precision, evaluating sorting2 against sorting1.
1331+ - 'cosine_similarity': Cosine similarity between the spike trains of sorting1 and sorting2.
1332+
1333+ Notes
1334+ -----
1335+ - The metrics are calculated based on the dot product and squared norms of the spike trains, which are represented
1336+ as box-car functions.
1337+ - The function assumes that both sorting objects have the same number of segments.
1338+ """
12021339 num_units1 = sorting1 .get_num_units ()
12031340 num_units2 = sorting2 .get_num_units ()
12041341
@@ -1211,8 +1348,8 @@ def calculate_generalized_metrics(sorting1: BaseSorting, sorting2: BaseSorting,
12111348 num_segments_sorting1 == num_segments_sorting2
12121349 ), "make_match_count_matrix : sorting1 and sorting2 must have the same number of segments"
12131350
1214- squared_norm_1 = _compute_spike_vector_squared_norm (spike_vector1_segments , num_units2 , delta_frames )
1215- squared_norm_2 = _compute_spike_vector_squared_norm (spike_vector2_segments , num_units2 , delta_frames )
1351+ squared_norm1 = _compute_spike_vector_squared_norm (spike_vector1_segments , num_units2 , delta_frames )
1352+ squared_norm2 = _compute_spike_vector_squared_norm (spike_vector2_segments , num_units2 , delta_frames )
12161353
12171354 dot_product = _compute_spike_vector_dot_product (
12181355 spike_vector1_segments ,
@@ -1222,15 +1359,13 @@ def calculate_generalized_metrics(sorting1: BaseSorting, sorting2: BaseSorting,
12221359 delta_frames ,
12231360 )
12241361
1225- generalized_accuracy = dot_product / (squared_norm_1 + squared_norm_2 - dot_product )
1226- cosine_similarity = dot_product / np .sqrt (squared_norm_1 * squared_norm_2 )
1362+ generalized_accuracy = dot_product / (squared_norm1 + squared_norm2 - dot_product )
1363+ cosine_similarity = dot_product / np .sqrt (squared_norm1 * squared_norm2 )
12271364
1228- generalized_recall = dot_product / squared_norm_1 ** 2 # Assumes sorting1 is the ground truth
1229- generalized_precision = dot_product / squared_norm_2 ** 2 # Assumes sorting2 is the sorting that is being evaluated
1365+ generalized_recall = dot_product / squared_norm1 # Assumes sorting1 is the ground truth
1366+ generalized_precision = dot_product / squared_norm2 # Assumes sorting2 is the sorting that is being evaluated
12301367
1231- # Note that the generalized and recall can be written in terms of the cosine similarity
1232- # generalized_recall = cosine_similarity / (1 + cosine_similarity)
1233- # generalized_precision = cosine_similarity / (1 + cosine_similarity)
1368+ # TODO: Maybe distance should be here? who wants a distance by itself?
12341369
12351370 metrics = dict (
12361371 accuracy = generalized_accuracy ,
0 commit comments