Convert 2D spatial trajectories into 1D linear positions along track structures.
track_linearization is a Python package for mapping animal movement on complex track environments (mazes, figure-8s, T-mazes) into simplified 1D representations. It uses Hidden Markov Models to handle noisy position data and provides powerful tools for analyzing spatial behavior in neuroscience experiments.
- Flexible Track Representation: Define any 2D track structure using NetworkX graphs
- Pre-Built Track Geometries: 8+ track builders for common experimental setups (T-maze, circular, figure-8, etc.)
- HMM-Based Classification: Optional temporal continuity for robust segment classification
- Edge Merging: Treat different spatial paths as equivalent behavioral segments
- Automatic Layout Inference: Smart edge ordering and spacing for intuitive linearization
- Interactive Track Builder: Create tracks from images with mouse clicks (Jupyter-compatible)
- Validation & Quality Control: Confidence scoring, outlier detection, and comprehensive quality assessment
- Visualization Tools: Plot tracks in 2D and linearized 1D representations
pip install track_linearizationconda install -c franklab track_linearizationgit clone https://github.com/LorenFrankLab/track_linearization.git
cd track_linearization
pip install -e .For additional features (performance & visualization):
pip install track_linearization[opt] # Includes numba, ipympl๐ For a comprehensive tutorial with detailed examples, see notebooks/track_linearization_tutorial.ipynb
import numpy as np
from track_linearization import make_track_graph, get_linearized_position
# Define a simple L-shaped track
node_positions = [(0, 0), (10, 0), (10, 10)]
edges = [(0, 1), (1, 2)]
track_graph = make_track_graph(node_positions, edges)
# Animal positions moving along the track
position = np.array([
[2, 0], # On first segment
[5, 0], # Middle of first segment
[10, 3], # On second segment
[10, 7] # Near end of second segment
])
# Linearize the positions
result = get_linearized_position(position, track_graph)
print(result)
# linear_position track_segment_id projected_x_position projected_y_position
# 0 2.0 0 2.0 0.0
# 1 5.0 0 5.0 0.0
# 2 13.0 1 10.0 3.0
# 3 17.0 1 10.0 7.0Control gaps between segments in the linearized representation:
# Add 5-unit spacing between segments
result = get_linearized_position(
position,
track_graph,
edge_spacing=5.0
)
# Now segment 1 starts at position 15 (10 + 5 spacing)
# instead of position 10For real-world tracking data with noise, use the HMM mode:
result = get_linearized_position(
position,
track_graph,
use_HMM=True,
sensor_std_dev=10.0, # Expected position noise in your units
diagonal_bias=0.5 # Preference to stay on same segment
)The edge_map parameter enables powerful behavioral analysis by treating different spatial paths as equivalent:
# Create a T-maze
# L R
# 2 4
# | |
# 0---1--------3---5
node_positions = [(0, 0), (10, 0), (10, 10), (20, 0), (20, 10)]
edges = [
(0, 1), # Stem (edge_id 0)
(1, 2), # Left arm (edge_id 1)
(1, 3), # Center (edge_id 2)
(3, 4), # Right arm (edge_id 3)
]
track_graph = make_track_graph(node_positions, edges)
# Positions 5 units up each arm
position = np.array([
[10, 5], # 5 units up left arm
[20, 5], # 5 units up right arm
])
# Merge left and right arms into single "choice_arm" segment
edge_map = {1: 'choice_arm', 3: 'choice_arm'}
result = get_linearized_position(
position,
track_graph,
edge_map=edge_map
)
print(result)
# linear_position track_segment_id projected_x projected_y
# 0 5.0 choice_arm 10.0 5.0
# 1 5.0 choice_arm 20.0 5.0
# Both positions have linear_position = 5.0!
# This treats left/right choice arms as behaviorally equivalentKey insight: Positions equidistant from the start of merged edges have identical linear positions, enabling behavioral analyses that ignore spatial distinctions.
- T-mazes: Merge left/right choice arms
- Figure-8 tracks: Merge symmetric loops
- Multiple paths: Treat different routes to the same goal as equivalent
- Semantic labeling: Use strings instead of integers for clearer analysis
# Example: Semantic labels for analysis
edge_map = {
0: 'start',
1: 'left_arm',
2: 'right_arm',
3: 'goal'
}Control how segments are arranged in the linearization:
# Specify exact edge order
edge_order = [(1, 2), (0, 1), (2, 3)] # Custom path through nodes
result = get_linearized_position(
position,
track_graph,
edge_order=edge_order
)
# Or use automatic inference
from track_linearization import infer_edge_layout
edge_order, edge_spacing = infer_edge_layout(
track_graph,
start_node=0 # Begin linearization at node 0
)Use different spacing between each pair of segments:
# Custom spacing for each gap
edge_spacing = [5, 10, 3] # Different spacing between each pair
result = get_linearized_position(
position,
track_graph,
edge_spacing=edge_spacing
)from track_linearization import plot_track_graph, plot_graph_as_1D
# Plot the 2D track structure
plot_track_graph(track_graph, show=True)
# Plot the linearized 1D representation
plot_graph_as_1D(
track_graph,
edge_order=edge_order,
edge_spacing=5.0,
show=True
)get_linearized_position(): Main function to convert 2D positions to 1Dmake_track_graph(): Create a properly formatted track graph from node positions and edgesinfer_edge_layout(): Automatically determine optimal edge ordering and spacing
plot_track_graph(): Visualize 2D track structureplot_graph_as_1D(): Visualize linearized 1D representationproject_1d_to_2d(): Convert linear positions back to 2D coordinates
For detailed API documentation, see the docstrings in each function or visit the documentation.
- Graph Representation: Tracks are represented as NetworkX graphs where nodes have 2D positions and edges represent valid paths
- Segment Classification: For each position, determine which track segment (edge) the animal is on using either:
- Nearest-neighbor (fast, for clean data)
- HMM inference (robust, for noisy data with temporal continuity)
- Projection: Project 2D position onto the identified edge
- Linearization: Calculate 1D position based on distance along the edge from a reference point, with optional spacing between segments
- Edge Mapping: Optionally merge or relabel segments to create unified coordinate systems for behavioral analysis
- Python 3.10+
- numpy
- scipy
- pandas
- matplotlib
- networkx >= 3.2.1
- dask
Optional:
- numba (for acceleration)
- ipympl (for interactive plots)
For development with testing and linting tools:
# Clone the repository
git clone https://github.com/LorenFrankLab/track_linearization.git
cd track_linearization
# Create conda environment
conda env create -f environment.yml
conda activate track_linearization
# Install in editable mode with dev dependencies
pip install -e .[dev]
# Run tests
pytest src/track_linearization/tests/ -v
# Run linting
ruff check src/
mypy src/track_linearization/# Run all tests
pytest src/track_linearization/tests/
# Run with coverage
pytest src/track_linearization/tests/ --cov=track_linearization --cov-report=html
# Run specific test file
pytest src/track_linearization/tests/test_core.py -vIf you use this package in your research, please cite:
@software{track_linearization,
title = {track_linearization: 2D to 1D position linearization using HMMs},
author = {{Loren Frank Lab}},
url = {https://github.com/LorenFrankLab/track_linearization},
year = {2024}
}Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Interactive Tutorial - A comprehensive, pedagogically-designed notebook covering:
- Simple to complex track examples (linear, L-shaped, circular, W-track)
- Edge mapping for behavioral analysis (T-maze example)
- HMM-based classification for noisy data
- Best practices and troubleshooting tips
Perfect for scientists new to the package or computational spatial analysis!
๐ง Advanced Features Tutorial - Learn about new features in v2.4+:
- Pre-built track geometries (T-maze, plus maze, figure-8, etc.)
- Validation & quality control for linearization
- Interactive track builder from images (Jupyter-compatible)
- Outlier detection and confidence scoring
- Real-world analysis workflows
- Issues: Report bugs or request features via GitHub Issues
- Documentation: See docstrings in the code for detailed API documentation
- Tutorial: Start with track_linearization_tutorial.ipynb
- Examples: Check the
notebooks/directory for additional Jupyter notebook examples
- replay_trajectory_classification - Decode spatial trajectories from neural activity
- spyglass - Analysis framework for systems neuroscience
Developed by the Loren Frank Lab at UCSF for analyzing spatial behavior in neuroscience experiments.