ezmsg.sigproc.util.channels#
One way to say “split these channels into groups”.
Several sources attach a structured CoordinateAxis to the channel dimension
carrying per-channel fields — e.g. ezmsg-blackrock’s ChannelMap emits a
ch axis whose .data is a numpy struct array with
x/y/size/label/array/bank/elec/headstage.
Operations that treat channels in groups — per-bank rereferencing, block
spatial filters — need to turn that metadata into index groups.
ChannelGroupSpec is the single spec type every such operation accepts,
and resolve_channel_groups() is the single resolver:
spec |
meaning |
|---|---|
|
no grouping (caller’s default applies) |
|
group by that struct-array field |
|
group by the tuple of those fields |
|
explicit index groups |
|
anything else |
Groups are returned in first-appearance order along the channel axis so a resolved grouping is reproducible and readable against the channel table.
Module Attributes
How to split a channel axis into groups. |
Functions
- channel_groups_from_field(message, axis=None, field='bank')[source]#
Group channel indices by one or more fields of a structured coordinate axis.
- Parameters:
message (AxisArray) – Message whose
axiscoordinate is a structuredCoordinateAxis(its.datais a structured numpy array).axis (str | None) – Channel axis name.
Nonedefaults to the last dimension.field (str | Sequence[str]) – Struct-array field to group by (e.g.
"bank"), or a sequence of fields to group by their tuple (e.g.("array", "bank")).
- Returns:
Index groups, one per distinct value, in first-appearance order.
Nonewhen the axis carries no usable structured field (no such axis, no.data, unstructured.data, a field is absent, or the per-channel length doesn’t match the data). ReturningNonerather than a single all-channel group lets callers distinguish “no metadata, fall back to my default” from “one bank”.- Return type:
- group_spec_fields(spec)[source]#
The metadata field names spec groups by, or
Noneif it needs none.Explicit index groups, callables and
Noneall returnNone: nothing about them can change with the message.Deliberately O(1) — a field spec is discriminated by its first element, never by scanning all of them, because this runs on the per-message hash path.
resolve_channel_groups()does the full homogeneity check once, at state reset, so a malformed spec still fails loudly.
- group_spec_fingerprint(message, axis, spec)[source]#
O(1) summary of whether spec can resolve against this message.
Transformers fold this into their per-message state hash so a stream that gains or loses the grouping field re-resolves its groups, without paying to hash the field’s bytes on every message. Field values changing under a fixed key and channel count is deliberately not detected — a genuine remap arrives with a new key or channel count.
The two common specs –
Noneand a single field name – are classified inline rather than throughgroup_spec_fields(), because at this call rate the function call itself is a measurable share of the cost. Both shortcuts must agree with that function; everything less trivial defers to it.
- resolve_channel_groups(message, axis, spec)[source]#
Turn a
ChannelGroupSpecinto validated index groups.Returns
Nonewhen spec isNoneor when a metadata-derived spec finds nothing to group by — in both cases the caller applies its own default (typically “all channels in one group”).
- validate_channel_groups(groups, n_channels)[source]#
Raise
ValueErrorunless groups are in-range and pairwise disjoint.Disjointness matters because every consumer of a grouping assumes a channel belongs to at most one group — a channel listed twice would be rereferenced twice, or would have two weight blocks written to the same output. Not every channel has to appear: omitted channels are the caller’s business (rereferencing passes them through unchanged).
An empty groups list, or empty groups within it, validates trivially.