A python package to map 2D trajectories to 1D.
Project description
track_linearization
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.
Features
- 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
Installation
From PyPI (recommended)
pip install track_linearization
From Conda
conda install -c franklab track_linearization
From Source
git clone https://github.com/LorenFrankLab/track_linearization.git
cd track_linearization
pip install -e .
Optional Dependencies
For additional features (performance & visualization):
pip install track_linearization[opt] # Includes numba, ipympl
Quick Start
📚 For a comprehensive tutorial with detailed examples, see notebooks/track_linearization_tutorial.ipynb
Basic Example: Linear Track
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.0
Adding Edge Spacing
Control 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 10
Using HMM for Noisy Data
For 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
)
Advanced Usage
Edge Mapping: Merging Track Segments
The edge_map parameter enables powerful behavioral analysis by treating different spatial paths as equivalent:
Example: T-Maze Left/Right Arms
# 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 equivalent
Key insight: Positions equidistant from the start of merged edges have identical linear positions, enabling behavioral analyses that ignore spatial distinctions.
Use Cases for Edge Mapping
- 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'
}
Custom Edge Order
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
)
Variable Edge Spacing
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
)
Visualization
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
)
API Reference
Core Functions
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
Utility Functions
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.
How It Works
- 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
Requirements
- Python 3.10+
- numpy
- scipy
- pandas
- matplotlib
- networkx >= 3.2.1
- dask
Optional:
- numba (for acceleration)
- ipympl (for interactive plots)
Developer Installation
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/
Testing
# 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 -v
Citation
If 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}
}
Contributing
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
License
This project is licensed under the MIT License - see the LICENSE file for details.
Learning Resources
Tutorial Notebooks
📚 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
Support
- 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
Related Projects
- replay_trajectory_classification - Decode spatial trajectories from neural activity
- spyglass - Analysis framework for systems neuroscience
Acknowledgments
Developed by the Loren Frank Lab at UCSF for analyzing spatial behavior in neuroscience experiments.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file track_linearization-2.4.0.tar.gz.
File metadata
- Download URL: track_linearization-2.4.0.tar.gz
- Upload date:
- Size: 6.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b576bee09b1c0c4c4a9f12db779e285705ac583e4bad4eb795ad522b8ca66dd
|
|
| MD5 |
175e44b879faf35cee37626d97fb8925
|
|
| BLAKE2b-256 |
9fea171491a089fd128e2e3ed7cd18403dd17e58db0e86a173e8c6d909cfab05
|
Provenance
The following attestation bundles were made for track_linearization-2.4.0.tar.gz:
Publisher:
release.yml on LorenFrankLab/track_linearization
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
track_linearization-2.4.0.tar.gz -
Subject digest:
6b576bee09b1c0c4c4a9f12db779e285705ac583e4bad4eb795ad522b8ca66dd - Sigstore transparency entry: 617483222
- Sigstore integration time:
-
Permalink:
LorenFrankLab/track_linearization@93985e8b16838b6019777adff3f185d44f534483 -
Branch / Tag:
refs/tags/v2.4.0 - Owner: https://github.com/LorenFrankLab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@93985e8b16838b6019777adff3f185d44f534483 -
Trigger Event:
push
-
Statement type:
File details
Details for the file track_linearization-2.4.0-py3-none-any.whl.
File metadata
- Download URL: track_linearization-2.4.0-py3-none-any.whl
- Upload date:
- Size: 56.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de3f599424e942e1e547a23153d1274370625e67a7ea61b3f432b59a27f089a3
|
|
| MD5 |
398bd8bdc3ba8105712582a2afb77e1c
|
|
| BLAKE2b-256 |
a1a5dce775e3918b0a57383cc5cd1b301ea481d42bfc8c76280515d57ea4e346
|
Provenance
The following attestation bundles were made for track_linearization-2.4.0-py3-none-any.whl:
Publisher:
release.yml on LorenFrankLab/track_linearization
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
track_linearization-2.4.0-py3-none-any.whl -
Subject digest:
de3f599424e942e1e547a23153d1274370625e67a7ea61b3f432b59a27f089a3 - Sigstore transparency entry: 617483285
- Sigstore integration time:
-
Permalink:
LorenFrankLab/track_linearization@93985e8b16838b6019777adff3f185d44f534483 -
Branch / Tag:
refs/tags/v2.4.0 - Owner: https://github.com/LorenFrankLab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@93985e8b16838b6019777adff3f185d44f534483 -
Trigger Event:
push
-
Statement type: