chronaxis.core.affine_numpy
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""按列执行单位的完整仿射转换,保持固定类型和逐位置质量掩码。23输入为 NumericArray、静态倍率/偏移与目标类型;输出形状保持(N,)的独立4只读 NumericArray。整数复用已有数组仿射核的精确逆区间算法;浮点使用5NumPy ufunc与有效掩码,不调用标量参考数值计算,不使用object数组或6逐样本Python回调。无效位置不参与运算,已有质量原因保留;不读写文件、7不改调用方数组、不保存样本历史。单位关系由表达式或结果编译器事先证明。8"""910# 共享静态声明检查;实际数组计算与标量参考分开。11from math import isfinite1213import numpy as np1415from .affine import _validate_affine16from .normalize_numpy import _affine_integer17from .numeric import INTEGER_BOUNDS18from .numeric_numpy import NumericArray, _output, to_float_numpy19from .types import QualityFlag, SignalDType202122def _float_coefficient(value: int | float) -> tuple[float | None, QualityFlag]:23 """仅转换一个编译期系数,不对样本调用Python标量参考。2425 参数: value为已验证的原生整数或有限浮点系数。26 返回: 有限浮点值与NONE,或None与NONFINITE/PRECISION_LOSS。27 异常: 无主动异常;整数超出浮点范围转换成NONFINITE。28 """29 # 配置常量数量固定,与样本数量无关;整数往返不能先舍入原整数。30 try:31 result = float(value)32 except OverflowError:33 return None, QualityFlag.NONFINITE34 if not isfinite(result):35 return None, QualityFlag.NONFINITE36 if type(value) is int and int(result) != value:37 return None, QualityFlag.PRECISION_LOSS38 return result, QualityFlag.NONE394041def affine_numpy(value: NumericArray, scale: int | float, offset: int | float,42 dtype: SignalDType) -> NumericArray:43 """在有效位置执行value*scale+offset,不以中间回绕或舍入替代结果。4445 参数: value为(N,)数值列;scale、offset为有限静态系数;dtype为目标46 数值类型。整数目标要求整数输入和整数系数;需要小数时应声明float64。47 返回: 同长独立只读NumericArray;无效位置仅存零占位,原因保持或增加48 OVERFLOW、NONFINITE、PRECISION_LOSS。输入和有效/质量列不被修改。49 异常: TypeError/ValueError为声明结构错误;MemoryError等分配异常传播。50 边界: 温度偏移抵消为零有效;只有零偏移、非零因子的非零值变成零时,51 才按纯倍率下溢报告精度损失。全部数值操作按数组执行。52 """53 # 结构与静态目标类型错误不能悄悄当成数据缺失。54 if not isinstance(value, NumericArray):55 raise TypeError('仿射数组转换需要NumericArray')56 _validate_affine(value.dtype, dtype, scale, offset)57 # 整数复用已验数组算法,先求完整仿射的合法输入区间,再执行安全乘加。58 if dtype in INTEGER_BOUNDS:59 values, overflow = _affine_integer(value.values, value.valid, dtype, scale, offset)60 quality = value.quality.copy()61 quality[overflow] |= np.uint16(QualityFlag.OVERFLOW)62 return _output(dtype, values, quality)63 # 输入整数的精度用数组尾位掩码核实,不能先astype再与已舍入值比较。64 converted = to_float_numpy(value)65 quality = converted.quality.copy()66 factor, factor_quality = _float_coefficient(scale)67 shift, shift_quality = _float_coefficient(offset)68 coefficient_quality = factor_quality | shift_quality69 values = np.zeros(len(value.values), dtype=np.float64)70 # 系数精度失败影响原本有效的输入,保留同时发生的输入精度原因。71 if coefficient_quality != QualityFlag.NONE:72 quality[value.valid] |= np.uint16(coefficient_quality)73 return _output(dtype, values, quality)74 # ufunc只触碰通过精度检查的位置;中间浮点异常在完整结果上明确分类。75 selected = converted.valid76 with np.errstate(over='ignore', under='ignore', invalid='ignore'):77 np.multiply(converted.values, factor, out=values, where=selected)78 np.add(values, shift, out=values, where=selected)79 quality[selected & ~np.isfinite(values)] |= np.uint16(QualityFlag.NONFINITE)80 # 偏移抵消为零是合法数值,不将-273.15℃转换到0K标成下溢。81 if shift == 0 and factor != 0:82 underflow = selected & (converted.values != 0) & (values == 0)83 quality[underflow] |= np.uint16(QualityFlag.PRECISION_LOSS)84 return _output(dtype, values, quality)85