Source code for pykrait.trace_analysis.synchronicity

import numpy as np
from typing import Tuple
import warnings

[docs] def find_synchronous_peaks( shortest_path_matrix: np.ndarray, peak_series: np.ndarray, neighbour_degree: int, frame_window: int, ) -> np.ndarray: """ finds the number of synchronous peaks between connected cells given a certain neighbourhood degree and time window. :param shortest_path_matrix: ROI x ROI matrix with shortest path lengths between cells n and m :type shortest_path_matrix: np.ndarray :param peak_series: T x ROI binary matrix with 1s at calcium peak frames :type peak_series: np.ndarray :param neighbour_degree: degree of neighbourhood to consider (1 = direct neighbours) :type neighbour_degree: int :param frame_window: time window within peaks are considered synchronous :type frame_window: int :return: returns a ROI x ROI matrix with the number of synchronous peaks between connected cells :rtype: np.ndarray """ synchronous_cells_matrix = np.zeros(shortest_path_matrix.shape, dtype=np.uint8) # find connected cells cell_indices = np.where( shortest_path_matrix == neighbour_degree ) if cell_indices[0].size == 0 or cell_indices[1].size == 0: warnings.warn(f"No connected cell pairs found for the specified neighbour degree of {neighbour_degree}.", UserWarning) return synchronous_cells_matrix peak_indices = [np.nonzero(t)[0] for t in peak_series.transpose()] for synchronous_cellpair in range(0, cell_indices[0].shape[0]): # calculates the frame-difference between all peaks of both actually neighbouring cells peak_differences = np.subtract.outer( peak_indices[cell_indices[0][synchronous_cellpair]], peak_indices[cell_indices[1][synchronous_cellpair]], ) # some complex logical operations, built-in .__and__ basically means # 0 < peak_differences <= self.FRAME_THRESHOLD synchronous_cells_matrix[ cell_indices[0][synchronous_cellpair], cell_indices[1][synchronous_cellpair] ] = ((0 <= peak_differences).__and__(peak_differences <= frame_window)).sum() synchronous_cells_matrix[ cell_indices[1][synchronous_cellpair], cell_indices[0][synchronous_cellpair] ] = ((-frame_window <= peak_differences).__and__(peak_differences < 0)).sum() return synchronous_cells_matrix
[docs] def find_possible_synchronous_peaks( shortest_path_matrix: np.ndarray, peak_series: np.ndarray, neighbour_degree: int, frame_window: int, ) -> np.ndarray: """ finds the number of possible synchronous peaks between connected cells given a certain neighbourhood degree and time window. This assumes that one peak can only be synchronous with one other peak from a connected cell, so the maximum number of synchronous events is limited by the cell with the fewer peaks. :param shortest_path_matrix: ROI x ROI matrix with shortest path lengths between cells n and m :type shortest_path_matrix: np.ndarray :param peak_series: T x ROI binary matrix with 1s at calcium peak frames :type peak_series: np.ndarray :param neighbour_degree: degree of neighbourhood to consider (1 = direct neighbours) :type neighbour_degree: int :param frame_window: time window within peaks are considered synchronous :type frame_window: int :return: returns a ROI x ROI matrix with the number of possible synchronous peaks between connected cells :rtype: np.ndarray """ possible_synchronous_cells_matrix = np.zeros(shortest_path_matrix.shape, dtype=np.uint16) # find connected cells cell_indices = np.where( shortest_path_matrix == neighbour_degree ) if cell_indices[0].size == 0 or cell_indices[1].size == 0: warnings.warn(f"No connected cell pairs found for the specified neighbour degree of {neighbour_degree}.", UserWarning) return possible_synchronous_cells_matrix n_peaks_per_cell = np.sum(peak_series, axis=0) for synchronous_cellpair in range(0, cell_indices[0].shape[0]): possible_synchronous_cells_matrix[ cell_indices[0][synchronous_cellpair], cell_indices[1][synchronous_cellpair] ] = min( n_peaks_per_cell[cell_indices[0][synchronous_cellpair]], n_peaks_per_cell[cell_indices[1][synchronous_cellpair]], ) return possible_synchronous_cells_matrix
def _random_synchronous_peaks( shortest_path_matrix: np.ndarray, peak_series: np.ndarray, neighbour_degree: int, frame_window: int, n_iter: int = 100, ) -> Tuple[np.ndarray, np.ndarray]: """ bootstraps the number of synchronous peaks between connected cells given a certain neighbourhood degree and time window by randomizing the peak series. :param shortest_path_matrix: ROI x ROI matrix with shortest path lengths between cells n and m :type shortest_path_matrix: np.ndarray :param peak_series: T x ROI binary matrix with 1s at calcium peak frames :type peak_series: np.ndarray :param neighbour_degree: degree of neighbourhood to consider (1 = direct neighbours) :type neighbour_degree: int :param frame_window: time window within peaks are considered synchronous :type frame_window: int :param n_iter: number of random permutations to bootstrap against, defaults to 100 :type n_iter: int, optional :return: returns a list of the number of synchronous peaks in the bootstrapped random control as well as the corresponding matrices :rtype: Tuple[np.ndarray, np.ndarray, np.ndarray] """ list_rand_matrices = np.zeros( shape=(n_iter, shortest_path_matrix.shape[0], shortest_path_matrix.shape[1]) ) count_rand_synchronous_peaks = np.zeros(shape=n_iter) for i in range(0, n_iter): # randomize the peak series orders = np.random.permutation(np.arange(peak_series.shape[1])) random_peak_series = peak_series[:, orders] # calculate the synchronous peaks list_rand_matrices[i, :, :] = find_synchronous_peaks( shortest_path_matrix=shortest_path_matrix, peak_series=random_peak_series, neighbour_degree=neighbour_degree, frame_window=frame_window, ) count_rand_synchronous_peaks[i] = np.sum(list_rand_matrices[i, :, :], axis=None) return count_rand_synchronous_peaks, list_rand_matrices
[docs] def calculate_synchronicity_zscore( shortest_path_matrix: np.ndarray, peak_series: np.ndarray, neighbour_degree: int, frame_window: int, n_iter=100, ) -> Tuple[float, float, np.ndarray, np.ndarray, np.ndarray]: """ calculates the zscore of the synchronicity between connected cells given a certain neighbourhood degree and time window. :param shortest_path_matrix: ROI x ROI matrix with shortest path lengths between cells n and m :type shortest_path_matrix: np.ndarray :param peak_series: T x ROI binary matrix with 1s at calcium peak frames :type peak_series: np.ndarray :param neighbour_degree: degree of neighbourhood to consider (1 = direct neighbours) :type neighbour_degree: int :param frame_window: time window within peaks are considered synchronous :type frame_window: int :param n_iter: number of random permutations to bootstrap against, defaults to 100 :type n_iter: int, optional :return: returns the zscore of the synchronicity, the true synchronous peaks matrix, one example random synchronous peaks matrix and the possible synchronous peaks matrix :rtype: Tuple[float, float, np.ndarray, np.ndarray, np.ndarray] """ # finds true connected peaks true_synchronous_peaks_matrix = find_synchronous_peaks( shortest_path_matrix, peak_series, neighbour_degree, frame_window ) true_synchronous_peaks = np.sum(true_synchronous_peaks_matrix, axis=None) # if there are no synchronous peaks, warn the user if true_synchronous_peaks == 0: warnings.warn(f"No synchronous peaks found between connected cells for neighbour {neighbour_degree}. Synchronicity z-score will be NaN.", UserWarning) return ( np.nan, np.nan, np.zeros(true_synchronous_peaks_matrix.shape), np.zeros(true_synchronous_peaks_matrix.shape), np.zeros(true_synchronous_peaks_matrix.shape) ) # finds random connected peaks to bootstrap against rand_synchronous_peaks, rand_synchronous_peaks_matrix = _random_synchronous_peaks( shortest_path_matrix, peak_series, neighbour_degree, frame_window, n_iter ) # calculates zscore true_synchronicity_zscore = (true_synchronous_peaks - np.mean(rand_synchronous_peaks)) / np.std(rand_synchronous_peaks) rand_synchronicity_zscore = (rand_synchronous_peaks[0] - np.mean(rand_synchronous_peaks)) / np.std(rand_synchronous_peaks) # calculate possible synchronous peaks matrices potential_synchronous_peaks_matrix = find_possible_synchronous_peaks( shortest_path_matrix, peak_series, neighbour_degree, frame_window ) return ( true_synchronicity_zscore, rand_synchronicity_zscore, true_synchronous_peaks_matrix, rand_synchronous_peaks_matrix[0, :, :], potential_synchronous_peaks_matrix )