Python中整合Pint与Uncertainties处理单位和不确定度的问题
问题背景
我正在开发一个用于分析光谱等数据的Python库,需要同时处理数据的单位和不确定度。例如,我们有波长(nm)、计数、功率(W)这类数据,希望能通过Pint轻松实现单位转换(如nm转um),同时借助Uncertainties处理不确定度传递(如对power执行sqrt操作并保留不确定度),理想情况下可通过pandas.DataFrame存储和处理这些数据。
目前无法将Pint的Quantity及数量数组与uncertainties.unumpy模块正确整合,遇到两个主要问题:
1. unumpy操作不支持pint.Quantity
尝试对带不确定度的Quantity执行平方根操作的代码:
from uncertainties import unumpy as unp from pint import UnitRegistry ureg = UnitRegistry() Qty = ureg.Quantity a = Qty(3, 'um').plus_minus(0.1) b = unp.sqrt(a)
返回错误,要求Quantity为无量纲:
File "C:\Users\USERNAME\AppData\Local\Programs\Python\Python310\lib\site-packages\pint\facets\plain\quantity.py", line 707, in __float__ raise DimensionalityError(self._units, "dimensionless") pint.errors.DimensionalityError: Cannot convert from 'micrometer' to 'dimensionless'
注意,使用numpy执行相同操作也会返回类似uncertainties.ufloat的错误:
TypeError: loop of ufunc does not support argument 0 of type Measurement which has no callable sqrt method
2. unumpy.uarray或pint.Measurement.from_list无法初始化pint.Measurement对象数组
第一种方法返回如下错误:
File "C:\Users\USERNAME\AppData\Local\Programs\Python\Python310\lib\site-packages\pint\facets\plain\quantity.py", line 707, in __float__ raise DimensionalityError(self._units, "dimensionless") pint.errors.DimensionalityError: Cannot convert from 'micrometer' to 'dimensionless'
第二种方法返回与uncertainties相关的错误:
File "C:\Users\USERNAME\AppData\Local\Programs\Python\Python310\lib\site-packages\uncertainties\core.py", line 2714, in raise_error raise TypeError("can't convert an affine function (%s)" TypeError: can't convert an affine function (<class 'uncertainties.core.Variable'>) to float; use x.nominal_value
解决方案与替代方案
1. 手动拆分-处理-重组(基础整合思路)
Unumpy和Pint的核心冲突在于两者都试图封装数值类型,无法直接兼容。可以手动拆解Pint Measurement的标称值和不确定度,用unumpy处理后再重新包装为Pint Quantity:
from uncertainties import unumpy as unp from pint import UnitRegistry ureg = UnitRegistry() Qty = ureg.Quantity # 带不确定度的Pint量 a = Qty(3, 'um').plus_minus(0.1) # 拆解标称值和不确定度,转为unumpy数组 nominal = unp.uarray([a.nominal_value], [a.std_dev]) # 执行平方根操作 nominal_sqrt = unp.sqrt(nominal) # 重新包装为Pint Measurement,注意单位也要做对应转换(平方根后单位是um^0.5) b = Qty(nominal_sqrt[0].nominal_value, a.units**0.5).plus_minus(nominal_sqrt[0].std_dev)
这种方法需要手动处理单位转换逻辑,适合简单操作。
2. 使用Pint的内置不确定度扩展(替代Uncertainties)
Pint本身支持通过plus_minus创建带不确定度的Measurement对象,且可以通过注册自定义numpy ufunc来支持数学操作。例如为sqrt操作注册适配:
import numpy as np from pint import UnitRegistry ureg = UnitRegistry() Qty = ureg.Quantity # 为numpy.sqrt注册Pint兼容的处理函数 @ureg.wraps(ureg.Quantity, ureg.Quantity) def sqrt_quantity(q): nominal = np.sqrt(q.nominal_value) # 不确定度传递公式:sqrt(x)的不确定度是 (0.5 / sqrt(x)) * dx std_dev = 0.5 * q.std_dev / np.sqrt(q.nominal_value) return Qty(nominal, q.units**0.5).plus_minus(std_dev) # 使用示例 a = Qty(3, 'um').plus_minus(0.1) b = sqrt_quantity(a) print(b) # 输出 ~1.732 um^0.5 +/- 0.0289
这种方式不需要依赖Uncertainties,直接用Pint的API处理,更贴合Pint的生态,也能自然支持单位转换。
3. 数组初始化的正确方式
如果需要创建Pint Measurement数组,不要用unumpy.uarray,而是直接用Pint的API批量创建:
from pint import UnitRegistry ureg = UnitRegistry() Qty = ureg.Quantity # 批量创建Measurement数组 nominal_values = [3, 4, 5] std_devs = [0.1, 0.2, 0.15] units = 'um' measurements = [Qty(n, units).plus_minus(s) for n, s in zip(nominal_values, std_devs)] # 转为Pint数组(如果需要numpy支持) meas_array = ureg.Quantity(measurements)
如果需要结合pandas,可以直接将这些Measurement对象存入DataFrame列:
import pandas as pd df = pd.DataFrame({'wavelength': measurements}) # 单位转换示例 df['wavelength_nm'] = df['wavelength'].to('nm')
4. 第三方整合库(简化操作)
可以使用pint-pandas扩展来让Pint Quantity更好地兼容Pandas,同时结合自定义不确定度处理逻辑,或者使用社区维护的uncertainties-pint整合库来简化两者的交互。
内容的提问来源于stack exchange,提问作者Selewirre

