Containers

Spatial data structures for efficient nearest neighbor queries.

Containers module.

This module provides spatial data structures for efficient nearest neighbor queries, spatial indexing, and tracking containers.

Spatial Index Hierarchy

Most spatial index structures inherit from BaseSpatialIndex, which defines a common interface for k-nearest neighbor and radius queries. RTree is the exception: it indexes bounding boxes rather than points and counts entries rather than samples, so it stands outside the hierarchy:

BaseSpatialIndex (abstract)
├── KDTree - K-dimensional tree (Euclidean space)
├── BallTree - Ball tree variant of KD-tree
└── MetricSpatialIndex (abstract)
    ├── VPTree - Vantage point tree (any metric)
    └── CoverTree - Cover tree (any metric)

RTree - Rectangle tree for bounding boxes (standalone)

K-D Tree

K-dimensional tree for spatial queries.

K-D Tree implementation.

A k-dimensional tree (k-d tree) is a space-partitioning data structure for organizing points in k-dimensional space. Useful for efficient nearest neighbor searches in tracking applications.

References

  • J. L. Bentley, “Multidimensional binary search trees used for associative searching,” Communications of the ACM, 1975.

  • J. H. Friedman, J. L. Bentley, R. A. Finkel, “An Algorithm for Finding Best Matches in Logarithmic Expected Time,” ACM TOMS, 1977.

class pytcl.containers.kd_tree.KDNode(point, index, split_dim, bucket=None)[source]

Bases: object

A node in the k-d tree.

Variables:
  • point (ndarray) – The point stored at this node.

  • index (int) – Original index of this point in the data array.

  • split_dim (int) – The dimension used for splitting at this node.

  • left (KDNode or None) – Left child (points with smaller split dimension value).

  • right (KDNode or None) – Right child (points with larger split dimension value).

  • bucket (ndarray or None) – For a leaf node, the indices of every point it holds (point and index are the first of them). None for an internal node.

__init__(point, index, split_dim, bucket=None)[source]
point
index
split_dim
left: KDNode | None
right: KDNode | None
bucket

Indices of every point held by this node when it is a leaf, or None for an internal node. A leaf is scanned exhaustively rather than split further; see KDTree.leaf_size.

pytcl.containers.kd_tree.NearestNeighborResult

alias of NeighborResult

class pytcl.containers.kd_tree.KDTree(data, leaf_size=10)[source]

Bases: BaseSpatialIndex

K-D Tree for efficient spatial queries.

A k-d tree recursively partitions space by splitting along different dimensions at each level. This enables O(log n) average-case nearest neighbor queries.

Parameters:
  • data (array_like) – Data points of shape (n_samples, n_features).

  • leaf_size (int, optional) – Maximum number of points in a leaf node. Default 10.

Examples

>>> import numpy as np
>>> points = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
>>> tree = KDTree(points)
>>> result = tree.query(np.array([[0.1, 0.1]]), k=2)
>>> result.indices
array([[0, 1]])

Notes

The tree is balanced by choosing the median point along the split dimension at each level, giving O(n log n) construction time.

Query complexity is O(log n) on average for nearest neighbor search, though worst case is O(n) for highly unbalanced queries.

See also

BaseSpatialIndex

Abstract base class defining the spatial index interface.

BallTree

Alternative spatial index using hyperspheres.

__init__(data, leaf_size=10)[source]
query(X, k=1)[source]

Query the tree for k nearest neighbors.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • k (int, optional) – Number of nearest neighbors. Default 1.

Returns:

result – Indices and distances of k nearest neighbors for each query.

Return type:

NeighborResult

Examples

>>> tree = KDTree(np.array([[0, 0], [1, 1], [2, 2]]))
>>> result = tree.query([[0.4, 0.4]], k=2)
>>> result.indices
array([[0, 1]])
query_radius(X, r)[source]

Query the tree for all points within radius r.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • r (float) – Query radius.

Returns:

indices – For each query, a list of indices of points within radius r.

Return type:

list of lists

Examples

>>> tree = KDTree(np.array([[0, 0], [1, 0], [0, 1], [5, 5]]))
>>> sorted(tree.query_radius([[0, 0]], r=1.5)[0])
[0, 1, 2]
class pytcl.containers.kd_tree.BallTree(data, leaf_size=10)[source]

Bases: BaseSpatialIndex

Ball Tree for efficient spatial queries.

A ball tree partitions space using hyperspheres (balls), which can be more efficient than k-d trees for high-dimensional data or non-Euclidean metrics.

Parameters:
  • data (array_like) – Data points of shape (n_samples, n_features).

  • leaf_size (int, optional) – Maximum number of points in a leaf node. Default 10.

Examples

>>> import numpy as np
>>> points = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
>>> tree = BallTree(points)
>>> result = tree.query(np.array([[0.1, 0.1]]), k=2)

Notes

Ball trees have O(n log n) construction and O(log n) average-case query time. They can outperform k-d trees in high dimensions.

See also

BaseSpatialIndex

Abstract base class defining the spatial index interface.

KDTree

Alternative spatial index using axis-aligned splits.

__init__(data, leaf_size=10)[source]
query(X, k=1)[source]

Query the tree for k nearest neighbors.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • k (int, optional) – Number of nearest neighbors. Default 1.

Returns:

result – Indices and distances of k nearest neighbors.

Return type:

NeighborResult

query_radius(X, r)[source]

Query the tree for all points within radius r.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • r (float) – Query radius.

Returns:

indices – For each query, a list of indices of points within radius r.

Return type:

list of lists

R-Tree

R-tree for spatial indexing.

R-tree implementation for spatial indexing of bounding boxes.

R-trees are tree data structures used for spatial access methods, i.e., for indexing multi-dimensional information such as geographical coordinates, rectangles, or polygons.

References

  • A. Guttman, “R-trees: A Dynamic Index Structure for Spatial Searching,” ACM SIGMOD, 1984.

  • N. Beckmann et al., “The R*-tree: An Efficient and Robust Access Method for Points and Rectangles,” ACM SIGMOD, 1990.

pytcl.containers.rtree.SpatialQueryResult

alias of NeighborResult

class pytcl.containers.rtree.BoundingBox(min_coords, max_coords)[source]

Bases: NamedTuple

Axis-aligned bounding box.

Variables:
  • min_coords (ndarray) – Minimum coordinates in each dimension.

  • max_coords (ndarray) – Maximum coordinates in each dimension.

min_coords: ndarray[tuple[Any, ...], dtype[floating]]

Alias for field number 0

max_coords: ndarray[tuple[Any, ...], dtype[floating]]

Alias for field number 1

property center: ndarray[tuple[Any, ...], dtype[floating]]

Center point of the bounding box.

property dimensions: ndarray[tuple[Any, ...], dtype[floating]]

Size in each dimension.

property volume: float

Volume (area in 2D) of the bounding box.

Zero if the box is degenerate in any dimension. A flat box such as [0,0]-[2,0] encloses no volume, and this used to report 2.0 for it by multiplying only the nonzero extents (gh-22). That behavior belongs to the insertion heuristic below, which needs to distinguish degenerate boxes from one another; as a geometric property it was simply wrong.

contains_point(point)[source]

Check if box contains a point.

intersects(other)[source]

Check if this box intersects another.

contains_box(other)[source]

Check if this box fully contains another.

pytcl.containers.rtree.merge_boxes(boxes)[source]

Compute minimum bounding box containing all boxes.

pytcl.containers.rtree.box_from_point(point)[source]

Create a zero-volume bounding box from a point.

pytcl.containers.rtree.box_from_points(points)[source]

Create minimum bounding box containing all points.

class pytcl.containers.rtree.RTreeNode(is_leaf=True)[source]

Bases: object

Node in an R-tree.

Variables:
  • bbox (BoundingBox) – Bounding box of this node.

  • is_leaf (bool) – Whether this is a leaf node.

  • children (list) – Child nodes (for internal nodes).

  • entries (list) – (bbox, data_index) pairs (for leaf nodes).

__init__(is_leaf=True)[source]
is_leaf
children: List[RTreeNode]
entries: List[Tuple[BoundingBox, int]]
bbox: BoundingBox | None
parent: RTreeNode | None
update_bbox()[source]

Update bounding box to contain all children/entries.

class pytcl.containers.rtree.RTreeResult(indices, boxes)[source]

Bases: NamedTuple

Result of R-tree query.

Variables:
  • indices (list) – Indices of matching entries.

  • boxes (list) – Bounding boxes of matching entries.

indices: List[int]

Alias for field number 0

boxes: List[BoundingBox]

Alias for field number 1

class pytcl.containers.rtree.RTree(max_entries=10, min_entries=None)[source]

Bases: object

R-tree for spatial indexing of bounding boxes.

An R-tree groups nearby objects and represents them with their minimum bounding rectangle. This allows efficient spatial queries.

Unlike KDTree and BallTree which only index points, RTree can index bounding boxes of arbitrary size. It also supports dynamic insertion.

Parameters:
  • max_entries (int, optional) – Maximum entries per node. Default 10.

  • min_entries (int, optional) – Minimum entries a split leaves in a node, root excepted. Default max_entries // 2. Must not exceed (max_entries + 1) // 2: a node splits at max_entries + 1 entries and the halves are as even as possible, so nothing larger is achievable, and an unsatisfiable value raises rather than being silently violated. This tree is insert-only, so there is no deletion underflow for min_entries to govern.

Variables:
  • n_entries (int) – Number of entries in the tree.

  • n_features (int) – Dimensionality of the data (set after first insertion).

Examples

>>> tree = RTree()
>>> tree.insert(BoundingBox(np.array([0, 0]), np.array([1, 1])), 0)
0
>>> tree.insert(BoundingBox(np.array([2, 2]), np.array([3, 3])), 1)
1
>>> query_box = BoundingBox(np.array([0.5, 0.5]), np.array([2.5, 2.5]))
>>> result = tree.query_intersect(query_box)

Notes

This implementation uses a simplified insertion algorithm. For production use, consider using R*-tree or packed R-tree variants.

See also

KDTree

Point-based spatial index using axis-aligned splits.

BallTree

Point-based spatial index using hyperspheres.

__init__(max_entries=10, min_entries=None)[source]
n_features: int | None
classmethod from_points(data, max_entries=10, min_entries=None)[source]

Create an RTree from point data.

This factory method provides an interface similar to KDTree and BallTree, allowing RTree to be used interchangeably for point queries.

Parameters:
  • data (array_like) – Data points of shape (n_samples, n_features).

  • max_entries (int, optional) – Maximum entries per node. Default 10.

  • min_entries (int, optional) – Minimum entries a split leaves in a node. Default max_entries // 2; must not exceed (max_entries + 1) // 2.

Returns:

tree – RTree with all points inserted.

Return type:

RTree

Examples

>>> points = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
>>> tree = RTree.from_points(points)
>>> result = tree.query([[0.1, 0.1]], k=2)
insert(bbox, data_index=None)[source]

Insert a bounding box into the tree.

Parameters:
  • bbox (BoundingBox) – Bounding box to insert.

  • data_index (int, optional) – Index to associate with this box. If None, uses next available.

Returns:

index – Index of the inserted entry.

Return type:

int

insert_point(point, data_index=None)[source]

Insert a point as a zero-volume bounding box.

insert_points(points)[source]

Insert multiple points.

query(X, k=1)[source]

Query the tree for k nearest neighbors.

This method provides API compatibility with KDTree and BallTree.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • k (int, optional) – Number of nearest neighbors. Default 1.

Returns:

result – Indices and distances of k nearest neighbors for each query.

Return type:

NeighborResult

Examples

>>> tree = RTree.from_points(np.array([[0, 0], [1, 1], [2, 2]]))
>>> result = tree.query([[0.5, 0.5]], k=2)
>>> result.indices
array([[0, 1]])
query_radius(X, r)[source]

Query the tree for all points within radius r.

This method provides API compatibility with KDTree and BallTree.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • r (float) – Query radius.

Returns:

indices – For each query, a list of indices of points within radius r.

Return type:

list of lists

Examples

>>> tree = RTree.from_points(np.array([[0, 0], [1, 0], [0, 1], [5, 5]]))
>>> tree.query_radius([[0, 0]], r=1.5)
[[0, 1, 2]]
query_ball_point(X, r)[source]

Query the tree for all points within radius r.

This is an alias for query_radius() provided for compatibility with scipy.spatial.KDTree.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • r (float) – Search radius.

Returns:

indices – For each query point, a list of indices of data points within distance r.

Return type:

list of list of int

See also

query_radius

The underlying implementation.

query_intersect(query_bbox)[source]

Find all entries intersecting a query box.

Parameters:

query_bbox (BoundingBox) – Query bounding box.

Returns:

result – Indices and boxes of intersecting entries.

Return type:

RTreeResult

query_contains(query_bbox)[source]

Find all entries contained within a query box.

Parameters:

query_bbox (BoundingBox) – Query bounding box.

Returns:

result – Indices and boxes of contained entries.

Return type:

RTreeResult

query_point(point)[source]

Find all entries containing a point.

Parameters:

point (array_like) – Query point.

Returns:

result – Indices and boxes of entries containing the point.

Return type:

RTreeResult

nearest(query_point, k=1)[source]

Find k nearest entries to a query point.

Parameters:
  • query_point (array_like) – Query point.

  • k (int, optional) – Number of nearest neighbors. Default 1.

Returns:

  • indices (list) – Indices of nearest entries.

  • distances (list) – Distances to nearest entries.

Return type:

Tuple[List[int], List[float]]

VP-Tree

Vantage-point tree for metric spaces.

Vantage Point Tree (VP-tree) implementation.

VP-trees are metric trees that partition data based on distance to selected vantage points. They are effective for nearest neighbor search in metric spaces, particularly with high-dimensional data.

References

  • P. N. Yianilos, “Data structures and algorithms for nearest neighbor search in general metric spaces,” SODA 1993.

pytcl.containers.vptree.VPTreeResult

alias of NeighborResult

class pytcl.containers.vptree.VPNode(index, radius=0.0)[source]

Bases: object

Node in a VP-tree.

Variables:
  • index (int) – Index of the vantage point in the original data.

  • radius (float) – Median distance to vantage point (splitting threshold).

  • left (VPNode or None) – Left subtree (points closer than radius).

  • right (VPNode or None) – Right subtree (points farther than radius).

__init__(index, radius=0.0)[source]
index
radius
left: VPNode | None
right: VPNode | None
class pytcl.containers.vptree.VPTree(data, metric=None)[source]

Bases: MetricSpatialIndex

Vantage Point Tree for metric space nearest neighbor search.

A VP-tree recursively partitions space by selecting a vantage point and dividing remaining points into those closer than a threshold (median distance) and those farther.

Parameters:
  • data (array_like) – Data points of shape (n_samples, n_features).

  • metric (callable, optional) – Distance function metric(x, y) -> float. Default is Euclidean distance.

Examples

>>> import numpy as np
>>> points = np.random.rand(100, 3)
>>> tree = VPTree(points)
>>> result = tree.query(points[:5], k=3)
>>> result.indices.shape
(5, 3)

Notes

VP-trees can use any metric distance function, making them useful for non-Euclidean spaces (e.g., edit distance for strings, geodesic distance on manifolds).

Query complexity is O(log n) on average but can degrade to O(n) for pathological distance distributions.

See also

MetricSpatialIndex

Abstract base class for metric-based spatial indices.

CoverTree

Alternative metric space index with theoretical guarantees.

__init__(data, metric=None)[source]
query(X, k=1)[source]

Query the tree for k nearest neighbors.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • k (int, optional) – Number of nearest neighbors. Default 1.

Returns:

result – Indices and distances of k nearest neighbors.

Return type:

NeighborResult

query_radius(X, r)[source]

Find all points within radius r of query points.

Parameters:
  • X (array_like) – Query points.

  • r (float) – Query radius.

Returns:

indices – For each query, list of indices within radius.

Return type:

list of lists

Cover Tree

Cover tree for approximate nearest neighbors.

Cover Tree implementation for nearest neighbor search.

Cover trees are data structures for nearest neighbor search in metric spaces. The reference algorithm carries a theoretical O(c^12 log n) query-time guarantee, where c is the expansion constant of the data – but that bound assumes the strict cover invariant (d(parent, child) <= base^level) is maintained during insertion. This implementation’s insertion is simplified and does not maintain that invariant (see the covering-radii comment in __init__, where covering radii are computed from actual descendant distances precisely because the level bound cannot be trusted). Queries still return correct results, since pruning falls back to the exact computed radii, but the O(c^12 log n) bound does not apply to this implementation.

References

  • A. Beygelzimer, S. Kakade, J. Langford, “Cover trees for nearest neighbor,” ICML 2006.

pytcl.containers.covertree.CoverTreeResult

alias of NeighborResult

class pytcl.containers.covertree.CoverTreeNode(index, level)[source]

Bases: object

Node in a Cover tree.

Variables:
  • index (int) – Index of the point in the original data.

  • level (int) – Level in the tree (determines covering radius 2^level).

  • children (dict) – Children organized by level.

  • max_desc (float) – Covering radius: maximum distance from this node’s point to any point in its subtree. Used for exact query pruning.

__init__(index, level)[source]
index
level
children: dict[int, List[CoverTreeNode]]
max_desc
add_child(level, child)[source]

Add a child at the specified level.

class pytcl.containers.covertree.CoverTree(data, metric=None, base=2.0)[source]

Bases: MetricSpatialIndex

Cover Tree for metric space nearest neighbor search.

A cover tree maintains a hierarchy of nested coverings of the data, where points at level i are a subset of points at level i-1 and cover all points within distance 2^i.

Parameters:
  • data (array_like) – Data points of shape (n_samples, n_features).

  • metric (callable, optional) – Distance function metric(x, y) -> float. Default is Euclidean distance.

  • base (float, optional) – Base for the exponential scale. Default 2.0.

Examples

>>> import numpy as np
>>> points = np.random.rand(100, 3)
>>> tree = CoverTree(points)
>>> result = tree.query(points[:5], k=3)

Notes

The classical cover tree’s guarantees (O(c^12 log n) queries in the expansion constant c) do NOT apply to this implementation – the module docstring details the simplifications: insertion does not maintain the strict cover invariant, and query pruning uses a per-node max-descendant bound instead of the level radius. Results are exact; the guarantees that are lost are about running time. For well-distributed data, queries are efficient even in high dimensions.

The implementation uses a simplified version of the original algorithm for clarity.

See also

MetricSpatialIndex

Abstract base class for metric-based spatial indices.

VPTree

Alternative metric space index using vantage points.

__init__(data, metric=None, base=2.0)[source]
root: CoverTreeNode | None
query(X, k=1)[source]

Query the tree for k nearest neighbors.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • k (int, optional) – Number of nearest neighbors. Default 1.

Returns:

result – Indices and distances of k nearest neighbors.

Return type:

NeighborResult

query_radius(X, r)[source]

Find all points within radius r of query points.

Parameters:
  • X (array_like) – Query points.

  • r (float) – Query radius.

Returns:

indices – For each query, list of indices within radius.

Return type:

list of lists

Base

Base classes for spatial data structures.

This module provides abstract base classes that define the common interface for spatial indexing data structures like KD-trees, VP-trees, R-trees, and Cover trees.

The unified interface ensures all spatial indices provide consistent: - Constructor patterns (data, optional parameters) - Query methods (query, query_radius, query_ball_point) - Return types (NeighborResult)

class pytcl.containers.base.NeighborResult(indices, distances)[source]

Bases: NamedTuple

Unified result type for spatial index queries.

All spatial index implementations (KDTree, BallTree, VPTree, CoverTree, RTree) return this type from their query methods, ensuring a consistent interface across the library.

Variables:
  • indices (ndarray of shape (n_queries, k) or (n_queries,)) – Indices of the k nearest neighbors in the original data array. For k=1, may be 1D. For k>1, shape is (n_queries, k).

  • distances (ndarray of shape (n_queries, k) or (n_queries,)) – Distances to the k nearest neighbors. Same shape as indices.

Examples

>>> from pytcl.containers import KDTree
>>> import numpy as np
>>> points = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
>>> tree = KDTree(points)
>>> result = tree.query([[0.1, 0.2]], k=2)
>>> result.indices
array([[0, 2]])
>>> result.distances
array([[0.2236068 , 0.80622577]])

See also

BaseSpatialIndex

Abstract base class for spatial indices.

indices: ndarray[tuple[Any, ...], dtype[int64]]

Alias for field number 0

distances: ndarray[tuple[Any, ...], dtype[floating]]

Alias for field number 1

class pytcl.containers.base.BaseSpatialIndex(data)[source]

Bases: ABC

Abstract base class for spatial indexing data structures.

All spatial index implementations (KDTree, VPTree, RTree, CoverTree) should inherit from this class and implement the required methods.

This provides a consistent interface for: - Building the index from point data - k-nearest neighbor queries - Range/radius queries - Dimension and size introspection

Parameters:

data (array_like) – Data points of shape (n_samples, n_features).

Variables:
  • data (ndarray) – The indexed data points.

  • n_samples (int) – Number of data points.

  • n_features (int) – Dimensionality of data points.

__init__(data)[source]
abstractmethod query(X, k=1)[source]

Query the index for k nearest neighbors.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • k (int, optional) – Number of nearest neighbors to return. Default is 1.

Returns:

result – Named tuple with indices and distances of k nearest neighbors for each query point.

Return type:

NeighborResult

abstractmethod query_radius(X, r)[source]

Query the index for all points within radius r.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • r (float) – Search radius.

Returns:

indices – For each query point, a list of indices of data points within distance r.

Return type:

list of list of int

query_ball_point(X, r)[source]

Query the index for all points within radius r.

This is an alias for query_radius() provided for compatibility with scipy.spatial.KDTree.

Parameters:
  • X (array_like) – Query points of shape (n_queries, n_features) or (n_features,).

  • r (float) – Search radius.

Returns:

indices – For each query point, a list of indices of data points within distance r.

Return type:

list of list of int

See also

query_radius

The underlying implementation.

__len__()[source]

Return number of indexed points.

class pytcl.containers.base.MetricSpatialIndex(data, metric=None)[source]

Bases: BaseSpatialIndex

Base class for metric space spatial indices.

Extends BaseSpatialIndex with support for custom distance metrics. Used by VP-trees and Cover trees which can work with any metric.

Parameters:
  • data (array_like) – Data points of shape (n_samples, n_features).

  • metric (callable, optional) – Distance function with signature metric(x, y) -> float. Default is Euclidean distance.

__init__(data, metric=None)[source]
pytcl.containers.base.validate_query_input(X, n_features)[source]

Validate and reshape query input.

Parameters:
  • X (array_like) – Query points.

  • n_features (int) – Expected number of features.

Returns:

X – Validated query array of shape (n_queries, n_features).

Return type:

ndarray

Raises:

ValueError – If query has wrong number of features.

pytcl.containers.base.validate_neighbor_count(k, n_samples)[source]

Reject a request for more neighbors than the index holds.

Every index here used to pad the shortfall with index 0 and an infinite distance. Zero is a valid index, so a caller who read the indices without also checking the distances got point 0 returned as a neighbor – silently, and as many times as the request overshot (gh-22).

Raising instead matches sklearn.neighbors, which is where most callers’ expectations come from, and turns a wrong answer into a message at the call site. A caller who genuinely wants “up to k” can ask for min(k, index.n_samples).

Parameters:
  • k (int) – Requested number of neighbors.

  • n_samples (int) – Number of points in the index.

Raises:

ValueError – If k is not a positive integer, or exceeds n_samples.

pytcl.containers.base.SpatialQueryResult

alias of NeighborResult

pytcl.containers.base.NearestNeighborResult

alias of NeighborResult

pytcl.containers.base.VPTreeResult

alias of NeighborResult

pytcl.containers.base.CoverTreeResult

alias of NeighborResult

pytcl.containers.base.RTreeResult

alias of NeighborResult

Cluster Set

Cluster set container.

This module provides a container for managing groups of tracks that move together (formations, convoys, etc.).

class pytcl.containers.cluster_set.TrackCluster(id, track_ids, centroid, covariance, time)[source]

Bases: NamedTuple

A cluster of related tracks.

Variables:
  • id (int) – Unique cluster identifier.

  • track_ids (Tuple[int, ...]) – Immutable tuple of track IDs in this cluster.

  • centroid (ndarray) – Cluster center position.

  • covariance (ndarray) – Cluster spread covariance matrix.

  • time (float) – Time at which cluster was computed.

id: int

Alias for field number 0

track_ids: Tuple[int, ...]

Alias for field number 1

centroid: ndarray[tuple[Any, ...], dtype[float64]]

Alias for field number 2

covariance: ndarray[tuple[Any, ...], dtype[float64]]

Alias for field number 3

time: float

Alias for field number 4

class pytcl.containers.cluster_set.ClusterSet(clusters=None)[source]

Bases: object

Collection of track clusters.

Provides: - Cluster creation from tracks - Cluster queries - Cluster merging/splitting

Parameters:

clusters (Iterable[TrackCluster], optional) – Initial clusters to add.

Examples

>>> import numpy as np
>>> from pytcl.trackers.multi_target import Track, TrackStatus
>>> from pytcl.containers.track_list import TrackList
>>> # Create some tracks in two groups
>>> t1 = Track(id=0, state=np.array([0, 0, 0, 0]),
...            covariance=np.eye(4), status=TrackStatus.CONFIRMED,
...            hits=5, misses=0, time=1.0)
>>> t2 = Track(id=1, state=np.array([1, 0, 1, 0]),
...            covariance=np.eye(4), status=TrackStatus.CONFIRMED,
...            hits=5, misses=0, time=1.0)
>>> t3 = Track(id=2, state=np.array([10, 0, 10, 0]),
...            covariance=np.eye(4), status=TrackStatus.CONFIRMED,
...            hits=5, misses=0, time=1.0)
>>> tracks = TrackList([t1, t2, t3])
>>> # Cluster using DBSCAN
>>> clusters = cluster_tracks_dbscan(tracks, eps=5.0, min_samples=2)
__init__(clusters=None)[source]

Initialize cluster set.

classmethod from_tracks(tracks, method='dbscan', **kwargs)[source]

Create a ClusterSet by clustering tracks.

Parameters:
  • tracks (TrackList) – Tracks to cluster.

  • method (str) – Clustering method: ‘dbscan’ or ‘kmeans’.

  • **kwargs (Any) – Additional arguments passed to the clustering function. For DBSCAN: eps, min_samples, state_indices For K-means: n_clusters, state_indices, rng

Returns:

New ClusterSet containing the computed clusters.

Return type:

ClusterSet

Examples

>>> clusters = ClusterSet.from_tracks(tracks, method='dbscan', eps=2.0)
>>> clusters = ClusterSet.from_tracks(tracks, method='kmeans', n_clusters=3)
__len__()[source]

Return number of clusters.

__iter__()[source]

Iterate over clusters.

__getitem__(idx)[source]

Get cluster by index or slice.

Parameters:

idx (int or slice) – Index or slice to retrieve.

Returns:

Single cluster if int, ClusterSet if slice.

Return type:

TrackCluster or ClusterSet

__contains__(cluster_id)[source]

Check if cluster ID exists in set.

__repr__()[source]

String representation.

get_cluster(cluster_id)[source]

Get cluster by ID.

Parameters:

cluster_id (int) – Cluster ID to find.

Returns:

The cluster if found, None otherwise.

Return type:

TrackCluster or None

get_cluster_for_track(track_id)[source]

Get the cluster containing a specific track.

Parameters:

track_id (int) – Track ID to find.

Returns:

The cluster containing the track, or None if not found.

Return type:

TrackCluster or None

clusters_in_region(center, radius)[source]

Get clusters with centroids within a spatial region.

Parameters:
  • center (array_like) – Center point [x, y].

  • radius (float) – Radius of the region.

Returns:

Clusters within the region.

Return type:

list of TrackCluster

property cluster_ids: List[int]

Get list of all cluster IDs.

property n_tracks_total: int

Get total number of tracks across all clusters.

cluster_stats(cluster_id, tracks=None, state_indices=(0, 2), velocity_indices=(1, 3))[source]

Compute statistics for a cluster.

Parameters:
  • cluster_id (int) – ID of the cluster.

  • tracks (TrackList, optional) – TrackList containing the tracks. Required for velocity coherence.

  • state_indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

  • velocity_indices (tuple of int, optional) – Indices of vx and vy in state vector (default: (1, 3)).

Returns:

Statistics for the cluster, or None if cluster not found.

Return type:

ClusterStats or None

all_stats(tracks=None, state_indices=(0, 2), velocity_indices=(1, 3))[source]

Compute statistics for all clusters.

Parameters:
  • tracks (TrackList, optional) – TrackList containing the tracks.

  • state_indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

  • velocity_indices (tuple of int, optional) – Indices of vx and vy in state vector (default: (1, 3)).

Returns:

Mapping from cluster ID to ClusterStats.

Return type:

dict

add_cluster(cluster)[source]

Add a cluster and return a new ClusterSet.

Parameters:

cluster (TrackCluster) – Cluster to add.

Returns:

New ClusterSet with the cluster added.

Return type:

ClusterSet

remove_cluster(cluster_id)[source]

Remove a cluster by ID and return a new ClusterSet.

Parameters:

cluster_id (int) – ID of cluster to remove.

Returns:

New ClusterSet without the specified cluster.

Return type:

ClusterSet

merge_clusters(id1, id2, new_id=None)[source]

Merge two clusters into one.

Parameters:
  • id1 (int) – ID of first cluster.

  • id2 (int) – ID of second cluster.

  • new_id (int, optional) – ID for the merged cluster. Defaults to id1.

Returns:

New ClusterSet with merged cluster.

Return type:

ClusterSet

Raises:

ValueError – If either cluster ID is not found.

split_cluster(cluster_id, track_ids_1, track_ids_2, new_id_1=None, new_id_2=None, tracks=None, state_indices=(0, 2))[source]

Split a cluster into two.

Parameters:
  • cluster_id (int) – ID of cluster to split.

  • track_ids_1 (Iterable[int]) – Track IDs for first new cluster.

  • track_ids_2 (Iterable[int]) – Track IDs for second new cluster.

  • new_id_1 (int, optional) – ID for first new cluster. Defaults to cluster_id.

  • new_id_2 (int, optional) – ID for second new cluster. Defaults to max(cluster_ids) + 1.

  • tracks (TrackList, optional) – TrackList for computing centroids. If None, uses existing centroid.

  • state_indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

Returns:

New ClusterSet with split clusters.

Return type:

ClusterSet

Raises:

ValueError – If cluster ID is not found.

copy()[source]

Create a copy of this ClusterSet.

Returns:

A new ClusterSet with the same clusters.

Return type:

ClusterSet

class pytcl.containers.cluster_set.ClusterStats(n_tracks, mean_separation, max_separation, velocity_coherence)[source]

Bases: NamedTuple

Statistics for a cluster.

Variables:
  • n_tracks (int) – Number of tracks in the cluster.

  • mean_separation (float) – Average distance between tracks and centroid.

  • max_separation (float) – Maximum distance from any track to centroid.

  • velocity_coherence (float) – Measure of how aligned velocities are (0-1). 1.0 means perfectly aligned, 0.0 means random directions.

n_tracks: int

Alias for field number 0

mean_separation: float

Alias for field number 1

max_separation: float

Alias for field number 2

velocity_coherence: float

Alias for field number 3

pytcl.containers.cluster_set.cluster_tracks_dbscan(tracks, eps, min_samples=2, state_indices=(0, 2))[source]

Cluster tracks using DBSCAN algorithm.

Parameters:
  • tracks (TrackList) – Tracks to cluster.

  • eps (float) – Maximum distance between two tracks to be considered neighbors.

  • min_samples (int, optional) – Minimum number of tracks to form a cluster (default: 2).

  • state_indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

Returns:

Set of track clusters.

Return type:

ClusterSet

pytcl.containers.cluster_set.cluster_tracks_kmeans(tracks, n_clusters, state_indices=(0, 2), rng=None)[source]

Cluster tracks using K-means algorithm.

Parameters:
  • tracks (TrackList) – Tracks to cluster.

  • n_clusters (int) – Number of clusters to form.

  • state_indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

  • rng (numpy.random.Generator, optional) – Random number generator for reproducibility.

Returns:

Set of track clusters.

Return type:

ClusterSet

pytcl.containers.cluster_set.compute_cluster_centroid(tracks, state_indices=(0, 2))[source]

Compute the centroid of a group of tracks.

Parameters:
  • tracks (Iterable[Track]) – Tracks to compute centroid for.

  • state_indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

Returns:

centroid – Centroid position [x, y].

Return type:

ndarray

Examples

>>> from pytcl.trackers.multi_target import Track, TrackStatus
>>> import numpy as np
>>> def make_track(tid, state):
...     return Track(id=tid, state=np.asarray(state), covariance=np.eye(4),
...                  status=TrackStatus.CONFIRMED, hits=1, misses=0, time=0.0)
>>> # Create sample tracks with [x, vx, y, vy] state vectors
>>> tracks = [make_track(0, [0.0, 1.0, 0.0, 1.0]),
...           make_track(1, [2.0, 1.0, 2.0, 1.0]),
...           make_track(2, [4.0, 1.0, 4.0, 1.0])]
>>> centroid = compute_cluster_centroid(tracks)
>>> centroid
array([2., 2.])

See also

compute_cluster_covariance

Compute covariance of track positions.

Measurement Set

Measurement set container.

This module provides a time-indexed collection of measurements with spatial query support.

class pytcl.containers.measurement_set.Measurement(value, time, covariance=None, sensor_id=0, id=-1)[source]

Bases: NamedTuple

Single measurement with metadata.

Variables:
  • value (ndarray) – Measurement vector.

  • time (float) – Time of measurement.

  • covariance (ndarray, optional) – Measurement covariance matrix.

  • sensor_id (int) – ID of the sensor that produced this measurement.

  • id (int) – Unique measurement identifier.

value: ndarray[tuple[Any, ...], dtype[float64]]

Alias for field number 0

time: float

Alias for field number 1

covariance: ndarray[tuple[Any, ...], dtype[float64]] | None

Alias for field number 2

sensor_id: int

Alias for field number 3

id: int

Alias for field number 4

class pytcl.containers.measurement_set.MeasurementSet(measurements=None)[source]

Bases: object

Collection of measurements with time and spatial indexing.

Provides: - Time-windowed queries - Spatial region queries - Sensor filtering - Batch value extraction

Parameters:

measurements (Iterable[Measurement], optional) – Initial measurements to add.

Examples

>>> import numpy as np
>>> # Create measurements
>>> m1 = Measurement(value=np.array([1.0, 2.0]), time=0.0, id=0)
>>> m2 = Measurement(value=np.array([3.0, 4.0]), time=0.0, id=1)
>>> m3 = Measurement(value=np.array([5.0, 6.0]), time=1.0, id=2)
>>> # Create measurement set
>>> mset = MeasurementSet([m1, m2, m3])
>>> len(mset)
3
>>> # Filter by time
>>> at_t0 = mset.at_time(0.0)
>>> len(at_t0)
2
>>> # Get values
>>> values = mset.values()
>>> values.shape
(3, 2)
__init__(measurements=None)[source]

Initialize measurement set.

classmethod from_arrays(values, times, covariances=None, sensor_ids=None)[source]

Create MeasurementSet from arrays.

Parameters:
  • values (array_like) – Array of shape (n_meas, meas_dim) containing measurement values.

  • times (array_like) – Array of length n_meas containing measurement times.

  • covariances (array_like, optional) – Array of shape (n_meas, meas_dim, meas_dim) containing covariances.

  • sensor_ids (array_like, optional) – Array of length n_meas containing sensor IDs.

Returns:

New MeasurementSet containing the measurements.

Return type:

MeasurementSet

__len__()[source]

Return number of measurements.

__iter__()[source]

Iterate over measurements.

__getitem__(idx)[source]

Get measurement by index or slice.

Parameters:

idx (int or slice) – Index or slice to retrieve.

Returns:

Single measurement if int, MeasurementSet if slice.

Return type:

Measurement or MeasurementSet

__repr__()[source]

String representation.

at_time(time, tolerance=1e-09)[source]

Get measurements at a specific time.

Parameters:
  • time (float) – Time to query.

  • tolerance (float, optional) – Tolerance for time matching (default: 1e-9).

Returns:

Measurements at the specified time.

Return type:

MeasurementSet

in_time_window(start, end)[source]

Get measurements in a time window.

Parameters:
  • start (float) – Start time (inclusive).

  • end (float) – End time (inclusive).

Returns:

Measurements within the time window.

Return type:

MeasurementSet

in_region(center, radius)[source]

Get measurements within a spatial region.

Parameters:
  • center (array_like) – Center point of the region.

  • radius (float) – Radius of the region.

Returns:

Measurements within the region.

Return type:

MeasurementSet

by_sensor(sensor_id)[source]

Get measurements from a specific sensor.

Parameters:

sensor_id (int) – Sensor ID to filter by.

Returns:

Measurements from the specified sensor.

Return type:

MeasurementSet

nearest_to(point, k=1)[source]

Find k nearest measurements to a point.

Parameters:
  • point (array_like) – Query point.

  • k (int, optional) – Number of nearest neighbors (default: 1).

Returns:

Query result with measurements and indices.

Return type:

MeasurementQuery

Notes

This method builds a spatial index on first call if not already built.

property times: ndarray[tuple[Any, ...], dtype[float64]]

Get unique measurement times.

property sensors: List[int]

Get unique sensor IDs.

property time_range: Tuple[float, float]

Get time range of measurements.

Returns:

(min_time, max_time) or (0.0, 0.0) if empty.

Return type:

tuple of float

values()[source]

Extract all measurement values as array.

Returns:

Array of shape (n_meas, meas_dim).

Return type:

ndarray

values_at_time(time, tolerance=1e-09)[source]

Extract measurement values at a specific time.

Parameters:
  • time (float) – Time to query.

  • tolerance (float, optional) – Tolerance for time matching (default: 1e-9).

Returns:

Array of shape (n_meas_at_time, meas_dim).

Return type:

ndarray

add(measurement)[source]

Add a measurement and return a new MeasurementSet.

Parameters:

measurement (Measurement) – Measurement to add.

Returns:

New MeasurementSet with the measurement added.

Return type:

MeasurementSet

add_batch(measurements)[source]

Add multiple measurements and return a new MeasurementSet.

Parameters:

measurements (Iterable[Measurement]) – Measurements to add.

Returns:

New MeasurementSet with the measurements added.

Return type:

MeasurementSet

merge(other)[source]

Merge with another MeasurementSet.

Parameters:

other (MeasurementSet) – MeasurementSet to merge with.

Returns:

New MeasurementSet containing measurements from both.

Return type:

MeasurementSet

copy()[source]

Create a copy of this MeasurementSet.

Returns:

A new MeasurementSet with the same measurements.

Return type:

MeasurementSet

build_spatial_index()[source]

Build spatial index for efficient nearest neighbor queries.

The index is built from measurement values. This is called automatically by nearest_to() if needed.

class pytcl.containers.measurement_set.MeasurementQuery(measurements, indices)[source]

Bases: NamedTuple

Result of a measurement set query.

Variables:
  • measurements (List[Measurement]) – List of measurements matching the query.

  • indices (List[int]) – Original indices of the matching measurements.

measurements: List[Measurement]

Alias for field number 0

indices: List[int]

Alias for field number 1

Track List

Track list container.

This module provides a collection class for managing multiple tracks with filtering, querying, and batch operations.

class pytcl.containers.track_list.TrackList(tracks=None)[source]

Bases: object

Collection of tracks with query and filter capabilities.

Provides: - Filtering by status, time, region - Iteration and indexing - Batch state extraction - Statistics computation

Parameters:

tracks (Iterable[Track], optional) – Initial tracks to add to the list.

Examples

>>> import numpy as np
>>> from pytcl.trackers.multi_target import Track, TrackStatus
>>> # Create some tracks
>>> t1 = Track(id=0, state=np.array([1, 0, 2, 0]),
...            covariance=np.eye(4), status=TrackStatus.CONFIRMED,
...            hits=5, misses=0, time=1.0)
>>> t2 = Track(id=1, state=np.array([5, 0, 6, 0]),
...            covariance=np.eye(4), status=TrackStatus.TENTATIVE,
...            hits=2, misses=1, time=1.0)
>>> # Create track list
>>> tracks = TrackList([t1, t2])
>>> len(tracks)
2
>>> # Filter by status
>>> confirmed = tracks.confirmed
>>> len(confirmed)
1
>>> # Get positions
>>> positions = tracks.positions(indices=(0, 2))
>>> positions.shape
(2, 2)
__init__(tracks=None)[source]

Initialize track list.

classmethod from_tracker(tracker)[source]

Create a TrackList from a MultiTargetTracker.

Parameters:

tracker (MultiTargetTracker) – The tracker to extract tracks from.

Returns:

A new TrackList containing the tracker’s current tracks.

Return type:

TrackList

__len__()[source]

Return number of tracks.

__iter__()[source]

Iterate over tracks.

__getitem__(idx)[source]

Get track by index or slice.

Parameters:

idx (int or slice) – Index or slice to retrieve.

Returns:

Single track if int, TrackList if slice.

Return type:

Track or TrackList

__contains__(track_id)[source]

Check if track ID exists in list.

__repr__()[source]

String representation.

get_by_id(track_id)[source]

Get track by ID.

Parameters:

track_id (int) – Track ID to find.

Returns:

The track if found, None otherwise.

Return type:

Track or None

get_by_ids(track_ids)[source]

Get multiple tracks by their IDs.

Parameters:

track_ids (Iterable[int]) – Track IDs to find.

Returns:

New TrackList containing the found tracks.

Return type:

TrackList

filter_by_status(status)[source]

Filter tracks by status.

Parameters:

status (TrackStatus) – Status to filter by.

Returns:

New TrackList containing only tracks with the given status.

Return type:

TrackList

filter_by_time(min_time=None, max_time=None)[source]

Filter tracks by time range.

Parameters:
  • min_time (float, optional) – Minimum time (inclusive).

  • max_time (float, optional) – Maximum time (inclusive).

Returns:

New TrackList containing tracks within the time range.

Return type:

TrackList

filter_by_region(center, radius, state_indices=(0, 2))[source]

Filter tracks by spatial region.

Parameters:
  • center (array_like) – Center point [x, y].

  • radius (float) – Radius of the region.

  • state_indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

Returns:

New TrackList containing tracks within the region.

Return type:

TrackList

filter_by_predicate(predicate)[source]

Filter tracks using a custom predicate.

Parameters:

predicate (callable) – Function that takes a Track and returns True to include it.

Returns:

New TrackList containing tracks that pass the predicate.

Return type:

TrackList

property confirmed: TrackList

Get confirmed tracks only.

property tentative: TrackList

Get tentative tracks only.

property track_ids: List[int]

Get list of all track IDs.

states()[source]

Extract all track states as array.

Returns:

Array of shape (n_tracks, state_dim) containing all states.

Return type:

ndarray

covariances()[source]

Extract all track covariances as array.

Returns:

Array of shape (n_tracks, state_dim, state_dim).

Return type:

ndarray

positions(indices=(0, 2))[source]

Extract track positions.

Parameters:

indices (tuple of int, optional) – Indices of x and y in state vector (default: (0, 2)).

Returns:

Array of shape (n_tracks, 2) containing positions.

Return type:

ndarray

stats()[source]

Compute statistics about the track list.

Returns:

Statistics about the tracks.

Return type:

TrackListStats

add(track)[source]

Add a track and return a new TrackList.

Parameters:

track (Track) – Track to add.

Returns:

New TrackList with the track added.

Return type:

TrackList

remove(track_id)[source]

Remove a track by ID and return a new TrackList.

Parameters:

track_id (int) – ID of track to remove.

Returns:

New TrackList without the specified track.

Return type:

TrackList

merge(other)[source]

Merge with another TrackList.

Parameters:

other (TrackList) – TrackList to merge with.

Returns:

New TrackList containing tracks from both lists.

Return type:

TrackList

Notes

Duplicate track IDs are not handled specially - both tracks will be included. Use get_by_id on the result to access specific tracks.

copy()[source]

Create a copy of this TrackList.

Returns:

A new TrackList with the same tracks.

Return type:

TrackList

class pytcl.containers.track_list.TrackQuery(tracks, indices)[source]

Bases: NamedTuple

Result of a track list query.

Variables:
  • tracks (List[Track]) – List of tracks matching the query.

  • indices (List[int]) – Original indices of the matching tracks.

tracks: List[Track]

Alias for field number 0

indices: List[int]

Alias for field number 1

class pytcl.containers.track_list.TrackListStats(n_tracks, n_confirmed, n_tentative, n_deleted, mean_hits, mean_misses)[source]

Bases: NamedTuple

Statistics about a track list.

Variables:
  • n_tracks (int) – Total number of tracks.

  • n_confirmed (int) – Number of confirmed tracks.

  • n_tentative (int) – Number of tentative tracks.

  • n_deleted (int) – Number of deleted tracks.

  • mean_hits (float) – Average number of hits per track.

  • mean_misses (float) – Average number of misses per track.

n_tracks: int

Alias for field number 0

n_confirmed: int

Alias for field number 1

n_tentative: int

Alias for field number 2

n_deleted: int

Alias for field number 3

mean_hits: float

Alias for field number 4

mean_misses: float

Alias for field number 5