chronaxis.core.moments_reference
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""以精确有理数维护参考端的加权中心矩或均方。23输入是通过精度/质量门槛的有限二进制浮点数,正权重来自样本或实际选中区间。4Welford 更新只保存总权重、均值和中心矩。RMS 的有理数平方不会发生浮点中间5溢出;最后一次才按目标倍率和 float64 可表示性给出结果及质量。不依赖 NumPy。6"""78from fractions import Fraction9from math import ldexp, sqrt10from .types import QualityFlag as Q111213class ReferenceMoments:14 """每个归约器独享累计量,无样本列表、临时文件或跨会话缓存。"""1516 def __init__(self, operation):17 """创建零累计。参数: 操作名。返回: 无。异常: 调用方先做操作检查。"""18 self.operation = operation19 self.weight = self.mean = self.total = Fraction(0)2021 def push(self, values, weights=None):22 """合并有限块。参数: 有效浮点序列和可选同长正权重。返回: 无。异常: 无效权重或输入。"""23 for index, item in enumerate(values):24 x = Fraction(item)25 weight = Fraction(weights[index]) if weights is not None else Fraction(1)26 if weight <= 0:27 raise ValueError('二阶统计只接收正权重')28 self.weight += weight29 if self.operation.endswith('_rms'):30 self.total += weight * x * x31 else:32 delta = x - self.mean33 self.mean += delta * weight / self.weight34 self.total += weight * delta * (x - self.mean)3536 def finish(self, ddof, factor):37 """完成目标单位换算。参数: 自由度、正倍率。返回: (float或None,质量)。异常: 配置错误。"""38 denominator = self.weight - (ddof or 0)39 if denominator <= 0:40 return None, Q.NONE41 value = self.total / denominator42 if value == 0:43 return 0., Q.NONE44 try:45 if self.operation.endswith('_variance'):46 number = float(value * Fraction(factor))47 else:48 # 把倍率放到根号内并提取偶次二进制指数,避免平方范围决定根的可表示性。49 value *= Fraction(factor) ** 250 exponent = (value.numerator.bit_length() - value.denominator.bit_length()) // 251 scaled = value / (Fraction(2) ** (2 * exponent))52 number = ldexp(sqrt(float(scaled)), exponent)53 except OverflowError:54 return None, Q.NONFINITE55 return (number, Q.NONE) if number != 0 else (None, Q.PRECISION_LOSS)56