pept.tracking.trajectory_separation API documentation

# 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_size_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_size_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)