API overview

Seven modules, each doing one stage of the pipeline. Everything takes and returns ordinary objects — a Recording, a SpikeTable, a Waveforms array, a UnitSet — and nothing holds global state.

This page lists the entry points with their defaults. Argument semantics that are not obvious from the name are explained on the methods page, which is where the reasoning lives.

axonforge.io#

read(path, *, fs=None, n_channels=None, dtype=None, gain_uv_per_bit=1.0, offset=0, dataset=None, chunk_s=30.0) → Recording
Dispatches on the suffix. Values from a sidecar are used where an argument is omitted. Returns a lazily mapped recording; nothing is read until a filter or detector asks for it.
read_spikes(path, *, time_unit="s") → SpikeTable
Reads spike times from CSV. Accepts "ms" and "samples" for time_unit; the last requires fs.
sanity(rec) → dict
Cheap checks on the first and last 10 s: saturation fraction per channel, flat channels, a comb-spectrum score that catches a wrong channel count, and the dynamic range in bits actually used.
concat_spikes(tables, offsets_s) → SpikeTable
Merges tables from separately processed files, shifting each by its offset. Unit labels are not reconciled across files — that is a sorting decision, not a bookkeeping one.

axonforge.filt#

bandpass(rec, *, low=300.0, high=6000.0, order=4, zero_phase=True) → Recording
Butterworth in second-order sections. With zero_phase the sections run forwards and backwards, doubling the effective order to eight.
common_median(rec, *, groups=None, exclude_bad=True) → Recording
Subtracts the per-sample median of each channel group. With groups=None the grouping from the sidecar is used, and failing that all channels form one group.
noise_sigma(rec, *, estimator="median", span_s=10.0) → ndarray
Per-channel noise estimate. The default is the median of the absolute value divided by 0.6745; pass "std" for the plain standard deviation, which is biased upwards by the spikes themselves.
notch(rec, *, freq=50.0, q=30.0, harmonics=3) → Recording
Mains removal. Rarely needed after bandpassing above 300 Hz, and worth avoiding when it is not: a narrow notch rings, and the ringing looks like a small spike.

axonforge.detect#

threshold(rec, *, k=4.5, polarity="neg", dead_time_ms=1.0, sigma=None, channels=None) → SpikeTable
Crossings of k × sigma, one event per crossing per channel, with dead_time_ms enforced afterwards. polarity takes "neg", "pos" or "both"; passing sigma explicitly skips re-estimation, which is what you want when comparing sessions.
dual_threshold(rec, *, k_hi=5.0, k_lo=2.5, **kw) → SpikeTable
Crossing of the high threshold opens an event, return above the low one closes it. Slower, and better behaved on recordings with slow amplitude changes.

axonforge.waveform#

extract(rec, spikes, *, pre_ms=0.5, post_ms=1.5, channels="group") → Waveforms
Cuts a window around each event. channels="group" takes the whole channel group of the detecting channel, "one" takes only it, and an explicit list takes exactly those.
align(wf, *, mode="peak", interp=True, max_shift_samples=4) → Waveforms
mode is "peak", "min_slope" or "centre_of_mass". Shifts beyond max_shift_samples are left unaligned and flagged in wf.flags rather than dropped.
templates(wf, labels) → ndarray
Mean waveform per label with its standard error, for plotting and for reporting.

axonforge.sort#

features(wf, *, n_components=3, whiten=True, per_channel=False) → Features
Principal components of the aligned waveforms. With per_channel the decomposition is computed separately on each channel of the group and the scores are concatenated.
cluster(feat, *, max_k=8, restarts=20, criterion="bic", covariance="full", seed=0) → UnitSet
Gaussian mixtures for every k from 1 to max_k, each fitted restarts times from different initialisations; the model order with the best criterion wins. criterion also accepts "aic".
UnitSet.merge(a, b) / UnitSet.drop(label) → UnitSet
Curation as explicit operations. Both return a new set and record the action in the provenance, so the container shows what was done by hand.

axonforge.quality#

isi_violations(st, *, refractory_ms=1.5, censored_ms=0.0) → float
Fraction of intervals shorter than the refractory period. Report the period you used; 1.5 ms and 2.0 ms give visibly different numbers on the same data.
l_ratio(feat, labels, unit) → float
Mahalanobis-based measure of how much of the surrounding cloud leaks into the cluster.
isolation_distance(feat, labels, unit) → float
Undefined, and returned as nan, when the cluster holds more than half of all events — a case that occurs more often than the literature suggests.
presence_ratio(st, *, bin_s=60.0) → float
Fraction of time bins containing at least one spike. Catches units that vanish halfway through a session, which is usually drift.

axonforge.stats#

isi(st, *, unit=None, log=False) → ndarray
Interspike intervals in seconds, optionally log-spaced for histogramming.
cv(st) / cv2(st) → float
Coefficient of variation over all intervals, and the local measure averaged over adjacent pairs. The local one is insensitive to slow rate changes, which is why both are reported.
fano(st, *, window_s=1.0) → float
Variance over mean of the spike count in fixed windows. The value depends strongly on the window, so the window is part of the result when printed.
autocorrelogram(st, *, bin_ms=1.0, span_ms=50.0, unit=None) → (counts, edges)
The zero-lag bin is excluded, not zeroed, so the counts can be used directly.
bursts(st, *, max_isi_ms=6.0, min_spikes=3) → ndarray
Interval-threshold burst detection. Deliberately the simple method; surprise-based detection is not implemented, because its result depends on a baseline rate that is itself the thing in question.

Command line#

axonforge detect RAW [options]
  --fs HZ                 sample rate, overrides the sidecar
  --channels N            channel count, overrides the sidecar
  --dtype NAME            int16 (default), int32, float32
  --bandpass LOW:HIGH     default 300:6000, use "none" to skip
  --no-cmr                skip common median referencing
  --k FLOAT               threshold in sigma, default 4.5
  --polarity neg|pos|both default neg
  --dead-time MS          default 1.0
  --jobs N                parallel channel groups, default 1
  --out PATH              .afz container to write

axonforge sort IN.afz --max-k 8 --components 3 --criterion bic
axonforge report IN.afz --raster --isi --waveforms --out report.pdf
axonforge selftest
axonforge --version

Every flag maps to the keyword argument of the same name, and axonforge detect --dry-run prints the call it would make in Python. The intention is that the command line is a convenience, never a separate implementation.