Module pept.tracking.trajectory_separation

Source code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# File   : __init__.py
# License: License: GNU v3.0
# Author : Andrei Leonard Nicusan <a.l.nicusan@bham.ac.uk>
# Date   : 22.08.2019


from    .trajectory_separation      import  TrajectorySeparation

__all__ = [
    "TrajectorySeparation"
]


__author__ =        "Andrei Leonard Nicusan"
__credits__ =       ["Andrei Leonard Nicusan", "Kit Windows-Yule", "Sam Manger"]
__license__ =       "GNU v3.0"
__version__ =       "0.1"
__maintainer__ =    "Andrei Leonard Nicusan"
__email__ =         "a.l.nicusan@bham.ac.uk"
__status__ =        "Development"

Sub-modules

pept.tracking.trajectory_separation.trajectory_separation

The peptml module implements a hierarchical density-based clustering algorithm for general Positron Emission Particle Tracking (PEPT) …

Classes

class TrajectorySeparation (centres, points_to_check=25, max_distance=20, max_cluster_size_diff=500, points_cluster_size=50)
Source code
class TrajectorySeparation:

    def __init__(self,
                 centres,
                 points_to_check = 25,
                 max_distance = 20,
                 max_cluster_size_diff = 500,
                 points_cluster_size = 50):

        # centres row: [time, x, y, z, clusterSize]
        # Make sure the trajectory is memory-contiguous for efficient
        # KDTree partitioning
        self.centres = np.ascontiguousarray(centres)
        self.points_to_check = points_to_check
        self.max_distance = max_distance
        self.max_cluster_size_diff = max_cluster_size_diff
        self.points_cluster_size = points_cluster_size

        # For every point in centres, save a set of the trajectory
        # indices of the trajectories that they are part of
        #   eg. centres[2] is part of trajectories 0 and 1 =>
        #   trajectory_indices[2] = {0, 1}
        # Initialise a vector of empty sets of size len(centres)
        self.trajectory_indices = np.array([ set() for i in range(len(self.centres)) ])

        # For every trajectory found, save a list of the indices of
        # the centres that are part of that trajectory
        #   eg. trajectory 1 is comprised of centres 3, 5 and 8 =>
        #   centresIndices[1] = [3, 5, 8]
        self.centres_indices = [[]]

        # Maximum trajectory index
        self.max_index = 0


    def find_trajectories(self):

        for i, current_point in enumerate(self.centres):

            if i == 0:
                # Add the first point to trajectory 0
                self.trajectory_indices[0].add(self.max_index)
                self.centres_indices[self.max_index].append(0)
                self.max_index += 1
                continue

            # Search for the closest previous pointsToCheck points
            # within a given maxDistance
            start_index = i - self.points_to_check
            end_index = i

            if start_index < 0:
                start_index = 0

            # Construct a KDTree from the x, y, z (1:4) of the
            # selected points. Get the indices for all the points within
            # maxDistance of the currentPoint
            tree = cKDTree(self.centres[start_index:end_index, 1:4])
            closest_indices = tree.query_ball_point(current_point[1:4],
                                                    self.max_distance,
                                                    n_jobs = -1)
            closest_indices = np.array(closest_indices) + start_index

            # If no point was found, it is a new trajectory. Continue
            if len(closest_indices) == 0:
                self.trajectory_indices[i].add(self.max_index)
                self.centres_indices.append([i])
                self.max_index += 1
                continue

            # For every close point found, search for all the trajectory indices
            #   - If all trajectory indices sets are equal and of a single value
            #   then currentPoint is part of the same trajectory
            #   - If all trajectory indices sets are equal, but of more values,
            #   then currentPoint diverged from an intersection of trajectories
            #   and is part of a single trajectory => separate it
            #
            #   - If every pair of trajectory indices sets is not disjoint, then
            #   currentPoint is only one of them
            #   - If there exists a pair of trajectory indices sets that is
            #   disjoint, then currentPoint is part of all of them

            # Select the trajectories of all the points that were found
            # to be the closest
            closest_trajectories = self.trajectory_indices[closest_indices]
            #print("closestTrajectories:")
            #print(closestTrajectories)

            # If all the closest points are part of the same trajectory
            # (just one!), then the currentPoint is part of it too
            if (np.all(closest_trajectories == closest_trajectories[0]) and
                len(closest_trajectories[0]) == 1):

                self.trajectory_indices[i] = closest_trajectories[0]
                self.centres_indices[ next(iter(closest_trajectories[0])) ].append(i)
                continue

            # Otherwise, check the points based on their cluster size
            else:
                # Create a list of all the trajectories that were found to
                # intersect
                #print('\nIntersection:')
                closest_traj_indices = list( set().union(*closest_trajectories) )

                #print("ClosestTrajIndices:")
                #print(closestTrajIndices)

                # For each close trajectory, calculate the mean cluster size
                # of the last points_cluster_size points

                # Keep track of the mean cluster size that is the closest to
                # the currentPoint's clusterSize
                current_cluster_size = current_point[4]
                #print("currentClusterSize = {}".format(currentClusterSize))
                closest_traj_index = -1
                cluster_size_diff = self.max_cluster_diff

                for traj_index in closest_traj_indices:
                    #print("trajIndex = {}".format(trajIndex))

                    traj_centres = self.centres[ self.centres_indices[traj_index] ]
                    #print("trajCentres:")
                    #print(trajCentres)
                    mean_cluster_size = traj_centres[-self.points_cluster_size:][:, 4].mean()
                    #print("meanClusterSize = {}".format(meanClusterSize))
                    #print("clusterSizeDiff = {}".format(clusterSizeDiff))
                    #print("abs diff = {}".format(np.abs( currentClusterSize - meanClusterSize )))
                    if np.abs( current_cluster_size - mean_cluster_size ) < cluster_size_diff:
                        closest_traj_index = traj_index
                        cluster_size_diff = np.abs( current_cluster_size - mean_cluster_size )

                if closest_traj_index == -1:
                    #self.trajectoryIndices[i] = set(closestTrajIndices)
                    #for trajIndex in closestTrajIndices:
                    #    self.centresIndices[trajIndex].append(i)

                    print("\n**** -1 ****\n")
                    break
                else:
                    #print("ClosestTrajIndex found = {}".format(closestTrajIndex))
                    self.trajectory_indices[i] = set([closest_traj_index])
                    self.centres_indices[closest_traj_index].append(i)

        individual_trajectories = []
        for traj_centres in self.centres_indices:
            individual_traj = pept.PointData(self.centres[traj_centres],
                                             sample_size = 0,
                                             overlap = 0,
                                             verbose = False)
            individual_trajectories.append(individual_traj)

        return individual_trajectories



        '''
            # If the current point is not part of any trajectory, assign it
            # the maxIndex and increment it
            if len(self.trajectoryIndices[i]) == 0:
                self.trajectoryIndices[i].append(self.maxIndex)
                self.maxIndex += 1

            print(self.trajectoryIndices[i])
            print(self.maxIndex)

            # Construct a KDTree from the numberOfPoints in front of
            # the current point
            tree = cKDTree(self.trajectory[(i + 1):(i + self.numberOfPoints + 2)][1:4])

            # For every trajectory that the current point is part of,
            # find the closest points in front of it
            numberOfIntersections = len(self.trajectoryIndices[i])
            dist, nextPointsIndices = tree.query(currentPoint, k=numberOfIntersections, distance_upper_bound=self.maxDistance, n_jobs=-1)

            print(nextPointsIndices)

            # If the current point is part of more trajectories,
            # an intersection happened. Call subroutine to part
            # the trajectories
            if numberOfIntersections > 1:
                for j in range(0, len(self.trajectoryIndices[i])):
                    trajIndex = self.trajectoryIndices[i][j]
                    self.trajectoryIndices[i + 1 + nextPointsIndices[j]].append(trajIndex)

            else:
                self.trajectoryIndices[i + 1 + nextPointsIndices].append(self.trajectoryIndices[i][0])

            print(self.trajectoryIndices)
        '''


    def getTrajectories(self):

        self.individualTrajectories = []
        for trajCentres in self.centresIndices:
            self.individualTrajectories.append(self.centres[trajCentres])

        self.individualTrajectories = np.array(self.individualTrajectories)
        return self.individualTrajectories

        '''
        self.individualTrajectories = [ [] for i in range(0, self.maxIndex + 1) ]
        for i in range(0, len(self.trajectoryIndices)):
            for trajIndex in self.trajectoryIndices[i]:
                self.individualTrajectories[trajIndex].append(self.centres[i])

        self.individualTrajectories = np.array(self.individualTrajectories)
        for i in range(len(self.individualTrajectories)):
            if len(self.individualTrajectories[i]) > 0:
                self.individualTrajectories[i] = np.vstack(self.individualTrajectories[i])
        return self.individualTrajectories
        '''


    def plotTrajectoriesAltAxes(self, ax):
        trajectories = self.getTrajectories()
        for traj in trajectories:
            if len(traj) > 0:
                ax.scatter(traj[:, 3], traj[:, 1], traj[:, 2], marker='D', s=10)

Methods

def find_trajectories(self)
Source code
def find_trajectories(self):

    for i, current_point in enumerate(self.centres):

        if i == 0:
            # Add the first point to trajectory 0
            self.trajectory_indices[0].add(self.max_index)
            self.centres_indices[self.max_index].append(0)
            self.max_index += 1
            continue

        # Search for the closest previous pointsToCheck points
        # within a given maxDistance
        start_index = i - self.points_to_check
        end_index = i

        if start_index < 0:
            start_index = 0

        # Construct a KDTree from the x, y, z (1:4) of the
        # selected points. Get the indices for all the points within
        # maxDistance of the currentPoint
        tree = cKDTree(self.centres[start_index:end_index, 1:4])
        closest_indices = tree.query_ball_point(current_point[1:4],
                                                self.max_distance,
                                                n_jobs = -1)
        closest_indices = np.array(closest_indices) + start_index

        # If no point was found, it is a new trajectory. Continue
        if len(closest_indices) == 0:
            self.trajectory_indices[i].add(self.max_index)
            self.centres_indices.append([i])
            self.max_index += 1
            continue

        # For every close point found, search for all the trajectory indices
        #   - If all trajectory indices sets are equal and of a single value
        #   then currentPoint is part of the same trajectory
        #   - If all trajectory indices sets are equal, but of more values,
        #   then currentPoint diverged from an intersection of trajectories
        #   and is part of a single trajectory => separate it
        #
        #   - If every pair of trajectory indices sets is not disjoint, then
        #   currentPoint is only one of them
        #   - If there exists a pair of trajectory indices sets that is
        #   disjoint, then currentPoint is part of all of them

        # Select the trajectories of all the points that were found
        # to be the closest
        closest_trajectories = self.trajectory_indices[closest_indices]
        #print("closestTrajectories:")
        #print(closestTrajectories)

        # If all the closest points are part of the same trajectory
        # (just one!), then the currentPoint is part of it too
        if (np.all(closest_trajectories == closest_trajectories[0]) and
            len(closest_trajectories[0]) == 1):

            self.trajectory_indices[i] = closest_trajectories[0]
            self.centres_indices[ next(iter(closest_trajectories[0])) ].append(i)
            continue

        # Otherwise, check the points based on their cluster size
        else:
            # Create a list of all the trajectories that were found to
            # intersect
            #print('\nIntersection:')
            closest_traj_indices = list( set().union(*closest_trajectories) )

            #print("ClosestTrajIndices:")
            #print(closestTrajIndices)

            # For each close trajectory, calculate the mean cluster size
            # of the last points_cluster_size points

            # Keep track of the mean cluster size that is the closest to
            # the currentPoint's clusterSize
            current_cluster_size = current_point[4]
            #print("currentClusterSize = {}".format(currentClusterSize))
            closest_traj_index = -1
            cluster_size_diff = self.max_cluster_diff

            for traj_index in closest_traj_indices:
                #print("trajIndex = {}".format(trajIndex))

                traj_centres = self.centres[ self.centres_indices[traj_index] ]
                #print("trajCentres:")
                #print(trajCentres)
                mean_cluster_size = traj_centres[-self.points_cluster_size:][:, 4].mean()
                #print("meanClusterSize = {}".format(meanClusterSize))
                #print("clusterSizeDiff = {}".format(clusterSizeDiff))
                #print("abs diff = {}".format(np.abs( currentClusterSize - meanClusterSize )))
                if np.abs( current_cluster_size - mean_cluster_size ) < cluster_size_diff:
                    closest_traj_index = traj_index
                    cluster_size_diff = np.abs( current_cluster_size - mean_cluster_size )

            if closest_traj_index == -1:
                #self.trajectoryIndices[i] = set(closestTrajIndices)
                #for trajIndex in closestTrajIndices:
                #    self.centresIndices[trajIndex].append(i)

                print("\n**** -1 ****\n")
                break
            else:
                #print("ClosestTrajIndex found = {}".format(closestTrajIndex))
                self.trajectory_indices[i] = set([closest_traj_index])
                self.centres_indices[closest_traj_index].append(i)

    individual_trajectories = []
    for traj_centres in self.centres_indices:
        individual_traj = pept.PointData(self.centres[traj_centres],
                                         sample_size = 0,
                                         overlap = 0,
                                         verbose = False)
        individual_trajectories.append(individual_traj)

    return individual_trajectories



    '''
        # If the current point is not part of any trajectory, assign it
        # the maxIndex and increment it
        if len(self.trajectoryIndices[i]) == 0:
            self.trajectoryIndices[i].append(self.maxIndex)
            self.maxIndex += 1

        print(self.trajectoryIndices[i])
        print(self.maxIndex)

        # Construct a KDTree from the numberOfPoints in front of
        # the current point
        tree = cKDTree(self.trajectory[(i + 1):(i + self.numberOfPoints + 2)][1:4])

        # For every trajectory that the current point is part of,
        # find the closest points in front of it
        numberOfIntersections = len(self.trajectoryIndices[i])
        dist, nextPointsIndices = tree.query(currentPoint, k=numberOfIntersections, distance_upper_bound=self.maxDistance, n_jobs=-1)

        print(nextPointsIndices)

        # If the current point is part of more trajectories,
        # an intersection happened. Call subroutine to part
        # the trajectories
        if numberOfIntersections > 1:
            for j in range(0, len(self.trajectoryIndices[i])):
                trajIndex = self.trajectoryIndices[i][j]
                self.trajectoryIndices[i + 1 + nextPointsIndices[j]].append(trajIndex)

        else:
            self.trajectoryIndices[i + 1 + nextPointsIndices].append(self.trajectoryIndices[i][0])

        print(self.trajectoryIndices)
    '''
def getTrajectories(self)
Source code
def getTrajectories(self):

    self.individualTrajectories = []
    for trajCentres in self.centresIndices:
        self.individualTrajectories.append(self.centres[trajCentres])

    self.individualTrajectories = np.array(self.individualTrajectories)
    return self.individualTrajectories

    '''
    self.individualTrajectories = [ [] for i in range(0, self.maxIndex + 1) ]
    for i in range(0, len(self.trajectoryIndices)):
        for trajIndex in self.trajectoryIndices[i]:
            self.individualTrajectories[trajIndex].append(self.centres[i])

    self.individualTrajectories = np.array(self.individualTrajectories)
    for i in range(len(self.individualTrajectories)):
        if len(self.individualTrajectories[i]) > 0:
            self.individualTrajectories[i] = np.vstack(self.individualTrajectories[i])
    return self.individualTrajectories
    '''
def plotTrajectoriesAltAxes(self, ax)
Source code
def plotTrajectoriesAltAxes(self, ax):
    trajectories = self.getTrajectories()
    for traj in trajectories:
        if len(traj) > 0:
            ax.scatter(traj[:, 3], traj[:, 1], traj[:, 2], marker='D', s=10)