Changelog¶
All notable changes to neurospatial will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
Changed¶
- BREAKING:
smoothing_methodrenamed tomethod. The smoothing/estimator keyword is now uniformly namedmethodacross every smoothing encoder (compute_spatial_rate(s),compute_view_rate(s),compute_egocentric_rate(s),is_place_cell,compute_directional_place_fields), the four rate result classes (SpatialRateResult/SpatialRatesResult,ViewRateResult/ViewRatesResult), the decoder (decode_session,decode_session_summary,BayesianDecoder), andsimulation.validate_simulation. This is a hard rename with no alias; defaults and numerics are unchanged ("diffusion_kde", or"binned"for the egocentric encoders). NWBwrite_spatial_ratesstores the estimator under the key"method"(schema bumped to2.0); this is a clean break with no back-compatibility shim, so spatial-rates tables written by earlier versions (schema1.x) no longer read. Ondecode_session_summary,methodnow names the smoothing estimator, so it is no longer accepted as a per-block decode kwarg (Poisson is the only likelihood and is applied unconditionally).
Corrected¶
method="binned"is not a dense-kernel memory mitigation. Earlier notes (through 0.6.0) recommendedbinnedto avoid the dense(n_bins, n_bins)kernel, butbinnedsmooths its rate map through the same diffusion kernel (viaenv.smooth), so all smoothing methods build a dense kernel. The only memory mitigation is fewer bins (a largerbin_size).
UX hardening — fail-loud errors, one count convention, docs & viewer polish (0.8.0)¶
A broad usability pass: silent failures now raise or warn with actionable messages, a few APIs were made consistent, and the docs/viewers were polished.
Breaking Changes¶
add_positionsis nowadd_positions(events, *, times, positions)— reordered to the canonical(data, times, positions)order and made keyword-only (a bare 1-D trajectory made a positional swap shape-indistinguishable). Migration:add_positions(events, times=times, positions=positions).- Assembly functions take
(n_time_bins, n_neurons)—detect_assemblies,assembly_activation,pairwise_correlations,reactivation_strengthnow matchdecode_position/bin_spikes_in_time. Migration: feed the defaultbin_spikes_in_time(...)output as-is, or transpose an old(n_neurons, n_time_bins)matrix. - Behavior result
summary()returns adict(was a formatted string). Migration: usestr(result)for the human-readable form. - Fail-loud instead of silent sentinels —
bin_atraises on wrong-dimension points on a graph/track env;heading_from_velocityraises when every sample is belowmin_speed(opt out withallow_all_nan=True); graph queries reject a multi-point coordinate batch.
Added¶
units=/frame=keyword args onfrom_samples,open_field,linear_track, andmazeset the metadata at construction.- Graph queries accept coordinates (not just bin indices) —
neighbors,path_between,reachable_frommap a coordinate viabin_at. ResultMixinon every result class — concise repr/HTML and a scalarsummary()dict.dir(neurospatial.ops)surfaces the lazily-exported ops (autocomplete).
Fixed¶
min_occupancythresholds raw occupancy seconds, not smoothed density — fixes silent all-zero place fields; consistent across all smoothing methods (single and batch);min_occupancy=0.0unchanged.- Grid allocation is preflighted — a transposed
(2, N)trajectory raises fast instead of OOMing; a likely-transposed array warns rather than false-rejecting a valid low-sample N-D environment. - Non-finite query rows map to the
-1sentinel per row (one NaN no longer empties the whole map). to_fileno-overwrite default;occupancy()shape-before-monotonicity;from_graphedge-distancevalidation.SpatialRateResult.plot()colorbar docs corrected + "Firing Rate (Hz)" label; actionable batchplot()error.- Interactive-viewer accessibility — HTML player keyboard-focus guard,
:focus-visible, aria-live toggled off during autoplay; napari region dock and track-builder layout/labels.
Documentation¶
- Version bumped to 0.8.0; dead links and fork-clone fixed; value-first runnable
quickstart; grouped/collapsible nav; new advanced architecture page;
viridisdefaults; example-notebook links normalized + internal link-check in docs CI.
0.6.0 - 2026-07-03¶
Breaking Changes¶
to_xarray()now returns a labeledxarray.Datasetinstead of anxarray.DataArray.- Population rate results (
SpatialRatesResult,DirectionalRatesResult,ViewRatesResult,EgocentricRatesResult) use dims("unit_id", "bin"). The rate matrix lives in thefiring_ratedata variable andunit_idstores real per-unit identity labels. - Decode results (
DecodingResult) use dims("time", "bin"); they have nounit_idaxis. - Duplicate
unit_idsraiseValueErrorbecause label-based xarray selection requires unique labels. - Batch encoding
to_dataframe()is now dense tidy: one row per(unit, bin), always carryingunit_id, bin-center coordinates,firing_rate, andoccupancy. - Per-unit metric tables moved to
summary_table(), which is indexed byunit_id. The oldneuron_ids=relabeling keyword is replaced byunit_ids=onsummary_table().
Added¶
- Real unit identity on population results via
unit_ids, plus singularunit_idon indexed/iterated single-unit results. summary_table()on batch encoding results and population PSTH results.- Experiment-shaped environment presets:
Environment.open_field(...),Environment.linear_track(...), andEnvironment.maze(...). SpatialRatesResult.label_cell_types()for multi-class labels, withSpatialRatesResult.classify()reserved for the boolean place-cell predicate.decode_position()accepts population rate result objects directly, and preserves their dtype (afloat32rate result decodes infloat32rather than being promoted back tofloat64).- Memory-safe summary decoding for long sessions.
decode_position_summaryreturns a newDecodingSummaryresult that streams over time and reduces each block to per-time scalars/vectors (map_position/map_bin,mean_position,posterior_entropy,peak_prob) without ever materializing the full(n_time, n_bins)posterior.decode_session_summaryis the matching one-call encode -> bin -> decode wrapper, and now also streams the time-binning so the full count matrix is never materialized either.DecodingSummarycarries the standard terminal verbs (to_dataframe(),summary(),plot(),to_xarray()). decode_session()anddecode_session_summary()gain a keyword-onlydtypeparameter (np.float32/np.float64, defaultnp.float64) that is honored end-to-end — a singledtype=np.float32controls both the encoding-model working set and the posterior, halving the decode working set on the golden path (no silent promotion back tofloat64). Defaultnp.float64leaves every existing caller byte-for-byte unchanged; any other dtype raisesValueError.decode_position()gains keyword-onlydtype(np.float32/np.float64, defaultnp.float64) and a hybridtime_chunk.time_chunk=None(the default) keeps the full-matmul path byte-for-byte unchanged;decode_position(time_chunk=N)computes the Poisson likelihood blockwise directly into the preallocated posterior, cutting the transient peak to ~1× over the returned posterior (tolerance-equal to the default, not byte-exact).decode_session()forwards both.- The summary decoders (
decode_position_summary/decode_session_summary) rejecttime_chunk=None(raising a clearValueError): aNonevalue would set the streaming block to the full session length and materialize the full(n_time, n_bins)posterior, defeating their never-materialize contract.time_chunkmust be a positive integer (default1024); usedecode_position/decode_sessionif you want the full posterior. compute_spatial_rates()gains a keyword-onlydtype(np.float32/np.float64, defaultnp.float64) to halve the memory of stored rate maps.- Speed filtering on the encode path:
compute_spatial_rate/compute_spatial_ratesgain keyword-onlyspeed/min_speed(forwarded by bothdecode_sessionanddecode_session_summary). Whenmin_speedis set, one shared per-interval speed gate filters both the spike numerator and the occupancy denominator, so amin_speedknob can never bias firing rates by filtering only one side. population_coverage()gains a keyword-onlyn_jobsparameter to parallelize per-neuron coverage; results are identical regardless ofn_jobs.
Fixed¶
decode_session/decode_session_summarynow validatedt(finite,> 0; non-numeric andboolrejected) up front with a clearValueError, matchingbin_spikes_in_time. Previously the shared decode-grid builder bypassed that guard, so an invaliddtleaked a cryptic downstream error (dt=0->ZeroDivisionError,dt=NaN-> "cannot convert float NaN to integer",dt<0-> a misleading "span smaller than one bin" message).bin_spikes_in_timenow validatesdtconsistently via the same shared helper: a non-numericdt(including a numeric string like"0.1") and abool(dt=True) now raise a clearValueError("dt must be a finite number > 0, ..."). Previously a numeric string leaked a rawTypeErroranddt=Truewas silently accepted as a chunk size of1.- An unparseable
dtype(e.g.dtype="bogus") now raises a clearValueErrornamingdtypeacross the decode/encode entry points (decode_position,decode_position_summary,compute_spatial_rates,decode_session,decode_session_summary) instead of a raw NumPyTypeError: data type 'bogus' not understood. decode_positionnow preservesfloat32when handed a rate-result object (anything exposing.firing_rates). Previously the friendly object path promoted afloat32.firing_ratestofloat64, silently losing part of thedtype=np.float32memory win the raw-array path already delivered. The object path now matches the raw-array path byte-for-byte:float32staysfloat32,float64staysfloat64, an integer rate map is promoted tofloat64, and aNone/ dict / non-2-D.firing_ratesstill raises the same clearValueError.time_chunkis now validated as a positive integer (notbool) acrossnormalize_to_posterior,decode_position,decode_position_summary, anddecode_session_summary, raising a clearValueErrornaming the value and its type. Previously a float (1.5) or string ("2") leaked a rawTypeError, andTruewas silently accepted as a chunk size of1.
Changed¶
- Firing-rate numerator/denominator alignment (behavior change for gappy /
out-of-bounds data).
compute_spatial_rate/compute_spatial_ratesgain a keyword-onlymax_gap(default0.5 s); spikes inside large tracking gaps and out-of-bounds excursions are now excluded from the numerator so it drops the identical set of intervals thatenv.occupancydrops from the denominator. Rate maps now differ (and are more correct) for sessions with large tracking gaps or out-of-bounds samples; passmax_gap=Noneto disable gap gating on both sides. SpatialRatesResult.summary_table()no longer double-computes grid and border scores (single pass, faster for large populations).- The dense smoothing kernels (
diffusion_kdeandgaussian_kde) areO(n_bins²)memory by construction. For very large bin counts they now emit a loudUserWarning(with the estimated size) and proceed — there is no hard limit and no opt-out parameter. To reduce memory, usesmoothing_method="binned"(or fewer bins / a largerbin_size).
Deprecated¶
EgocentricRatesResult.detect_ovcs(...)->classify(...)ViewRatesResult.detect_view_cells(...)->classify(...)DirectionalRatesResult.detect_hd_cells(...)->classify(...)SpatialRatesResult.detect_cell_types(...)->label_cell_types(...)ViewRateResult.peak_view_location()->peak_location()ViewRatesResult.peak_view_location()->peak_locations()detect_region_crossings(position_bins, times, region_name, env, ...)->detect_region_crossings(position_bins, times, env, *, region_name, ...)
0.1.0 - 2024-11-03¶
Added¶
- Initial release of neurospatial
- Core
Environmentclass with factory methods - Multiple layout engines (regular grid, hexagonal, triangular, graph-based)
- Region support for defining ROIs
- Composite environment functionality
- Alignment and transformation tools
- Comprehensive test suite
- NumPy-style docstrings throughout
Features¶
- Automatic active bin detection from data samples
- NetworkX-based connectivity graphs
- 1D linearization for track-based experiments
- Spatial queries (bin_at, neighbors, shortest_path, distance_between)
- Visualization with matplotlib
- Morphological operations (dilation, closing, hole filling)