Review fixes for PR #65: map_channel_to_electrode, LFP boundaries, STTC, ImpedanceMeasurements#66
Review fixes for PR #65: map_channel_to_electrode, LFP boundaries, STTC, ImpedanceMeasurements#66MilagrosMarin wants to merge 4 commits into
Conversation
1) map channel to electrode function 2) Fix bug in trace extraction (currently doesn't account for file boundaries 3) Add impedance measurement tables 4) Add STTC tables
…ies, STTC, ImpedanceMeasurements) - Move neo/quantities/elephant imports to lazy imports inside STTC.make() to avoid breaking the module in environments without these packages - Fix map_channel_to_electrode: restrict ElectrodeConfig.Electrode fetch by probe_type (was unrestricted), add conflict detection across multiple configs, and deduplicate consistent mappings safely - Fix LFP boundary trimming: replace elif with two independent if blocks so single-file sessions correctly trim both start and end boundaries - Replace # REMOVE LATER comment in STTC.make() with proper explanation - Fix ImpedanceMeasurements.make(): move EphysSessionProbe existence check before the fetch to prevent KeyError on empty result Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…_to_electrode - Add explicit check when no ElectrodeConfig rows exist for the probe_type to prevent np.column_stack from crashing on empty arrays - Replace np.empty with np.full(..., -1) so unset lookup positions return a detectable sentinel value (-1) rather than uninitialized memory Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MilagrosMarin
left a comment
There was a problem hiding this comment.
PR #66 Self-Review — Review fixes for PR #65
This is a self-review of the changes in this PR before requesting @judewerth's sign-off. All 5 originally identified issues are addressed, plus 2 additional edge cases caught during review.
✅ Issue A — Lazy imports for neo, quantities, elephant
Confirmed: all three removed from module level, placed inside STTC.make(). Module now imports cleanly in environments without these packages.
✅ Issue B — map_channel_to_electrode: restricted fetch + conflict detection + edge cases
Original fix: probe.ElectrodeConfig.Electrode & {"probe_type": probe_type} (restricted), np.unique deduplication, conflict ValueError.
Two additional edge cases fixed in commit 61a37a9:
-
Empty fetch guard — if no
ElectrodeConfighas been inserted yet for the probe type, the fetch returns empty arrays andnp.column_stack([[], []])raises aValueErrorwith a cryptic message. Added an explicit check with a descriptive error before reachingcolumn_stack. -
np.empty→np.full(..., -1)—np.emptycreates an uninitialized array, so any lookup index not covered by the electrode config (e.g., partial configs) would silently return whatever value was in memory. Using-1as a sentinel makes uncovered positions detectable rather than silently wrong.
One minor suggestion for @judewerth (non-blocking): The return variable electrode_ids and the docstring Returns: electrodes (array-like)... are misleading when electrode_to_channel=True — the function returns channel indices in that direction, not electrode indices. Worth renaming to output_indices or updating the docstring in a follow-up.
✅ Issue C — LFP boundary elif → two independent if blocks
Confirmed: single-file sessions now correctly trim both start and end boundaries in a single lfps[:, start_idx:end_idx] slice. file_start parsed once. Multi-file and middle-file cases unaffected.
✅ Issue D — # REMOVE LATER in STTC.make()
Replaced with proper comment explaining the spike time clipping is required to keep spike times within t_stop for neo.SpikeTrain. Logic is correct and necessary.
✅ Issue E — ImpedanceMeasurements.make() existence check before fetch
Confirmed: if not (EphysSessionProbe & key) now runs before the fetch("port_id") call. A missing EphysSessionProbe now raises a clean ValueError instead of a KeyError on .pop().
Notes for @judewerth
- This PR supersedes PR #65 — once this merges, #65 can be closed.
- The workflow repo PR datajoint#211 (
integratebranch) importsmap_channel_to_electrodeandget_probe_typefrom this module — it should not merge until this PR is merged and the installed element is updated. neo,quantities, andelephantare not yet listed as dependencies insetup.py. They should be added before merging so theSTTCtable is installable by other users.
|
Closing in favor of a PR targeting judewerth:main (PR #65 branch) directly. |
Overview
This PR contains reviewed fixes for PR #65 (Changes to Ephys Schema by @judewerth). All of Jude's original changes are included here with the following corrections applied.
Fixes Applied
A — Lazy imports for
neo,quantities,elephantimport neo,import quantities as pq, andfrom elephant.spike_train_correlation import spike_time_tiling_coefficientwere moved from module-level to lazy imports insideSTTC.make().Why: These are optional heavy dependencies used only by
STTC.make(). Importing them at module level causesephys_no_curationto fail to import entirely on any environment where these packages are not installed (HPC nodes, minimal containers, dev machines not running spike sorting) — breaking LFP, frame analysis, and everything else that depends on this module. This follows the same lazy-import pattern applied tospikeinterfacein the workflow repo.B —
map_channel_to_electrode: restricted fetch + conflict detectionWhy: The original fetch was completely unrestricted — it pulled mappings from all electrode configs in the database regardless of
probe_type. This works accidentally while only one probe type is in the database, but silently returns a wrong mixed-probe mapping the moment a second probe type is added.probe_typeis already part ofElectrodeConfig.Electrode's primary key (via-> ProbeType.Electrode), so no join throughElectrodeConfigis needed. Thenp.uniquededuplication handles the case of multiple configs for the same probe type that share an identical mapping. If two configs have conflictingchannel_idxvalues for the same electrode, aValueErroris raised explicitly instead of silently overwriting with a non-deterministic value.C — LFP boundary trimming:
elifbug for single-file sessionsWhy: When a session lives entirely within one
.rhdfile,file_paths[0] == file_paths[-1]. Theelifmeans only the start boundary is trimmed — the end boundary is skipped, so those sessions silently include LFP data beyondend_time. The fix uses two independentifblocks so both boundaries are always evaluated, withfile_startparsed only once.D —
# REMOVE LATERcomment inSTTC.make()Replaced with a proper comment:
# clip spike times to session duration to guard against edge-case spikes beyond end_time. The line itself is necessary — without it,neo.SpikeTrainraises when any spike time exceedst_stop.E —
ImpedanceMeasurements.make(): existence check before fetchMoved
if not (EphysSessionProbe & key)before thefetch("port_id")call. Previously, if noEphysSessionProbeexisted, the fetch silently returned an empty set andport_id.pop()raised aKeyErrorinstead of the intendedValueError. Also removed the duplicate comment that incorrectly said "EphysSession" instead of "EphysSessionProbe".Test plan
from element_array_ephys.ephys_no_curation import map_channel_to_electrode, get_probe_typeimports without error in an environment withoutneo/elephantmap_channel_to_electrodereturns correct electrode indices for a known session.rhdfile produces correct durationSTTC.make()runs end-to-end for a session with spike sorted units🤖 Generated with Claude Code