summaryrefslogtreecommitdiff
path: root/pint/facets/measurement/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'pint/facets/measurement/__init__.py')
-rw-r--r--pint/facets/measurement/__init__.py219
1 files changed, 11 insertions, 208 deletions
diff --git a/pint/facets/measurement/__init__.py b/pint/facets/measurement/__init__.py
index f43598b..83454dc 100644
--- a/pint/facets/measurement/__init__.py
+++ b/pint/facets/measurement/__init__.py
@@ -1,213 +1,16 @@
-from __future__ import annotations
-
-import copy
-import re
-
-from ...compat import ufloat
-from ...formatting import _FORMATS, extract_custom_flags, siunitx_format_unit
-from ...util import build_dependent_class, create_class_with_registry
-from ..plain import PlainQuantity
-
-MISSING = object()
-
-
-class MeasurementQuantity:
-
- # Measurement support
- def plus_minus(self, error, relative=False):
- if isinstance(error, self.__class__):
- if relative:
- raise ValueError("{} is not a valid relative error.".format(error))
- error = error.to(self._units).magnitude
- else:
- if relative:
- error = error * abs(self.magnitude)
-
- return self._REGISTRY.Measurement(copy.copy(self.magnitude), error, self._units)
-
-
-class Measurement(PlainQuantity):
- """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")
- 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 abs(self.magnitude.std_dev / self.magnitude.nominal_value)
-
- def __reduce__(self):
- # See notes in Quantity.__reduce__
- from pint import _unpickle_measurement
-
- return _unpickle_measurement, (Measurement, self.magnitude, self._units)
+"""
+ pint.facets.measurement
+ ~~~~~~~~~~~~~~~~~~~~~~~
- def __repr__(self):
- return "<Measurement({}, {}, {})>".format(
- self.magnitude.nominal_value, self.magnitude.std_dev, self.units
- )
+ Adds pint the capability to handle measurements (quantities with uncertainties).
- def __str__(self):
- return "{}".format(self)
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
- def __format__(self, spec):
-
- spec = spec or self.default_format
-
- # 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),
- # using type code 'S', which siunitx actually accepts as input.
- # However, the implementation is incompatible with siunitx.
- # Uncertainties will do 9.1(1.1), which is invalid, should be 9.1(11).
- # TODO: add support for extracting options
- #
- # Get rid of this code, we'll deal with it here
- spec = spec.replace("Lx", "")
- # The most compatible format from uncertainties is the default format,
- # but even this requires fixups.
- # For one, SIUnitx does not except some formats that unc does, like 'P',
- # and 'S' is broken as stated, so...
- spec = spec.replace("S", "").replace("P", "")
- # get SIunitx options
- # TODO: allow user to set this value, somehow
- opts = _FORMATS["Lx"]["siopts"]
- if opts != "":
- opts = r"[" + opts + r"]"
- # SI requires space between "+-" (or "\pm") and the nominal value
- # and uncertainty, and doesn't accept "+/-", so this setting
- # selects the desired replacement.
- pm_fmt = _FORMATS["Lx"]["pm_fmt"]
- mstr = format(self.magnitude, spec).replace(r"+/-", pm_fmt)
- # Also, SIunitx doesn't accept parentheses, which uncs uses with
- # scientific notation ('e' or 'E' and sometimes 'g' or 'G').
- mstr = mstr.replace("(", "").replace(")", " ")
- ustr = siunitx_format_unit(self.units._units, self._REGISTRY)
- 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:
- space = r"\ "
- else:
- space = " "
-
- uspec = extract_custom_flags(spec)
- ustr = format(self.units, uspec)
- 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<sup>\1</sup>", mag)
- mag = re.sub(r"\)e-0?(\d+)", r")×10<sup>-\1</sup>", mag)
-
- return mag + space + ustr
-
-
-# TODO: Remove in the near future
-# This is kept for easy backward compatibility during refactoring.
-_Measurement = Measurement
-
-
-class MeasurementRegistry:
-
- _quantity_class = MeasurementQuantity
- _measurement_class = Measurement
-
- def __init_subclass__(cls, **kwargs):
- super().__init_subclass__()
-
- cls.Measurement = build_dependent_class(
- cls, "Measurement", "_measurement_class"
- )
-
- def _init_dynamic_classes(self) -> None:
- """Generate subclasses on the fly and attach them to self"""
- super()._init_dynamic_classes()
-
- if ufloat is not None:
- self.Measurement = create_class_with_registry(self, self.Measurement)
- else:
+from __future__ import annotations
- def no_uncertainties(*args, **kwargs):
- raise RuntimeError(
- "Pint requires the 'uncertainties' package to create a Measurement object."
- )
+from .objects import Measurement, MeasurementQuantity
+from .registry import MeasurementRegistry
- self.Measurement = no_uncertainties
+__all__ = [Measurement, MeasurementQuantity, MeasurementRegistry]