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¶
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)