Physical Units¶
Parsing and conversion for the unit strings a channel carries in
physical_dimension. parse_unit splits a decimal-prefixed unit into its
quantity and exponent ("uV" -> ("V", -6)); conversion_factor gives the exact
power-of-ten multiplier between two units of the same quantity, or None when
they are not convertible.
This is what lets bids.apply_channels_tsv adopt a BIDS channels.tsv units
column by converting the samples rather than relabelling them (issue #122).
Parsing is case-sensitive on purpose: m is milli and M is mega, and a
lenient reading of MV as millivolts would be a 10^9 error in the values.
Module Documentation¶
biosigio.units
¶
Physical-unit parsing and conversion for channel physical_dimension strings.
A channel carries its values and its unit label as two independent pieces of
metadata, and nothing in the data model forces them to agree. They must, so any
code that adopts a unit from an external source (a BIDS channels.tsv, a
user-supplied override) has to convert the samples at the same moment it sets
the label. :func:conversion_factor is the arithmetic behind that; see
:func:biosigio.bids.apply_channels_tsv for the caller that motivated it
(issue #122).
Every unit here is a decimal-prefixed base symbol, so a unit is fully described
by (base symbol, decimal exponent) and a conversion is 10 ** (from - to).
Keeping the exponent an int rather than a float multiplier matters: 1e-6
is not exactly representable in binary, so 1.0 / 1e-6 is
1000000.0000000001 while 10.0 ** 6 is exactly 1000000.0. A volts ->
microvolts rescale therefore introduces no error of its own.
Parsing is deliberately case-sensitive. m (milli) and M (mega) differ
by 10^9, and a lenient parser that treated MV as millivolts would turn a
label mismatch into a silent numeric one -- the exact failure mode this module
exists to prevent. An unrecognized spelling returns None (not convertible)
so the caller can keep the importer's values and warn, which is always safe.
_BASE_SYMBOLS = tuple(sorted(_BASE_UNITS, key=len, reverse=True))
module-attribute
¶
_BASE_UNITS = {'V': ('V', 0), 'T': ('T', 0), 'T/m': ('T/m', 0), 'T/cm': ('T/m', 2), 'A': ('A', 0), 'S': ('S', 0), 'Ohm': ('Ohm', 0), 'ohm': ('Ohm', 0), 'Ω': ('Ohm', 0), 'Ω': ('Ohm', 0), 'N': ('N', 0), 'g': ('g', 0), 'm': ('m', 0), 's': ('s', 0), 'Hz': ('Hz', 0), 'deg/s': ('deg/s', 0), 'rad/s': ('rad/s', 0)}
module-attribute
¶
_MANGLED_MICRO = '\x83Ê'
module-attribute
¶
_NON_UNITS = frozenset({'n/a', 'none', 'unknown', 'a.u.', 'a.u', 'arbitrary', '-', '?'})
module-attribute
¶
_PREFIX_EXPONENTS = {'f': -15, 'p': -12, 'n': -9, 'µ': -6, 'μ': -6, 'u': -6, 'm': -3, 'c': -2, 'd': -1, '': 0, 'k': 3, 'M': 6, 'G': 9}
module-attribute
¶
conversion_factor(from_unit, to_unit)
¶
The multiplier that re-expresses a value from from_unit in to_unit.
Args: from_unit: The unit the values are currently in. to_unit: The unit the values should be expressed in.
Returns:
A float k such that value_in_to_unit == value_in_from_unit * k,
or None when either unit is unparsable or the two measure different
quantities. None means "do not touch the values"; it is never a
conversion of 1.0 in disguise.
Examples: >>> conversion_factor("V", "uV") 1000000.0 >>> conversion_factor("uV", "uV") 1.0 >>> conversion_factor("V", "T") is None True
Source code in biosigio/units.py
parse_unit(unit)
¶
Split a unit string into its quantity and its decimal exponent.
Args:
unit: A physical unit label, e.g. "uV", "mV", "fT/cm".
Returns:
(quantity, exponent) such that one of unit equals
10 ** exponent of quantity -- e.g. "uV" -> ("V", -6) and
"fT/cm" -> ("T/m", -13). Returns None for an empty string, an
explicit non-unit ("n/a", "a.u."), or any spelling this module
does not recognize; callers treat None as "not convertible".
Examples: >>> parse_unit("uV") ('V', -6) >>> parse_unit("V") ('V', 0) >>> parse_unit("a.u.") is None True