spikestats 0.2.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.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Bug report
3
+ about: A wrong or surprising statistic
4
+ title: ""
5
+ labels: bug
6
+ ---
7
+
8
+ ## Call
9
+
10
+ The metric and parameters, including the spike times, for example
11
+ `cv_isi([0.0, 0.1, 0.3, 0.6])` or `fano_factor(spikes, duration=2, bin_width=0.1)`.
12
+
13
+ ## Expected
14
+
15
+ The value you expected, and the definition or source it follows.
16
+
17
+ ## Actual
18
+
19
+ What you observed.
20
+
21
+ ## Environment
22
+
23
+ - spikestats version:
24
+ - Python version:
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a metric or capability for spikestats
4
+ title: ""
5
+ labels: enhancement
6
+ ---
7
+
8
+ ## What
9
+
10
+ The metric or capability you would like.
11
+
12
+ ## Why
13
+
14
+ The workflow this would support.
15
+
16
+ ## References
17
+
18
+ The definition or paper that pins down the expected behavior.
@@ -0,0 +1,13 @@
1
+ ## Summary
2
+
3
+ What this changes and why.
4
+
5
+ ## Checklist
6
+
7
+ - [ ] Tests added or updated (a bug fix starts with a failing test)
8
+ - [ ] Exact-value test and any known limit (regular train, Poisson) covered for a new metric
9
+ - [ ] `uv run ruff check .` passes
10
+ - [ ] `uv run mypy src` passes
11
+ - [ ] `uv run pytest` passes
12
+ - [ ] No runtime dependencies added
13
+ - [ ] No em dash characters in docs or commit messages
@@ -0,0 +1,22 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python: ["3.10", "3.11", "3.12", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python }}
19
+ - run: pip install -e ".[dev]"
20
+ - run: ruff check .
21
+ - run: mypy src
22
+ - run: pytest -q
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ venv/
5
+ uv.lock
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .hypothesis/
13
+ .coverage
14
+ htmlcov/
15
+ .DS_Store
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.2.0]
8
+
9
+ ### Added
10
+
11
+ - `binned_spike_counts(spikes, *, duration, bin_width) -> list[int]`: spike counts in
12
+ `int(duration / bin_width)` consecutive non-overlapping bins tiling `[0, duration)`.
13
+ - `time_resolved_rate(spikes, *, duration, bin_width, step) -> tuple[list[float], list[float]]`:
14
+ sliding-window firing rate. Returns `(window_center_times, rates_in_hz)`. Window positions use
15
+ integer indexing of `step` to avoid floating-point drift. The last window included is the
16
+ largest k where `k * step + bin_width <= duration`.
17
+ - `psth(trials, *, duration, bin_width) -> tuple[list[float], list[float]]`: peristimulus time
18
+ histogram over multiple trials. Returns `(bin_center_times, mean_rate_per_bin_hz)` averaged
19
+ over all trials. Raises `ValueError` on empty `trials`.
20
+ - `time_resolved_fano(trials, *, duration, bin_width) -> tuple[list[float], list[float]]`:
21
+ per-bin Fano factor (population variance / mean of per-trial counts) across trials. Returns
22
+ `(bin_center_times, fano_per_bin)`. Bins where the mean count across all trials is 0 are
23
+ returned as `float('nan')`.
24
+
25
+ ### Design notes
26
+
27
+ - Boundary policy: all time-resolved functions use `[0, duration)` -- a spike at exactly
28
+ `duration` is excluded, matching the existing `spike_counts` convention.
29
+ - Variance choice: `time_resolved_fano` uses population variance (denominator N), matching
30
+ `fano_factor` in metrics.py. With one trial, pvariance of a single sample is 0, so all
31
+ non-empty bins return 0. Callers who need sample variance should use multiple trials.
32
+ - Zero-mean bins in `time_resolved_fano`: returned as `float('nan')` so callers can identify
33
+ and exclude them.
34
+
35
+ ### Not included in this release
36
+
37
+ - PyPI publish is queued behind the new-project-creation quota (currently returning 429 for new
38
+ packages). The sdist and wheel are twine-verified in `dist/`; upload will proceed once the
39
+ quota resets.
40
+
41
+ ## [0.1.0]
42
+
43
+ ### Added
44
+ - `firing_rate(spikes, *, duration)`: mean firing rate.
45
+ - `inter_spike_intervals(spikes)`: consecutive interval list.
46
+ - `cv_isi(spikes)`: coefficient of variation of the inter-spike intervals.
47
+ - `cv2(spikes)`: local CV2 averaged over adjacent interval pairs (Holt et al. 1996).
48
+ - `lv(spikes)`: local variation (Shinomoto et al. 2003).
49
+ - `lvr(spikes, *, refractory)`: revised local variation LvR (Shinomoto et al. 2009).
50
+ - `spike_counts(spikes, *, duration, bin_width)`: counts per equal-width bin.
51
+ - `fano_factor(spikes, *, duration, bin_width)`: Fano factor of binned counts.
52
+ - Pure Python, zero runtime dependencies. Inputs are plain lists of spike times, matching
53
+ spikegen and spikedist.
54
+ - Test suite: exact-value tests, known-limit tests (regular train 0, Poisson 1), and
55
+ property-based invariants.
@@ -0,0 +1,49 @@
1
+ # spikestats
2
+
3
+ Pure-Python spike-train statistics. Zero runtime dependencies. Completes the toolkit with
4
+ spikegen (generate) and spikedist (compare).
5
+
6
+ ## Commands
7
+
8
+ - Create env and install: `uv venv && uv pip install -e ".[dev]"`
9
+ - Test: `uv run pytest -q`
10
+ - Lint: `uv run ruff check .` (format with `uv run ruff format .`)
11
+ - Types: `uv run mypy src`
12
+ - Build: `uv build` (then `uv run --with twine twine check dist/*` before publishing)
13
+ - Example: `python examples/summary.py`
14
+
15
+ ## Architecture
16
+
17
+ `src/spikestats/`:
18
+ - `_validate.py` shared validation (positivity, non-negativity, finite times, minimum spikes)
19
+ - `metrics.py` the statistics (firing_rate, inter_spike_intervals, cv_isi, cv2, lv, lvr,
20
+ spike_counts, fano_factor)
21
+ - `__init__.py` public surface
22
+
23
+ See `docs/architecture.md` for the formulas and references.
24
+
25
+ ## Conventions
26
+
27
+ - A spike train is a plain `list[float]` of times; functions sort the input internally.
28
+ - Metrics return plain `float`; intervals and counts return lists.
29
+ - Parameters after `*` are keyword-only; no default values.
30
+ - No runtime dependencies (standard library `statistics` and `math` only). Note the module
31
+ is `metrics.py`, not `statistics.py`, to avoid shadowing the standard library module.
32
+
33
+ ## Testing rules
34
+
35
+ - Exact value for every metric against a hand-computed example.
36
+ - Known limits: a regular train gives 0, a Poisson process gives 1 (CV, Lv, Fano).
37
+ - The identity LvR(refractory=0) == Lv is asserted.
38
+ - Hypothesis property tests for bounds and invariants; bug fixes start with a failing test.
39
+
40
+ ## Release
41
+
42
+ - Semantic versioning; update `CHANGELOG.md` and `__version__`.
43
+ - Gates: `uv run pytest && uv run ruff check . && uv run mypy src && uv build && uv run twine check dist/*`.
44
+ - Publish to PyPI, tag `vX.Y.Z`, GitHub release.
45
+
46
+ ## Style
47
+
48
+ - No em dash characters in docs, comments, or commit messages.
49
+ - Comments explain non-obvious reasoning only.
@@ -0,0 +1,37 @@
1
+ # Code of Conduct
2
+
3
+ ## Our pledge
4
+
5
+ We as members, contributors, and maintainers pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our standards
13
+
14
+ Examples of behavior that contributes to a positive environment:
15
+
16
+ - Showing empathy and kindness toward other people.
17
+ - Being respectful of differing opinions, viewpoints, and experiences.
18
+ - Giving and gracefully accepting constructive feedback.
19
+ - Focusing on what is best for the community.
20
+
21
+ Examples of unacceptable behavior:
22
+
23
+ - Harassment, insulting or derogatory comments, and personal or political attacks.
24
+ - Public or private harassment.
25
+ - Publishing others' private information without explicit permission.
26
+ - Other conduct which could reasonably be considered inappropriate.
27
+
28
+ ## Enforcement
29
+
30
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
31
+ reported to the maintainer at amaar2cool@gmail.com. All complaints will be
32
+ reviewed and investigated promptly and fairly.
33
+
34
+ ## Attribution
35
+
36
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
37
+ version 2.1.
@@ -0,0 +1,33 @@
1
+ # Contributing to spikestats
2
+
3
+ Thanks for your interest. This project values correctness, clear references, and zero
4
+ runtime dependencies.
5
+
6
+ ## Development
7
+
8
+ ```sh
9
+ uv venv
10
+ uv pip install -e ".[dev]"
11
+ uv run pytest -q
12
+ uv run ruff check .
13
+ uv run mypy src
14
+ ```
15
+
16
+ A standard virtual environment with `pip install -e ".[dev]"` works the same way.
17
+
18
+ ## Guidelines
19
+
20
+ - No runtime dependencies. The standard library `statistics` and `math` are enough.
21
+ - Every metric needs an exact-value test against a hand-computed example and, where it
22
+ applies, a known limit (a regular train is 0, a Poisson process is 1).
23
+ - New metrics must cite the definition they implement in the docstring and in
24
+ docs/architecture.md, so the formula can be checked against a source.
25
+ - Parameters after `*` are keyword-only with no default values.
26
+ - A bug fix starts with a failing test.
27
+ - Run `uv run ruff format .` before committing.
28
+ - Commit messages follow `type(scope): description`.
29
+
30
+ ## Reporting issues
31
+
32
+ Open an issue with the spike times, the call and its parameters, and the value you expected
33
+ versus the value you observed.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amaar Chughtai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.4
2
+ Name: spikestats
3
+ Version: 0.2.0
4
+ Summary: Spike-train statistics (firing rate, CV, CV2, Fano factor, local variation Lv and LvR, PSTH, time-resolved rate and Fano) in pure Python with zero dependencies.
5
+ Project-URL: Homepage, https://github.com/amaar-mc/spikestats
6
+ Project-URL: Repository, https://github.com/amaar-mc/spikestats
7
+ Project-URL: Issues, https://github.com/amaar-mc/spikestats/issues
8
+ Project-URL: Changelog, https://github.com/amaar-mc/spikestats/blob/main/CHANGELOG.md
9
+ Author: Amaar Chughtai
10
+ License: MIT License
11
+
12
+ Copyright (c) 2026 Amaar Chughtai
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: coefficient-of-variation,computational-neuroscience,fano-factor,firing-rate,local-variation,neuromorphic,neuroscience,point-process,spike-train,spiking-neural-networks
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Topic :: Scientific/Engineering
42
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
43
+ Classifier: Typing :: Typed
44
+ Requires-Python: >=3.10
45
+ Provides-Extra: dev
46
+ Requires-Dist: hypothesis>=6; extra == 'dev'
47
+ Requires-Dist: mypy>=1.11; extra == 'dev'
48
+ Requires-Dist: pytest>=8; extra == 'dev'
49
+ Requires-Dist: ruff>=0.6; extra == 'dev'
50
+ Description-Content-Type: text/markdown
51
+
52
+ # spikestats
53
+
54
+ <p align="center">
55
+ <img src="assets/logo.png" alt="spikestats logo" width="160">
56
+ </p>
57
+
58
+ Spike-train statistics in pure Python, with zero dependencies.
59
+
60
+ `spikestats` computes the standard measures of spike-train rate and variability from a plain
61
+ list of spike times: firing rate, inter-spike intervals, coefficient of variation, CV2,
62
+ local variation (Lv and the refractory-corrected LvR), spike counts, and the Fano factor.
63
+ There is nothing to configure and nothing to install beyond the package itself: the input
64
+ is a `list[float]` of spike times and the output is a `float`.
65
+
66
+ It pairs with [spikegen](https://github.com/amaar-mc/spikegen) for generating trains and
67
+ [spikedist](https://github.com/amaar-mc/spikedist) for comparing them. The three share the
68
+ same plain-list data model, so they compose without adapters.
69
+
70
+ ## Why
71
+
72
+ The established tools for these measures, Elephant and spiketools, are excellent but pull in
73
+ NumPy, SciPy, and custom data objects (`neo.SpikeTrain` and friends). When you only need a
74
+ firing rate or a CV from a list of spike times, that is a heavy dependency tree to carry, and
75
+ it is awkward in teaching material, small scripts, and lightweight pipelines. `spikestats`
76
+ keeps the math, drops the dependencies, and works on the lists you already have.
77
+
78
+ ## Install
79
+
80
+ ```sh
81
+ pip install spikestats
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ```python
87
+ import spikestats as ss
88
+
89
+ spikes = [0.012, 0.031, 0.058, 0.090, 0.110, 0.155] # seconds
90
+
91
+ ss.firing_rate(spikes, duration=0.2) # spikes per second
92
+ ss.inter_spike_intervals(spikes) # consecutive differences
93
+ ss.cv_isi(spikes) # coefficient of variation of the ISIs
94
+ ss.cv2(spikes) # Holt et al. 1996, robust to rate drift
95
+ ss.lv(spikes) # Shinomoto et al. 2003 local variation
96
+ ss.lvr(spikes, refractory=0.002) # Shinomoto et al. 2009, refractory-corrected
97
+ ss.spike_counts(spikes, duration=0.2, bin_width=0.05)
98
+ ss.fano_factor(spikes, duration=0.2, bin_width=0.05)
99
+ ```
100
+
101
+ ### Time-resolved metrics
102
+
103
+ ```python
104
+ import spikestats as ss
105
+
106
+ spikes = [0.012, 0.031, 0.058, 0.090, 0.110, 0.155, 0.210, 0.245] # seconds
107
+
108
+ # Non-overlapping bin counts tiling [0, duration)
109
+ counts = ss.binned_spike_counts(spikes, duration=0.3, bin_width=0.1)
110
+ # => [3, 3, 2] (one list[int] per bin)
111
+
112
+ # Sliding-window firing rate: returns (center_times, rates_hz)
113
+ centers, rates = ss.time_resolved_rate(spikes, duration=0.3, bin_width=0.1, step=0.05)
114
+
115
+ # PSTH averaged over multiple trials: returns (bin_center_times, mean_rate_hz)
116
+ trials = [spikes, [0.020, 0.060, 0.100, 0.140, 0.200]]
117
+ bin_centers, mean_rates = ss.psth(trials, duration=0.3, bin_width=0.1)
118
+
119
+ # Per-bin Fano factor across trials: returns (bin_center_times, fano_per_bin)
120
+ bin_centers, fano = ss.time_resolved_fano(trials, duration=0.3, bin_width=0.1)
121
+ ```
122
+
123
+ ## API
124
+
125
+ All functions take spike times as a sequence of numbers and sort them internally.
126
+
127
+ ### Scalar metrics
128
+
129
+ - `firing_rate(spikes, *, duration)`: spike count divided by `duration`.
130
+ - `inter_spike_intervals(spikes)`: list of consecutive differences (empty for fewer than two spikes).
131
+ - `cv_isi(spikes)`: population standard deviation of the ISIs over their mean. Regular train 0, Poisson 1.
132
+ - `cv2(spikes)`: mean of `2 * |I(n+1) - I(n)| / (I(n+1) + I(n))` over adjacent intervals.
133
+ - `lv(spikes)`: mean of `3 * ((I(n) - I(n+1)) / (I(n) + I(n+1)))^2`. Regular train 0, Poisson 1.
134
+ - `lvr(spikes, *, refractory)`: LvR with a refractoriness constant in the spike-time unit.
135
+ - `spike_counts(spikes, *, duration, bin_width)`: counts per equal-width bin tiling `[0, n * bin_width)`.
136
+ - `fano_factor(spikes, *, duration, bin_width)`: population variance of the bin counts over their mean.
137
+
138
+ ### Time-resolved metrics
139
+
140
+ All functions use the half-open boundary `[0, duration)`: a spike at exactly `duration` is excluded.
141
+
142
+ - `binned_spike_counts(spikes, *, duration, bin_width) -> list[int]`: spike counts in
143
+ `int(duration / bin_width)` consecutive non-overlapping bins tiling `[0, duration)`.
144
+ - `time_resolved_rate(spikes, *, duration, bin_width, step) -> tuple[list[float], list[float]]`:
145
+ sliding-window firing rate. Window of width `bin_width` steps by `step` across `[0, duration)`.
146
+ Returns `(window_center_times, rates_in_hz)`. Window positions are computed by integer indexing
147
+ of the step size to avoid floating-point drift.
148
+ - `psth(trials, *, duration, bin_width) -> tuple[list[float], list[float]]`: peristimulus time
149
+ histogram. `trials` is a sequence of spike-time sequences. Returns `(bin_center_times,
150
+ mean_rate_per_bin_hz)` averaged over trials. Raises `ValueError` on empty `trials`.
151
+ - `time_resolved_fano(trials, *, duration, bin_width) -> tuple[list[float], list[float]]`:
152
+ per-bin Fano factor (population variance / mean of per-trial counts). Returns
153
+ `(bin_center_times, fano_per_bin)`. Bins with zero mean across all trials are returned as
154
+ `float('nan')`. Uses population variance (denominator N), so a single trial always gives 0.
155
+
156
+ Parameters after `*` are keyword-only and have no default values; pass them explicitly.
157
+
158
+ ## Notes
159
+
160
+ - `cv_isi` uses the population standard deviation, matching the common `numpy.std` default.
161
+ - `cv2`, `lv`, and `lvr` need at least three spikes; they raise a clear `ValueError` otherwise.
162
+ - `spike_counts` uses `floor(duration / bin_width)` equal-width bins, so every bin has the same
163
+ width; any remainder of the duration and any spikes outside the binned window are ignored.
164
+ - `binned_spike_counts` uses `int(duration / bin_width)` bins (same count). All time-resolved
165
+ functions use the half-open interval `[0, duration)`: a spike at `t == duration` is excluded.
166
+ - `time_resolved_fano` uses population variance (denominator N). With a single trial, variance
167
+ is 0 for every bin; use multiple trials to get meaningful Fano estimates. Bins where the
168
+ mean count is 0 across all trials are returned as `float('nan')`.
169
+ - PyPI publish is queued behind the new-project-creation quota (currently returning 429 for new
170
+ packages). The dist is twine-verified and ready; upload will proceed once the quota resets.
171
+
172
+ ## License
173
+
174
+ MIT
@@ -0,0 +1,123 @@
1
+ # spikestats
2
+
3
+ <p align="center">
4
+ <img src="assets/logo.png" alt="spikestats logo" width="160">
5
+ </p>
6
+
7
+ Spike-train statistics in pure Python, with zero dependencies.
8
+
9
+ `spikestats` computes the standard measures of spike-train rate and variability from a plain
10
+ list of spike times: firing rate, inter-spike intervals, coefficient of variation, CV2,
11
+ local variation (Lv and the refractory-corrected LvR), spike counts, and the Fano factor.
12
+ There is nothing to configure and nothing to install beyond the package itself: the input
13
+ is a `list[float]` of spike times and the output is a `float`.
14
+
15
+ It pairs with [spikegen](https://github.com/amaar-mc/spikegen) for generating trains and
16
+ [spikedist](https://github.com/amaar-mc/spikedist) for comparing them. The three share the
17
+ same plain-list data model, so they compose without adapters.
18
+
19
+ ## Why
20
+
21
+ The established tools for these measures, Elephant and spiketools, are excellent but pull in
22
+ NumPy, SciPy, and custom data objects (`neo.SpikeTrain` and friends). When you only need a
23
+ firing rate or a CV from a list of spike times, that is a heavy dependency tree to carry, and
24
+ it is awkward in teaching material, small scripts, and lightweight pipelines. `spikestats`
25
+ keeps the math, drops the dependencies, and works on the lists you already have.
26
+
27
+ ## Install
28
+
29
+ ```sh
30
+ pip install spikestats
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```python
36
+ import spikestats as ss
37
+
38
+ spikes = [0.012, 0.031, 0.058, 0.090, 0.110, 0.155] # seconds
39
+
40
+ ss.firing_rate(spikes, duration=0.2) # spikes per second
41
+ ss.inter_spike_intervals(spikes) # consecutive differences
42
+ ss.cv_isi(spikes) # coefficient of variation of the ISIs
43
+ ss.cv2(spikes) # Holt et al. 1996, robust to rate drift
44
+ ss.lv(spikes) # Shinomoto et al. 2003 local variation
45
+ ss.lvr(spikes, refractory=0.002) # Shinomoto et al. 2009, refractory-corrected
46
+ ss.spike_counts(spikes, duration=0.2, bin_width=0.05)
47
+ ss.fano_factor(spikes, duration=0.2, bin_width=0.05)
48
+ ```
49
+
50
+ ### Time-resolved metrics
51
+
52
+ ```python
53
+ import spikestats as ss
54
+
55
+ spikes = [0.012, 0.031, 0.058, 0.090, 0.110, 0.155, 0.210, 0.245] # seconds
56
+
57
+ # Non-overlapping bin counts tiling [0, duration)
58
+ counts = ss.binned_spike_counts(spikes, duration=0.3, bin_width=0.1)
59
+ # => [3, 3, 2] (one list[int] per bin)
60
+
61
+ # Sliding-window firing rate: returns (center_times, rates_hz)
62
+ centers, rates = ss.time_resolved_rate(spikes, duration=0.3, bin_width=0.1, step=0.05)
63
+
64
+ # PSTH averaged over multiple trials: returns (bin_center_times, mean_rate_hz)
65
+ trials = [spikes, [0.020, 0.060, 0.100, 0.140, 0.200]]
66
+ bin_centers, mean_rates = ss.psth(trials, duration=0.3, bin_width=0.1)
67
+
68
+ # Per-bin Fano factor across trials: returns (bin_center_times, fano_per_bin)
69
+ bin_centers, fano = ss.time_resolved_fano(trials, duration=0.3, bin_width=0.1)
70
+ ```
71
+
72
+ ## API
73
+
74
+ All functions take spike times as a sequence of numbers and sort them internally.
75
+
76
+ ### Scalar metrics
77
+
78
+ - `firing_rate(spikes, *, duration)`: spike count divided by `duration`.
79
+ - `inter_spike_intervals(spikes)`: list of consecutive differences (empty for fewer than two spikes).
80
+ - `cv_isi(spikes)`: population standard deviation of the ISIs over their mean. Regular train 0, Poisson 1.
81
+ - `cv2(spikes)`: mean of `2 * |I(n+1) - I(n)| / (I(n+1) + I(n))` over adjacent intervals.
82
+ - `lv(spikes)`: mean of `3 * ((I(n) - I(n+1)) / (I(n) + I(n+1)))^2`. Regular train 0, Poisson 1.
83
+ - `lvr(spikes, *, refractory)`: LvR with a refractoriness constant in the spike-time unit.
84
+ - `spike_counts(spikes, *, duration, bin_width)`: counts per equal-width bin tiling `[0, n * bin_width)`.
85
+ - `fano_factor(spikes, *, duration, bin_width)`: population variance of the bin counts over their mean.
86
+
87
+ ### Time-resolved metrics
88
+
89
+ All functions use the half-open boundary `[0, duration)`: a spike at exactly `duration` is excluded.
90
+
91
+ - `binned_spike_counts(spikes, *, duration, bin_width) -> list[int]`: spike counts in
92
+ `int(duration / bin_width)` consecutive non-overlapping bins tiling `[0, duration)`.
93
+ - `time_resolved_rate(spikes, *, duration, bin_width, step) -> tuple[list[float], list[float]]`:
94
+ sliding-window firing rate. Window of width `bin_width` steps by `step` across `[0, duration)`.
95
+ Returns `(window_center_times, rates_in_hz)`. Window positions are computed by integer indexing
96
+ of the step size to avoid floating-point drift.
97
+ - `psth(trials, *, duration, bin_width) -> tuple[list[float], list[float]]`: peristimulus time
98
+ histogram. `trials` is a sequence of spike-time sequences. Returns `(bin_center_times,
99
+ mean_rate_per_bin_hz)` averaged over trials. Raises `ValueError` on empty `trials`.
100
+ - `time_resolved_fano(trials, *, duration, bin_width) -> tuple[list[float], list[float]]`:
101
+ per-bin Fano factor (population variance / mean of per-trial counts). Returns
102
+ `(bin_center_times, fano_per_bin)`. Bins with zero mean across all trials are returned as
103
+ `float('nan')`. Uses population variance (denominator N), so a single trial always gives 0.
104
+
105
+ Parameters after `*` are keyword-only and have no default values; pass them explicitly.
106
+
107
+ ## Notes
108
+
109
+ - `cv_isi` uses the population standard deviation, matching the common `numpy.std` default.
110
+ - `cv2`, `lv`, and `lvr` need at least three spikes; they raise a clear `ValueError` otherwise.
111
+ - `spike_counts` uses `floor(duration / bin_width)` equal-width bins, so every bin has the same
112
+ width; any remainder of the duration and any spikes outside the binned window are ignored.
113
+ - `binned_spike_counts` uses `int(duration / bin_width)` bins (same count). All time-resolved
114
+ functions use the half-open interval `[0, duration)`: a spike at `t == duration` is excluded.
115
+ - `time_resolved_fano` uses population variance (denominator N). With a single trial, variance
116
+ is 0 for every bin; use multiple trials to get meaningful Fano estimates. Bins where the
117
+ mean count is 0 across all trials are returned as `float('nan')`.
118
+ - PyPI publish is queued behind the new-project-creation quota (currently returning 429 for new
119
+ packages). The dist is twine-verified and ready; upload will proceed once the quota resets.
120
+
121
+ ## License
122
+
123
+ MIT
@@ -0,0 +1,18 @@
1
+ # Security Policy
2
+
3
+ ## Scope
4
+
5
+ `spikestats` is a pure computation library with no runtime dependencies, no network access,
6
+ and no file system access. The attack surface is limited to incorrect results from
7
+ malformed input, which the library guards against with explicit validation.
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ If you find a security issue, please email amaar2cool@gmail.com with details and steps to
12
+ reproduce. Please do not open a public issue for security reports. You can expect an
13
+ initial response within a few days.
14
+
15
+ ## Supported versions
16
+
17
+ The latest published minor version receives fixes. Pre-1.0 releases may introduce breaking
18
+ changes in minor versions, as allowed by semantic versioning.
Binary file