lfpack 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lfpack-0.1.0/.githooks/pre-commit +4 -0
- lfpack-0.1.0/.github/workflows/docs.yml +57 -0
- lfpack-0.1.0/.github/workflows/publish_to_pypi.yaml +26 -0
- lfpack-0.1.0/.github/workflows/tests.yml +51 -0
- lfpack-0.1.0/.gitignore +134 -0
- lfpack-0.1.0/CHANGELOG.md +6 -0
- lfpack-0.1.0/CLAUDE.md +77 -0
- lfpack-0.1.0/CONTRIBUTING.md +77 -0
- lfpack-0.1.0/PKG-INFO +46 -0
- lfpack-0.1.0/README.md +27 -0
- lfpack-0.1.0/docs/.gitignore +2 -0
- lfpack-0.1.0/docs/README.txt +25 -0
- lfpack-0.1.0/docs/_quarto.yml +64 -0
- lfpack-0.1.0/docs/explanation/gallery.qmd +51 -0
- lfpack-0.1.0/docs/explanation/pipeline.qmd +149 -0
- lfpack-0.1.0/docs/figures/density_lfp_dab512bd.png +0 -0
- lfpack-0.1.0/docs/figures/favicon.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_1a276285-8b0e-4cc9-9f0a-a3a002978724.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_1e104bf4-7a24-4624-a5b2-c2c8289c0de7.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_6638cfb3-3831-4fc2-9327-194b76cf22e1.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_749cb2b7-e57e-4453-a794-f6230e4d0226.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_d7ec0892-0a6c-4f4f-9d8f-72083692af5c.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_da8dfec1-d265-44e8-84ce-6ae9c109b8bd.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_dab512bd-a02d-4c1f-8dbc-9155a163efc0.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_dc7e9403-19f7-409f-9240-05ee57cb7aea.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_e8f9fba4-d151-4b00-bee7-447f0f3e752c.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_eebcaf65-7fa4-4118-869d-a084e84530e2.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-02_lfp_density_voltage_fe380793-8035-414e-b000-09bfe5ece92a.png +0 -0
- lfpack-0.1.0/docs/figures/gallery/2026-06-15_lfp_compression_aggregate.png +0 -0
- lfpack-0.1.0/docs/figures/logo.png +0 -0
- lfpack-0.1.0/docs/how-to/binned-reads.qmd +59 -0
- lfpack-0.1.0/docs/how-to/multi-recording.qmd +58 -0
- lfpack-0.1.0/docs/index.qmd +47 -0
- lfpack-0.1.0/docs/objects.json +1 -0
- lfpack-0.1.0/docs/reference/LFPCompressed.qmd +33 -0
- lfpack-0.1.0/docs/reference/LFPackReader.qmd +152 -0
- lfpack-0.1.0/docs/reference/_sidebar.yml +20 -0
- lfpack-0.1.0/docs/reference/compress.qmd +21 -0
- lfpack-0.1.0/docs/reference/compress_bin_to_h5.qmd +63 -0
- lfpack-0.1.0/docs/reference/compress_pipeline.qmd +36 -0
- lfpack-0.1.0/docs/reference/compress_to_h5.qmd +56 -0
- lfpack-0.1.0/docs/reference/decompress.qmd +20 -0
- lfpack-0.1.0/docs/reference/hdf5-layout.qmd +93 -0
- lfpack-0.1.0/docs/reference/index.qmd +30 -0
- lfpack-0.1.0/docs/reference/run_cadzow_checkpoint.qmd +49 -0
- lfpack-0.1.0/docs/references.bib +18 -0
- lfpack-0.1.0/docs/tutorials/first-compression.qmd +79 -0
- lfpack-0.1.0/pyproject.toml +55 -0
- lfpack-0.1.0/src/lfpack/__init__.py +17 -0
- lfpack-0.1.0/src/lfpack/_core.py +1228 -0
- lfpack-0.1.0/tests/__init__.py +0 -0
- lfpack-0.1.0/tests/test_lfpack.py +578 -0
- lfpack-0.1.0/uv.lock +2561 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pages: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: pages
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Install Quarto
|
|
23
|
+
uses: quarto-dev/quarto-actions/setup@v2
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v4
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
cache-dependency-glob: "uv.lock"
|
|
30
|
+
|
|
31
|
+
- name: Set up Python
|
|
32
|
+
run: uv python install 3.14
|
|
33
|
+
|
|
34
|
+
- name: Install dependencies
|
|
35
|
+
run: uv sync --group dev
|
|
36
|
+
|
|
37
|
+
- name: Build API reference
|
|
38
|
+
run: uv run quartodoc build --config docs/_quarto.yml
|
|
39
|
+
|
|
40
|
+
- name: Render site
|
|
41
|
+
run: quarto render docs/
|
|
42
|
+
|
|
43
|
+
- name: Upload pages artifact
|
|
44
|
+
uses: actions/upload-pages-artifact@v3
|
|
45
|
+
with:
|
|
46
|
+
path: docs/_site
|
|
47
|
+
|
|
48
|
+
deploy:
|
|
49
|
+
needs: build
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
environment:
|
|
52
|
+
name: github-pages
|
|
53
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
54
|
+
steps:
|
|
55
|
+
- name: Deploy to GitHub Pages
|
|
56
|
+
id: deployment
|
|
57
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
deploy:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v4
|
|
18
|
+
|
|
19
|
+
- name: Build package
|
|
20
|
+
run: uv build
|
|
21
|
+
|
|
22
|
+
- name: Publish package
|
|
23
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
24
|
+
with:
|
|
25
|
+
user: __token__
|
|
26
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v4
|
|
16
|
+
with:
|
|
17
|
+
enable-cache: true
|
|
18
|
+
cache-dependency-glob: "uv.lock"
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
run: uv python install 3.14
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: uv sync --extra dev
|
|
25
|
+
|
|
26
|
+
- name: Run tests with coverage
|
|
27
|
+
run: uv run pytest tests/ -v --cov=lfpack --cov-report=term-missing --cov-report=json
|
|
28
|
+
|
|
29
|
+
- name: Post coverage to step summary
|
|
30
|
+
if: always()
|
|
31
|
+
run: |
|
|
32
|
+
python -c "
|
|
33
|
+
import json, pathlib, os
|
|
34
|
+
data = json.loads(pathlib.Path('coverage.json').read_text())
|
|
35
|
+
t = data['totals']
|
|
36
|
+
lines = [
|
|
37
|
+
'## Coverage report',
|
|
38
|
+
f\"| Statements | Covered | Missed | % |\",
|
|
39
|
+
f\"|---|---|---|---|\",
|
|
40
|
+
f\"| {t['num_statements']} | {t['covered_lines']} | {t['missing_lines']} | **{t['percent_covered']:.1f}%** |\",
|
|
41
|
+
'',
|
|
42
|
+
'| File | Stmts | Miss | Cover |',
|
|
43
|
+
'|---|---|---|---|',
|
|
44
|
+
]
|
|
45
|
+
for path, info in data['files'].items():
|
|
46
|
+
s = info['summary']
|
|
47
|
+
lines.append(f\"| \`{path}\` | {s['num_statements']} | {s['missing_lines']} | {s['percent_covered']:.1f}% |\")
|
|
48
|
+
summary = os.environ.get('GITHUB_STEP_SUMMARY', '/dev/null')
|
|
49
|
+
with open(summary, 'a') as f:
|
|
50
|
+
f.write('\n'.join(lines) + '\n')
|
|
51
|
+
"
|
lfpack-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
TODO.md
|
|
2
|
+
|
|
3
|
+
# Byte-compiled / optimized / DLL files
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*$py.class
|
|
7
|
+
|
|
8
|
+
# C extensions
|
|
9
|
+
*.so
|
|
10
|
+
|
|
11
|
+
# Distribution / packaging
|
|
12
|
+
.Python
|
|
13
|
+
build/
|
|
14
|
+
develop-eggs/
|
|
15
|
+
dist/
|
|
16
|
+
downloads/
|
|
17
|
+
eggs/
|
|
18
|
+
.eggs/
|
|
19
|
+
lib/
|
|
20
|
+
lib64/
|
|
21
|
+
parts/
|
|
22
|
+
sdist/
|
|
23
|
+
var/
|
|
24
|
+
wheels/
|
|
25
|
+
pip-wheel-metadata/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py,cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
|
|
56
|
+
# Translations
|
|
57
|
+
*.mo
|
|
58
|
+
*.pot
|
|
59
|
+
|
|
60
|
+
# Django stuff:
|
|
61
|
+
*.log
|
|
62
|
+
local_settings.py
|
|
63
|
+
db.sqlite3
|
|
64
|
+
db.sqlite3-journal
|
|
65
|
+
|
|
66
|
+
# Flask stuff:
|
|
67
|
+
instance/
|
|
68
|
+
.webassets-cache
|
|
69
|
+
|
|
70
|
+
# Scrapy stuff:
|
|
71
|
+
.scrapy
|
|
72
|
+
|
|
73
|
+
# Sphinx documentation
|
|
74
|
+
docs/_build/
|
|
75
|
+
|
|
76
|
+
# PyBuilder
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
.python-version
|
|
88
|
+
|
|
89
|
+
# pipenv
|
|
90
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
91
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
92
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
93
|
+
# install all needed dependencies.
|
|
94
|
+
#Pipfile.lock
|
|
95
|
+
|
|
96
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
97
|
+
__pypackages__/
|
|
98
|
+
|
|
99
|
+
# Celery stuff
|
|
100
|
+
celerybeat-schedule
|
|
101
|
+
celerybeat.pid
|
|
102
|
+
|
|
103
|
+
# SageMath parsed files
|
|
104
|
+
*.sage.py
|
|
105
|
+
|
|
106
|
+
# Environments
|
|
107
|
+
.env
|
|
108
|
+
.venv
|
|
109
|
+
env/
|
|
110
|
+
venv/
|
|
111
|
+
ENV/
|
|
112
|
+
env.bak/
|
|
113
|
+
venv.bak/
|
|
114
|
+
|
|
115
|
+
# Spyder project settings
|
|
116
|
+
.spyderproject
|
|
117
|
+
.spyproject
|
|
118
|
+
|
|
119
|
+
# Rope project settings
|
|
120
|
+
.ropeproject
|
|
121
|
+
|
|
122
|
+
# mkdocs documentation
|
|
123
|
+
/site
|
|
124
|
+
|
|
125
|
+
# Quarto rendered output
|
|
126
|
+
docs/_site/
|
|
127
|
+
|
|
128
|
+
# mypy
|
|
129
|
+
.mypy_cache/
|
|
130
|
+
.dmypy.json
|
|
131
|
+
dmypy.json
|
|
132
|
+
|
|
133
|
+
# Pyre type checker
|
|
134
|
+
.pyre/
|
lfpack-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this project is
|
|
6
|
+
|
|
7
|
+
**lfpack** is a lossy LFP (local-field-potential) codec for Neuropixels recordings. It compresses raw `.cbin` LFP binaries into HDF5 files at >100× compression with RMSE < 25 µV, using an 8-stage pipeline followed by adaptive SVD + wavelet-packet thresholding.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Dev setup
|
|
13
|
+
uv sync --group dev
|
|
14
|
+
git config core.hooksPath .githooks # installs ruff pre-commit hook
|
|
15
|
+
|
|
16
|
+
# Tests
|
|
17
|
+
uv run pytest # all tests
|
|
18
|
+
uv run pytest tests/test_lfpack.py::TestLfpack::test_compress_output_shapes # single test
|
|
19
|
+
|
|
20
|
+
# Lint / format
|
|
21
|
+
uv run ruff check src/ tests/
|
|
22
|
+
uv run ruff format src/ tests/
|
|
23
|
+
|
|
24
|
+
# Docs — regenerate API reference from docstrings, then preview
|
|
25
|
+
uv run quartodoc build --config docs/_quarto.yml
|
|
26
|
+
quarto preview docs/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
Nearly all code lives in `src/lfpack/_core.py` (~1000 lines). The public API is re-exported from `src/lfpack/__init__.py`.
|
|
32
|
+
|
|
33
|
+
### Compression pipeline (`compress_bin_to_h5`)
|
|
34
|
+
|
|
35
|
+
1. Bad-channel detection (`ibldsp.voltage.detect_bad_channels_cbin`)
|
|
36
|
+
2. Dephasing — sample-shift correction (NP1 only, via `ibldsp.fourier.fshift`)
|
|
37
|
+
3. Highpass filter — 2 Hz zero-phase 3rd-order Butterworth
|
|
38
|
+
4. Bad-channel interpolation — distance-weighted neighbors
|
|
39
|
+
5. CAR — median subtraction across channels
|
|
40
|
+
6. Decimation — 2500 → 250 Hz (Q=10)
|
|
41
|
+
7. Cadzow denoising — spatial rank reduction (`ibldsp.cadzow`)
|
|
42
|
+
8. **Adaptive SVD + wavelet-packet thresholding** — the actual codec
|
|
43
|
+
|
|
44
|
+
Steps 6–7 are checkpointed to a `.npy` file so the expensive decimation can be skipped on resume. Steps 7–8 are parallelised with `joblib`.
|
|
45
|
+
|
|
46
|
+
### Codec (`compress` / `decompress`)
|
|
47
|
+
|
|
48
|
+
`compress(data, epsilon=150, alpha=28)` → `LFPCompressed` dataclass:
|
|
49
|
+
- **SVD rank selection**: keep singular values > `epsilon × sigma_noise`
|
|
50
|
+
- **Wavelet-packet thresholding**: db4, level 5; per-component threshold `alpha × sigma_noise / sv[k]`
|
|
51
|
+
- Sparse Vh stored as `(vh_indices, vh_values)` in HDF5; `U_scaled` with shuffle+gzip
|
|
52
|
+
|
|
53
|
+
Guard bands: 64-sample Cadzow halos, 128-sample SVD/WP overlap to prevent edge transients.
|
|
54
|
+
|
|
55
|
+
### HDF5 layout (multi-recording, pyramidal)
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
<file>.h5
|
|
59
|
+
└─ <recording>/
|
|
60
|
+
└─ <scale_2digit>/
|
|
61
|
+
├─ meta # nc, ns_total, fs, fs_sync, t0_sync, epsilon, alpha, geometry, …
|
|
62
|
+
└─ chunks/
|
|
63
|
+
└─ <i>/ # U_scaled, vh_indices, vh_values + attrs
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Legacy flat layout (meta at root) is still readable for backwards compatibility.
|
|
67
|
+
|
|
68
|
+
### `LFPackReader`
|
|
69
|
+
|
|
70
|
+
Drop-in replacement for `spikeglx.Reader`. Wraps an HDF5 file and decompresses chunks on demand. Supports multi-recording files via `recording=` kwarg and pyramidal scales via `scale=` kwarg.
|
|
71
|
+
|
|
72
|
+
## Ecosystem interconnections
|
|
73
|
+
|
|
74
|
+
lfpack sits in a tight three-way dependency with two sibling IBL packages:
|
|
75
|
+
|
|
76
|
+
- **ibl-neuropixel** (`/Users/olivier/PycharmProjects/ephys-atlas/ibl-neuropixel`) — provides the low-level Neuropixels binary I/O (`spikeglx.Reader`), destriping, dephasing (`ibldsp.fourier.fshift`), decimation (`ibldsp.voltage.resample_denoise_lfp_cbin`), bad-channel detection (`ibldsp.voltage.detect_bad_channels_cbin`), and Cadzow denoising (`ibldsp.cadzow`). lfpack wraps these directly; changes to ibldsp can break the pipeline.
|
|
77
|
+
- **viewephys** (`/Users/olivier/PycharmProjects/ephys-atlas/viewephys`) — Qt-based interactive viewer for raw Neuropixels traces. `LFPackReader` is a drop-in for `spikeglx.Reader`, so any file readable by viewephys can be replaced with an `LFPackReader` instance. `viewephys(traces.T, fs=sr.fs)` is the canonical way to visualise decompressed LFP data; note the transpose (`LFPackReader` returns time-first, viewephys expects channel-first).
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Contributing to lfpack
|
|
2
|
+
|
|
3
|
+
## Development setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/int-brain-lab/lfpack
|
|
7
|
+
cd lfpack
|
|
8
|
+
uv sync --group dev
|
|
9
|
+
git config core.hooksPath .githooks
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The last command installs the pre-commit hook from the tracked `.githooks/` directory.
|
|
13
|
+
|
|
14
|
+
## Code style
|
|
15
|
+
|
|
16
|
+
All code is formatted and linted with [ruff](https://docs.astral.sh/ruff/).
|
|
17
|
+
Configuration lives in `pyproject.toml` (`line-length = 120`, rules `E F W I`).
|
|
18
|
+
|
|
19
|
+
Before committing, run:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv run ruff format src/ tests/ # format
|
|
23
|
+
uv run ruff check src/ tests/ # lint
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The pre-commit hook installed above runs both checks automatically and blocks commits that fail.
|
|
27
|
+
|
|
28
|
+
## Docstrings
|
|
29
|
+
|
|
30
|
+
All public functions, classes, and methods use [NumPy/SciPy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
|
|
31
|
+
These docstrings are rendered into the API reference by [quartodoc](https://machow.github.io/quartodoc/).
|
|
32
|
+
|
|
33
|
+
## Documentation
|
|
34
|
+
|
|
35
|
+
The docs site lives in `docs/` and follows the [Diátaxis](https://diataxis.fr) framework:
|
|
36
|
+
|
|
37
|
+
| Directory | Type | Purpose |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `docs/tutorials/` | Tutorial | Guided learning paths |
|
|
40
|
+
| `docs/how-to/` | How-To | Step-by-step task guides |
|
|
41
|
+
| `docs/reference/` | Reference | API docs (quartodoc) + HDF5 spec |
|
|
42
|
+
| `docs/explanation/` | Explanation | Concepts, design choices, benchmarks |
|
|
43
|
+
|
|
44
|
+
Build the docs:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Generate API reference pages from docstrings
|
|
48
|
+
uv run quartodoc build --config docs/_quarto.yml
|
|
49
|
+
|
|
50
|
+
# Render HTML (output → docs/_site/)
|
|
51
|
+
quarto render docs/
|
|
52
|
+
|
|
53
|
+
# Live preview with hot-reload
|
|
54
|
+
quarto preview docs/
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Tests
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv run pytest # run all tests
|
|
61
|
+
uv run pytest --cov --cov-report=term-missing # with coverage report
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
All existing tests must pass and coverage must not regress before opening a pull request.
|
|
65
|
+
|
|
66
|
+
## Versioning
|
|
67
|
+
|
|
68
|
+
This project uses [Semantic Versioning](https://semver.org):
|
|
69
|
+
|
|
70
|
+
- **PATCH** (`0.1.x`) — bug fixes, no API changes.
|
|
71
|
+
- **MINOR** (`0.x.0`) — new backwards-compatible features or API additions.
|
|
72
|
+
- **MAJOR** (`x.0.0`) — breaking changes (e.g. new HDF5 layout, removed parameters).
|
|
73
|
+
|
|
74
|
+
Changes are recorded in `CHANGELOG.md` following the
|
|
75
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) convention.
|
|
76
|
+
The `[Unreleased]` section accumulates work until a release, at which point it is
|
|
77
|
+
renamed to `[x.y.z] - YYYY-MM-DD` and a matching git tag `vx.y.z` is pushed.
|
lfpack-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lfpack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LFP codec — lossy compression of local-field-potential recordings via adaptive SVD and wavelet-packet thresholding
|
|
5
|
+
Project-URL: Homepage, https://github.com/int-brain-lab/lfpack
|
|
6
|
+
Project-URL: Bug Tracker, https://github.com/int-brain-lab/lfpack/issues
|
|
7
|
+
Author: The International Brain Laboratory
|
|
8
|
+
License: MIT
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: h5py>=3.0
|
|
11
|
+
Requires-Dist: ibl-neuropixel
|
|
12
|
+
Requires-Dist: numpy
|
|
13
|
+
Requires-Dist: pywavelets
|
|
14
|
+
Requires-Dist: scipy
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# lfpack — LFP codec for Neuropixels recordings
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="docs/figures/logo.png" alt="lfpack logo" width="300"/>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
Lossy codec for local-field-potential (LFP) recordings from Neuropixels probes.
|
|
27
|
+
Achieves **>100× compression** with median RMSE < 25 µV via an 8-stage pipeline
|
|
28
|
+
(bad-channel detection → dephasing → highpass → interpolation → CAR → decimation → Cadzow → adaptive SVD + wavelet-packet thresholding).
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install lfpack
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Documentation
|
|
35
|
+
|
|
36
|
+
Full documentation is at **https://int-brain-lab.github.io/lfpack/**.
|
|
37
|
+
|
|
38
|
+
| Section | Contents |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| [Tutorial](https://int-brain-lab.github.io/lfpack/tutorials/first-compression.html) | End-to-end compression and decompression of a recording |
|
|
41
|
+
| [How-To: binned reads](https://int-brain-lab.github.io/lfpack/how-to/binned-reads.html) | Memory-efficient channel-binned access |
|
|
42
|
+
| [How-To: multi-recording files](https://int-brain-lab.github.io/lfpack/how-to/multi-recording.html) | Combining multiple recordings in one HDF5 file |
|
|
43
|
+
| [API reference](https://int-brain-lab.github.io/lfpack/reference/) | Full public API (`compress_bin_to_h5`, `LFPackReader`, …) |
|
|
44
|
+
| [HDF5 format](https://int-brain-lab.github.io/lfpack/reference/hdf5-layout.html) | On-disk layout specification |
|
|
45
|
+
| [Pipeline explanation](https://int-brain-lab.github.io/lfpack/explanation/pipeline.html) | Stage-by-stage description of the compression pipeline |
|
|
46
|
+
| [SVD+WP benchmark](https://int-brain-lab.github.io/lfpack/explanation/benchmark.html) | RMSE, SNR, and compression-ratio results across 11 insertions |
|
lfpack-0.1.0/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# lfpack — LFP codec for Neuropixels recordings
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="docs/figures/logo.png" alt="lfpack logo" width="300"/>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
Lossy codec for local-field-potential (LFP) recordings from Neuropixels probes.
|
|
8
|
+
Achieves **>100× compression** with median RMSE < 25 µV via an 8-stage pipeline
|
|
9
|
+
(bad-channel detection → dephasing → highpass → interpolation → CAR → decimation → Cadzow → adaptive SVD + wavelet-packet thresholding).
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install lfpack
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Documentation
|
|
16
|
+
|
|
17
|
+
Full documentation is at **https://int-brain-lab.github.io/lfpack/**.
|
|
18
|
+
|
|
19
|
+
| Section | Contents |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| [Tutorial](https://int-brain-lab.github.io/lfpack/tutorials/first-compression.html) | End-to-end compression and decompression of a recording |
|
|
22
|
+
| [How-To: binned reads](https://int-brain-lab.github.io/lfpack/how-to/binned-reads.html) | Memory-efficient channel-binned access |
|
|
23
|
+
| [How-To: multi-recording files](https://int-brain-lab.github.io/lfpack/how-to/multi-recording.html) | Combining multiple recordings in one HDF5 file |
|
|
24
|
+
| [API reference](https://int-brain-lab.github.io/lfpack/reference/) | Full public API (`compress_bin_to_h5`, `LFPackReader`, …) |
|
|
25
|
+
| [HDF5 format](https://int-brain-lab.github.io/lfpack/reference/hdf5-layout.html) | On-disk layout specification |
|
|
26
|
+
| [Pipeline explanation](https://int-brain-lab.github.io/lfpack/explanation/pipeline.html) | Stage-by-stage description of the compression pipeline |
|
|
27
|
+
| [SVD+WP benchmark](https://int-brain-lab.github.io/lfpack/explanation/benchmark.html) | RMSE, SNR, and compression-ratio results across 11 insertions |
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Local development
|
|
2
|
+
-----------------
|
|
3
|
+
|
|
4
|
+
1. Install dependencies (once):
|
|
5
|
+
uv sync --group dev
|
|
6
|
+
|
|
7
|
+
2. Regenerate API reference from docstrings (run after editing _core.py):
|
|
8
|
+
uv run quartodoc build --config docs/_quarto.yml
|
|
9
|
+
|
|
10
|
+
3. Live-preview the site:
|
|
11
|
+
quarto preview docs/
|
|
12
|
+
|
|
13
|
+
The preview server reloads automatically when .qmd files change.
|
|
14
|
+
Re-run step 2 manually whenever docstrings change — quartodoc output
|
|
15
|
+
is not watched by the preview server.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Production build
|
|
19
|
+
----------------
|
|
20
|
+
|
|
21
|
+
The site is built and deployed automatically by GitHub Actions on every
|
|
22
|
+
push to main (.github/workflows/docs.yml). The workflow runs the same
|
|
23
|
+
three steps above (quartodoc build → quarto render → deploy to GitHub Pages).
|
|
24
|
+
|
|
25
|
+
Do not commit the generated _site/ directory; it is built in CI.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
project:
|
|
2
|
+
type: website
|
|
3
|
+
output-dir: _site
|
|
4
|
+
|
|
5
|
+
website:
|
|
6
|
+
title: "lfpack"
|
|
7
|
+
favicon: figures/favicon.png
|
|
8
|
+
repo-url: https://github.com/int-brain-lab/lfpack
|
|
9
|
+
navbar:
|
|
10
|
+
left:
|
|
11
|
+
- text: "Tutorial"
|
|
12
|
+
file: tutorials/first-compression.qmd
|
|
13
|
+
- text: "How-To"
|
|
14
|
+
menu:
|
|
15
|
+
- text: "Binned-channel reads"
|
|
16
|
+
file: how-to/binned-reads.qmd
|
|
17
|
+
- text: "Multi-recording files"
|
|
18
|
+
file: how-to/multi-recording.qmd
|
|
19
|
+
- text: "Reference"
|
|
20
|
+
menu:
|
|
21
|
+
- text: "API"
|
|
22
|
+
file: reference/index.qmd
|
|
23
|
+
- text: "HDF5 format"
|
|
24
|
+
file: reference/hdf5-layout.qmd
|
|
25
|
+
- text: "Explanation"
|
|
26
|
+
menu:
|
|
27
|
+
- text: "Compression pipeline"
|
|
28
|
+
file: explanation/pipeline.qmd
|
|
29
|
+
- text: "Benchmark gallery"
|
|
30
|
+
file: explanation/gallery.qmd
|
|
31
|
+
right:
|
|
32
|
+
- icon: github
|
|
33
|
+
href: https://github.com/int-brain-lab/lfpack
|
|
34
|
+
|
|
35
|
+
format:
|
|
36
|
+
html:
|
|
37
|
+
theme: minty
|
|
38
|
+
toc: true
|
|
39
|
+
code-copy: true
|
|
40
|
+
code-overflow: wrap
|
|
41
|
+
lightbox: true
|
|
42
|
+
|
|
43
|
+
quartodoc:
|
|
44
|
+
package: lfpack
|
|
45
|
+
dir: reference
|
|
46
|
+
sidebar: reference/_sidebar.yml
|
|
47
|
+
sections:
|
|
48
|
+
- title: "Compression"
|
|
49
|
+
desc: "Compress LFP recordings to HDF5."
|
|
50
|
+
contents:
|
|
51
|
+
- compress_bin_to_h5
|
|
52
|
+
- compress_to_h5
|
|
53
|
+
- compress_pipeline
|
|
54
|
+
- compress
|
|
55
|
+
- decompress
|
|
56
|
+
- run_cadzow_checkpoint
|
|
57
|
+
- title: "Data types"
|
|
58
|
+
desc: "Core data structures."
|
|
59
|
+
contents:
|
|
60
|
+
- LFPCompressed
|
|
61
|
+
- title: "Reader"
|
|
62
|
+
desc: "Random-access decompression."
|
|
63
|
+
contents:
|
|
64
|
+
- LFPackReader
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Benchmark gallery"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Compression results across 11 IBL Neuropixels (NP1) recordings spanning a range of brain
|
|
6
|
+
regions and recording quality. Each recording was compressed with two parameter sets:
|
|
7
|
+
|
|
8
|
+
| Label | ε | α | Typical CR |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| **default** | 150 | 28 | ~350× |
|
|
11
|
+
| **aggressive** | 450 | 96 | ~750× |
|
|
12
|
+
|
|
13
|
+
Click any figure to open it full-size with keyboard navigation.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Aggregate metrics
|
|
18
|
+
|
|
19
|
+
RMSE distribution (bar = median, ▽ = p95) and compression ratio across all recordings.
|
|
20
|
+
|
|
21
|
+
{.lightbox}
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Voltage density
|
|
26
|
+
|
|
27
|
+
Columns within each panel: original → Cadzow → default → aggressive (top row), with
|
|
28
|
+
residuals vs the previous stage (bottom row).
|
|
29
|
+
|
|
30
|
+
{group="density" .lightbox}
|
|
31
|
+
|
|
32
|
+
{group="density" .lightbox}
|
|
33
|
+
|
|
34
|
+
{group="density" .lightbox}
|
|
35
|
+
|
|
36
|
+
{group="density" .lightbox}
|
|
37
|
+
|
|
38
|
+
{group="density" .lightbox}
|
|
39
|
+
|
|
40
|
+
{group="density" .lightbox}
|
|
41
|
+
|
|
42
|
+
{group="density" .lightbox}
|
|
43
|
+
|
|
44
|
+
{group="density" .lightbox}
|
|
45
|
+
|
|
46
|
+
{group="density" .lightbox}
|
|
47
|
+
|
|
48
|
+
{group="density" .lightbox}
|
|
49
|
+
|
|
50
|
+
{group="density" .lightbox}
|
|
51
|
+
|