Source code for pint.measurement

"""
    pint.measurement
    ~~~~~~~~~~~~~~~~

    :copyright: 2016 by Pint Authors, see AUTHORS for more details.
    :license: BSD, see LICENSE for more details.
"""
import re

from .compat import ufloat
from .formatting import _FORMATS, siunitx_format_unit
from .quantity import Quantity

MISSING = object()


[docs]class Measurement(Quantity): """Implements a class to describe a quantity with uncertainty. Parameters ---------- value : pint.Quantity or any numeric type The expected value of the measurement error : pint.Quantity or any numeric type The error or uncertainty of the measurement Returns ------- """ def __new__(cls, value, error, units=MISSING): if units is MISSING: try: value, units = value.magnitude, value.units except AttributeError: # if called with two arguments and the first looks like a ufloat # then assume the second argument is the units, keep value intact if hasattr(value, "nominal_value"): units = error error = MISSING # used for check below else: units = "" try: error = error.to(units).magnitude except AttributeError: pass if error is MISSING: mag = value elif error < 0: raise ValueError( "The magnitude of the error cannot be negative".format(value, error) ) else: mag = ufloat(value, error) inst = super().__new__(cls, mag, units) return inst @property def value(self): return self._REGISTRY.Quantity(self.magnitude.nominal_value, self.units) @property def error(self): return self._REGISTRY.Quantity(self.magnitude.std_dev, self.units) @property def rel(self): return float(abs(self.magnitude.std_dev / self.magnitude.nominal_value)) def __reduce__(self): # See notes in Quantity.__reduce__ from . import _unpickle return _unpickle, (Measurement, self.magnitude, self._units) def __repr__(self): return "<Measurement({}, {}, {})>".format( self.magnitude.nominal_value, self.magnitude.std_dev, self.units ) def __str__(self): return "{}".format(self) def __format__(self, spec): # special cases if "Lx" in spec: # the LaTeX siunitx code # the uncertainties module supports formatting # numbers in value(unc) notation (i.e. 1.23(45) instead of 1.23 +/- 0.45), # which siunitx actually accepts as input. we just need to give the 'S' # formatting option for the uncertainties module. spec = spec.replace("Lx", "S") # todo: add support for extracting options opts = "separate-uncertainty=true" mstr = format(self.magnitude, spec) ustr = siunitx_format_unit(self.units) return r"\SI[%s]{%s}{%s}" % (opts, mstr, ustr) # standard cases if "L" in spec: newpm = pm = r" \pm " pars = _FORMATS["L"]["parentheses_fmt"] elif "P" in spec: newpm = pm = "±" pars = _FORMATS["P"]["parentheses_fmt"] else: newpm = pm = "+/-" pars = _FORMATS[""]["parentheses_fmt"] if "C" in spec: sp = "" newspec = spec.replace("C", "") pars = _FORMATS["C"]["parentheses_fmt"] else: sp = " " newspec = spec if "H" in spec: newpm = "&plusmn;" newspec = spec.replace("H", "") pars = _FORMATS["H"]["parentheses_fmt"] mag = format(self.magnitude, newspec).replace(pm, sp + newpm + sp) if "(" in mag: # Exponential format has its own parentheses pars = "{}" if "L" in newspec and "S" in newspec: mag = mag.replace("(", r"\left(").replace(")", r"\right)") if "L" in newspec or "H" in spec: space = r"\ " else: space = " " ustr = format(self.units, spec) if not ("uS" in newspec or "ue" in newspec or "u%" in newspec): mag = pars.format(mag) if "H" in spec: # Fix exponential format mag = re.sub(r"\)e\+0?(\d+)", r")×10^{\1}", mag) mag = re.sub(r"\)e-0?(\d+)", r")×10^{-\1}", mag) assert ustr[:2] == r"\[" assert ustr[-2:] == r"\]" return r"\[" + mag + space + ustr[2:] else: return mag + space + ustr
_Measurement = Measurement def build_measurement_class(registry): if ufloat is None: class Measurement: _REGISTRY = registry def __init__(self, *args): raise RuntimeError( "Pint requires the 'uncertainties' package to create a Measurement object." ) else: class Measurement(_Measurement): _REGISTRY = registry return Measurement