A Python package for detecting sharp-wave ripple events (150-250 Hz) from local field potentials (LFPs) in neuroscience research.
-
Multiple Detection Algorithms
Kay_ripple_detector- Multi-channel consensus approach (Kay et al. 2016)Karlsson_ripple_detector- Per-channel detection with merging (Karlsson et al. 2009)Roumis_ripple_detector- Alternative detection methodmultiunit_HSE_detector- High Synchrony Event detection from multiunit activity
-
Comprehensive Event Statistics
- Temporal metrics (start time, end time, duration)
- Z-score metrics (mean, median, max, min, sustained threshold)
- Signal metrics (area under curve, total energy)
- Movement metrics (speed during event)
-
Flexible Signal Processing
- Bandpass filtering (150-250 Hz)
- Envelope extraction via Hilbert transform
- Gaussian smoothing with configurable parameters
- Movement exclusion based on speed thresholds
-
Simulation Tools
- Generate synthetic LFPs with embedded ripples
- Multiple noise types (white, pink, brown)
- Useful for testing and validation
pip install ripple_detectionconda install -c edeno ripple_detection# Clone the repository
git clone https://github.com/Eden-Kramer-Lab/ripple_detection.git
cd ripple_detection
# Install with optional dependencies
pip install -e .[dev,examples]- Python >= 3.10
- numpy >= 1.23.0
- scipy >= 1.9.0
- pandas >= 1.5.0
from ripple_detection import Kay_ripple_detector
import numpy as np
# Your data
time = np.arange(0, 10, 0.001) # 10 seconds at 1000 Hz
LFPs = np.random.randn(len(time), 4) # 4 channels of LFP data
speed = np.abs(np.random.randn(len(time))) # Animal speed
sampling_frequency = 1000 # Hz
# Detect ripples
ripple_times = Kay_ripple_detector(
time, LFPs, speed, sampling_frequency,
speed_threshold=4.0, # cm/s
minimum_duration=0.015, # seconds
zscore_threshold=2.0
)
print(ripple_times)from ripple_detection import Karlsson_ripple_detector
# Detect ripples with custom parameters
ripples = Karlsson_ripple_detector(
time, LFPs, speed, sampling_frequency,
speed_threshold=4.0,
minimum_duration=0.015,
zscore_threshold=3.0,
smoothing_sigma=0.004,
close_ripple_threshold=0.0
)
# Access detailed statistics
print(f"Detected {len(ripples)} ripple events")
print(f"Mean duration: {ripples['duration'].mean():.3f} seconds")
print(f"Mean z-score: {ripples['mean_zscore'].mean():.2f}")All detectors return a pandas DataFrame with comprehensive event statistics:
| Column | Description |
|---|---|
start_time |
Event start time |
end_time |
Event end time |
duration |
Event duration (seconds) |
max_thresh |
Maximum sustained threshold |
mean_zscore |
Mean z-score during event |
median_zscore |
Median z-score during event |
max_zscore |
Maximum z-score during event |
min_zscore |
Minimum z-score during event |
area |
Integral of z-score over time |
total_energy |
Integral of squared z-score |
speed_at_start |
Animal speed at event start |
speed_at_end |
Animal speed at event end |
max_speed |
Maximum speed during event |
min_speed |
Minimum speed during event |
median_speed |
Median speed during event |
mean_speed |
Mean speed during event |
See the examples directory for Jupyter notebooks demonstrating:
- Detection Examples - Using different detectors
- Algorithm Components - Testing individual components
Your LFP data must be 2D with shape (n_time, n_channels). Even for a single channel, the array must be 2D.
# Wrong - 1D array
lfps = np.random.randn(1000) # Shape: (1000,)
# Correct - 2D array with single channel
lfps = np.random.randn(1000, 1) # Shape: (1000, 1)
# OR reshape existing 1D array:
lfps = lfps.reshape(-1, 1)Your time, LFPs, and speed arrays must have the same length. Check dimensions:
print(f"time: {len(time)}, LFPs: {len(lfps)}, speed: {len(speed)}")Make sure all arrays cover the same time period and sampling rate.
The built-in filter_ripple_band() function uses a pre-computed filter designed for 1500 Hz sampling. For other sampling rates, generate a custom filter:
from ripple_detection import ripple_bandpass_filter
from scipy.signal import filtfilt
# Generate custom filter for your sampling rate
filter_num, filter_denom = ripple_bandpass_filter(sampling_frequency)
filtered_lfps = filtfilt(filter_num, filter_denom, raw_lfps, axis=0)If detection returns no events, try adjusting parameters:
ripples = Kay_ripple_detector(
time, filtered_lfps, speed, sampling_frequency,
zscore_threshold=1.5, # Lower from default 2.0
minimum_duration=0.010, # Lower from default 0.015
speed_threshold=10.0 # Increase if too restrictive (default 4.0)
)Diagnostic steps:
- Check if your LFPs actually contain ripples (150-250 Hz oscillations)
- Verify speed is in cm/s (not m/s)
- Plot the filtered LFP to visually inspect for ripple events
- Try different detector algorithms (Kay, Karlsson, Roumis)
| Parameter | Default | Description | When to Adjust |
|---|---|---|---|
speed_threshold |
4.0 cm/s | Maximum speed for ripple detection | Increase if too many events excluded during slow movement |
minimum_duration |
0.015 s | Minimum ripple duration (15 ms) | Decrease to 0.010 for shorter ripples; increase to 0.020 for stricter detection |
zscore_threshold |
2.0 (Kay/Roumis) 3.0 (Karlsson) |
Detection sensitivity | Decrease for more detections; increase for fewer, higher-confidence events |
smoothing_sigma |
0.004 s | Gaussian smoothing window (4 ms) | Rarely needs adjustment; increase for noisier data |
- Issues: GitHub Issues
- Discussions: For questions about usage and parameter selection
- Email: [email protected]
For detailed documentation on the detection algorithms and signal processing pipeline, see CLAUDE.md.
| Algorithm | Approach | Best For |
|---|---|---|
| Kay | Multi-channel consensus (sum of squared envelopes) | High-density electrode arrays |
| Karlsson | Per-channel detection with merging | Independent channel analysis |
| Roumis | Averaged square-root of squared envelopes | Balanced multi-channel approach |
# Create conda environment
conda env create -f environment.yml
conda activate ripple_detection
# Install in editable mode with dev dependencies
pip install -e .[dev,examples]The package has comprehensive test coverage (93%) across 163 tests organized in 6 modules:
# Run all tests with coverage
pytest --cov=ripple_detection --cov-report=term-missing tests/
# Run specific test modules
pytest tests/test_core.py # Core signal processing tests (70 tests)
pytest tests/test_detectors.py # Detector integration tests (25 tests)
pytest tests/test_simulate.py # Simulation module tests (36 tests)
pytest tests/test_properties.py # Property-based tests (23 tests)
pytest tests/test_snapshots.py # Snapshot/regression tests (9 tests)
# Run specific test
pytest tests/test_core.py::TestSegmentBooleanSeries::test_single_segment
# Generate HTML coverage report
pytest --cov=ripple_detection --cov-report=html tests/
# Open htmlcov/index.html in browserTest Coverage:
core.py: 93%detectors.py: 92%simulate.py: 100%- Overall: 93%
# Format code with black
black ripple_detection/ tests/
# Lint code with ruff (modern, fast linter)
ruff check ripple_detection/ tests/
# Type check with mypy
mypy ripple_detection/
# Check formatting without modifying
black --check ripple_detection/ tests/Releases are automated via GitHub Actions when a version tag is pushed:
# 1. Ensure all tests pass and code quality checks pass
pytest --cov=ripple_detection tests/
black --check ripple_detection/ tests/
ruff check ripple_detection/ tests/
mypy ripple_detection/
# 2. Update CHANGELOG.md with new version and changes
# - Add ## [X.Y.Z] - YYYY-MM-DD section
# - Document changes under Added/Changed/Deprecated/Removed/Fixed/Security
# - Update comparison links at bottom
# 3. Commit and push changelog
git add CHANGELOG.md
git commit -m "Update CHANGELOG for vX.Y.Z release"
git push origin master
# 4. Create and push annotated tag (triggers release workflow)
git tag -a vX.Y.Z -m "Release vX.Y.Z with feature descriptions"
git push origin vX.Y.ZThe automated workflow will:
- Run tests on Python 3.10, 3.11, 3.12, 3.13
- Build source distribution and wheels
- Publish to PyPI
- Create GitHub release
Note: Version numbers follow Semantic Versioning (MAJOR.MINOR.PATCH). The package version is automatically determined from git tags via hatch-vcs.
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.
If you use this package in your research, please cite the original papers:
@article{karlsson2009awake,
title={Awake replay of remote experiences in the hippocampus},
author={Karlsson, Mattias P and Frank, Loren M},
journal={Nature neuroscience},
volume={12},
number={7},
pages={913--918},
year={2009},
publisher={Nature Publishing Group}
}@article{kay2016hippocampal,
title={A hippocampal network for spatial coding during immobility and sleep},
author={Kay, Kenneth and Sosa, Marielena and Chung, Jason E and Karlsson, Mattias P and Larkin, Margaret C and Frank, Loren M},
journal={Nature},
volume={531},
number={7593},
pages={185--190},
year={2016},
publisher={Nature Publishing Group}
}This project is licensed under the MIT License - see the LICENSE file for details.
- Eric Denovellis - [email protected]
- Frank Lab for the pre-computed ripple filter
- Original algorithm implementations by Karlsson et al. and Kay et al.
- Issues: GitHub Issues
- Discussions: For questions and discussions about usage
- Email: [email protected]