def mean(seq, *args, **kwargs): """Return the arithmetic mean of data in ``seq`` :arg seq: a sequence, :class:`~numpy.ndarray`, or iterable, of numbers or uncertain numbers :arg args: optional arguments when ``seq`` is an :class:`~numpy.ndarray` :arg kwargs: optional keyword arguments when ``seq`` is an :class:`~numpy.ndarray` An uncertain number is returned if ``seq`` contains uncertain numbers. """ if is_sequence(seq): assert not args assert not kwargs mu = sum(seq) / len(seq) elif isinstance(seq, np.ndarray): mu = np.asarray(seq).mean(*args, **kwargs) elif isinstance(seq, Iterable): assert not args assert not kwargs count = 0 total = 0 for i in seq: total += i count += 1 mu = total / count else: raise RuntimeError("{!r} is not iterable".format(seq)) # If `seq` has uncertain number elements then `mu` will # be an uncertain number. return mu
def mean(seq,*args,**kwargs): """Return the arithmetic mean of the elements in `seq` :arg seq: a sequence, :class:`~numpy.ndarray`, or iterable, of numbers or uncertain numbers :arg args: optional arguments when ``seq`` is an :class:`~numpy.ndarray` :arg kwargs: optional keyword arguments when ``seq`` is an :class:`~numpy.ndarray` If the elements of ``seq`` are uncertain numbers, an uncertain number is returned. **Example** :: >>> seq = [ ureal(1,1), ureal(2,1), ureal(3,1) ] >>> function.mean(seq) ureal(2.0,0.5773502691896257,inf) """ if is_sequence(seq): return sum(seq)/len(seq) elif isinstance(seq,np.ndarray): return np.asarray(seq).mean(*args, **kwargs) elif isinstance(seq,Iterable): count = 0 total = 0 for i in seq: total += i count += 1 return total/count else: raise RuntimeError( "{!r} is not iterable".format(seq) )
def seq_to_complex(seq): """Transform a 4-element sequence into a complex number :arg seq: a 4-element sequence :raises RuntimeError: if ``seq`` is ill-conditioned The legitimate form of elements in ``seq`` is ``[x, -y, y, x]``, where ``x`` is the real component and ``y`` is the imaginary component of a complex number. See also :func:`complex_to_seq` **Examples**:: >>> import numpy >>> seq = (1,-2,2,1) >>> z = function.seq_to_complex( seq ) >>> z (1+2j) >>> a = numpy.array((1,-2,2,1)) >>> a.shape = 2,2 >>> a array([[ 1, -2], [ 2, 1]]) >>> z = function.seq_to_complex(a) >>> z (1+2j) """ if hasattr(seq, 'shape'): if seq.shape != (2, 2): raise RuntimeError("array shape illegal: {}".format(seq)) elif (math.fabs(seq[0, 0] - seq[1, 1]) > EPSILON or math.fabs(seq[1, 0] + seq[0, 1]) > EPSILON): raise RuntimeError("ill-conditioned sequence: {}".format(seq)) else: seq = list(seq.flat) elif is_sequence(seq): if len(seq) != 4: raise RuntimeError("sequence must have 4 elements: {}".format(seq)) elif (math.fabs(seq[0] - seq[3]) > EPSILON or math.fabs(seq[1] + seq[2]) > EPSILON): raise RuntimeError("ill-conditioned sequence: {}".format(seq)) else: raise RuntimeError("illegal argument: {}".format(seq)) return complex(seq[0], seq[2])
def mean(seq, *args, **kwargs): """Return the arithmetic mean of data in ``seq`` :arg seq: a sequence, :class:`~numpy.ndarray`, or iterable, of numbers or uncertain numbers :arg args: optional arguments when ``seq`` is an :class:`~numpy.ndarray` :arg kwargs: optional keyword arguments when ``seq`` is an :class:`~numpy.ndarray` An uncertain number is returned if ``seq`` contains uncertain numbers. **Example** :: >>> seq = [ ureal(1,1), ureal(2,1), ureal(3,1) ] >>> function.mean(seq) ureal(2.0,0.5773502691896257,inf) .. note:: When ``seq`` is an empty :class:`~numpy.ndarray` or a :class:`~numpy.ndarray` containing any ``NaN`` elements ``NaN`` is returned. In other cases, a :class:`ZeroDivisionError` is raised when there are no elements in ``seq``. """ if is_sequence(seq): assert not args assert not kwargs mu = sum(seq) / len(seq) elif isinstance(seq, np.ndarray): mu = np.asarray(seq).mean(*args, **kwargs) elif isinstance(seq, Iterable): assert not args assert not kwargs count = 0 total = 0 for i in seq: total += i count += 1 mu = total / count else: raise RuntimeError("{!r} is not iterable".format(seq)) # If `seq` has uncertain number elements then `mu` will be an uncertain number. return mu
def sum(seq, *args, **kwargs): """Return the sum of elements in `seq` :arg seq: a sequence, :class:`~numpy.ndarray`, or iterable, of numbers or uncertain numbers :arg args: optional arguments when ``seq`` is an :class:`~numpy.ndarray` :arg kwargs: optional keyword arguments when ``seq`` is an :class:`~numpy.ndarray` .. versionadded:: 1.1 """ if isinstance(seq, np.ndarray): return np.asarray(seq).sum(*args, **kwargs) elif is_sequence(seq) or isinstance(seq, Iterable): return builtins.sum(seq) else: raise RuntimeError("{!r} is not iterable".format(seq))
def _intermediate(self, labels): # Default second argument of calling function is `None` if labels is None: arr, itemset, iterator = self._create_empty() for i, x in enumerate(iterator): itemset(i, result(x)) else: # `_create_empty()` handles only ndarray-like sequences if not is_sequence(labels): # Add index notation to the label base labels = [ "{}[{}]".format(labels, i) for i in xrange(self.size) ] labels = np.asarray(labels) arr, itemset, iterator = self._create_empty((self, labels)) for i, (x, lbl) in enumerate(iterator): itemset(i, result(x, lbl)) return UncertainArray(arr)
def u_bar(ucpt): """Return the magnitude of a component of uncertainty :arg ucpt: a component of uncertainty :type ucpt: float or 4-element sequence of float If ``ucpt`` is a sequence, return the root-sum-square of the elements divided by :math:`\sqrt{2}` If ``ucpt`` is a number, return the absolute value. **Example**:: >>> x1 = 1-.5j >>> x2 = .2+7.1j >>> z1 = ucomplex(x1,1) >>> z2 = ucomplex(x2,1) >>> y = z1 * z2 >>> dy_dz1 = reporting.u_component(y,z1) >>> dy_dz1 ComponentOfUncertainty(rr=0.2, ri=-7.1, ir=7.1, ii=0.2) >>> reporting.u_bar(dy_dz1) 7.102816342831905 """ if is_sequence(ucpt): if len(ucpt) != 4: raise RuntimeError( "need a 4-element sequence, got: {!r}".format(ucpt)) return math.sqrt(reduce(lambda y, x: y + x * x, ucpt, 0) / 2) elif isinstance(ucpt, numbers.Real): return abs(ucpt) else: raise RuntimeError( "need a 4-element sequence or float, got: {!r}".format(ucpt))
def uarray(array, label=None, names=None): """Create an array of uncertain numbers. For an overview on how to use an :class:`.UncertainArray` see :ref:`numpy-uarray`. .. versionadded:: 1.1 .. attention:: Requires numpy :math:`\geq` v1.13.0 to be installed. :param array: An array-like object containing :class:`int`, :class:`float`, :class:`complex` :class:`~lib.UncertainReal` or :class:`~lib.UncertainComplex` elements. :param label: A label to assign to the `array`. This `label` does not change labels previously assigned to array elements. :type label: str :param names: The field `names` to use to create a :ref:`structured array <structured_arrays>`. :type names: list[str] :return: An :class:`.UncertainArray`. **Examples**: Create an `amps` and a `volts` array and then calculate the `resistances` >>> amps = la.uarray([ureal(0.57, 0.18), ureal(0.45, 0.12), ureal(0.68, 0.19)]) >>> volts = la.uarray([ureal(10.3, 1.3), ureal(9.5, 0.8), ureal(12.6, 1.9)]) >>> resistances = volts / amps >>> resistances uarray([ureal(18.070175438596493,6.145264246839438,inf), ureal(21.11111111111111,5.903661880050747,inf), ureal(18.52941176470588,5.883187720636909,inf)]) Create a :ref:`Structured array <structured_arrays>`, with the names ``'amps'`` and ``'volts'``, and then calculate the `resistances`. >>> data = la.uarray([(ureal(0.57, 0.18), ureal(10.3, 1.3)), ... (ureal(0.45, 0.12), ureal(9.5, 0.8)), ... (ureal(0.68, 0.19), ureal(12.6, 1.9))], names=['amps', 'volts']) >>> resistances = data['volts'] / data['amps'] >>> resistances uarray([ureal(18.070175438596493,6.145264246839438,inf), ureal(21.11111111111111,5.903661880050747,inf), ureal(18.52941176470588,5.883187720636909,inf)]) """ if np.__version__ < '1.13.0': # the __array_ufunc__ method was not introduced until version 1.13.0 raise ValueError('creating an UncertainArray requires numpy >= 1.13.0') # don't allow a scalar UncertainArray if not (is_sequence(array) or (isinstance(array, np.ndarray) and array.ndim > 0)): raise ValueError('cannot create an UncertainArray from a scalar') dtype = None if names is not None: try: a_len = len(array[0]) values = array[0] except: try: a_len = len(array) values = array except: a_len = None values = None if a_len is None or not isinstance(values, tuple): raise TypeError('The elements in the uarray must be a tuple if specifying field names') if a_len != len(names): raise ValueError('len(array[0]) != len(names) -> {} != {}'.format(a_len, len(names))) dtype = [(name, type(val)) for name, val in izip(names, array[0])] return UncertainArray(array, dtype=dtype, label=label)
def multiple_ucomplex(x_seq, u_seq, df, label_seq=None): """Return a sequence of uncertain complex numbers :arg x_seq: a sequence of complex values :arg u_seq: a sequence of standard uncertainties or covariances :arg df: the degrees-of-freedom :arg label_seq: a sequence of labels for the uncertain numbers :rtype: a sequence of :class:`~lib.UncertainComplex` This function defines an set of uncertain complex numbers with the same number of degrees-of-freedom. Correlation between any pairs of these uncertain numbers will not invalidate degrees-of-freedom calculations. (see: R Willink, *Metrologia* 44 (2007) 340-349, Sec. 4.1) **Example**:: # GUM Appendix H2 >>> values = [4.999+0j,0.019661+0j,1.04446j] >>> uncert = [(0.0032,0.0),(0.0000095,0.0),(0.0,0.00075)] >>> v,i,phi = multiple_ucomplex(values,uncert,5) >>> set_correlation(-0.36,v.real,i.real) >>> set_correlation(0.86,v.real,phi.imag) >>> set_correlation(-0.65,i.real,phi.imag) >>> z = v * exp(phi)/ i >>> print(z) (127.732(70)+219.847(296)j) >>> z.r -28.5825760885182... """ if len(x_seq) != len(u_seq): raise RuntimeError("unequal length sequences: x={!r} u={!r}".format( x_seq, u_seq)) if label_seq is None: label_seq = [None] * len(x_seq) elif is_sequence(label_seq): if len(x_seq) != len(label_seq): raise RuntimeError( "unequal length sequences: x={!r} label_seq={!r}".format( x_seq, u_seq)) else: raise RuntimeError("invalid `label_seq`: {!r}".format(label_seq)) rtn = [ # When u_i == 0 constant objects are created ucomplex(x_i, u_i, df, label=l_i, independent=False) for x_i, u_i, l_i in izip(x_seq, u_seq, label_seq) ] # Only non-constant UNs can be collected in an ensemble lib.complex_ensemble( [un_i for un_i in rtn if not lib._is_uncertain_complex_constant(un_i)], df) # All uncertain numbers are returned, including the constants return rtn
def ucomplex(z, u, df=inf, label=None, independent=True): """ Create an elementary uncertain complex number :arg z: the value (estimate) :type z: complex :arg u: the standard uncertainty or variance :type u: float, 2-element or 4-element sequence :arg df: the degrees-of-freedom :type df: float :type label: str :rtype: :class:`~lib.UncertainComplex` :raises: :exc:`ValueError` if ``df`` or ``u`` have illegal values. ``u`` can be a float, a 2-element or 4-element sequence. If ``u`` is a float, the standard uncertainty in both the real and imaginary components is taken to be ``u``. If ``u`` is a 2-element sequence, the first element is taken to be the standard uncertainty in the real component and the second element is taken to be the standard uncertainty in the imaginary component. If ``u`` is a 4-element sequence, the sequence is interpreted as a variance-covariance matrix. **Examples**:: >>> uc = ucomplex(1+2j,(.5,.5),3,label='x') >>> uc ucomplex((1+2j), u=[0.5,0.5], r=0.0, df=3.0, label=x) >>> cv = (1.2,0.7,0.7,2.2) >>> uc = ucomplex(0.2-.5j, cv) >>> variance(uc) VarianceCovariance(rr=1.1999999999999997, ri=0.7, ir=0.7, ii=2.2) """ # Arguments to these math functions must be compatible with float # otherwise a TypeError is raised by Python if cmath.isnan(z) or cmath.isinf(z): raise ValueError("invalid: '{!r}'".format(z)) if df < 1 or math.isnan(df): raise ValueError("invalid dof: '{!r}'".format(df)) if is_sequence(u): case = len(u) if case == 2: u_r = float(u[0]) u_i = float(u[1]) r = None elif case == 4: u_r, cv1, cv2, u_i = u # nan != nan is True if math.isinf(cv1) or cv1 != cv2: raise ValueError( "covariance elements not equal: {!r} and {!r}".format( cv1, cv2)) u_r = math.sqrt(u_r) u_i = math.sqrt(u_i) r = cv1 / (u_r * u_i) if cv1 != 0 else None # Allow a little tolerance for numerical imprecision if r is not None and abs(r) > 1 + 1E-10: raise ValueError("invalid correlation: {!r}, cv={}".format( r, u)) if r is not None: # This overrides an initial assignment independent = False else: raise ValueError("invalid uncertainty sequence: '{!r}'".format(u))
def multiple_ureal(x_seq, u_seq, df, label_seq=None): """Return a sequence of related elementary uncertain real numbers :arg x_seq: a sequence of values (estimates) :arg u_seq: a sequence of standard uncertainties :arg df: the degrees-of-freedom :arg label_seq: a sequence of labels :rtype: a sequence of :class:`~lib.UncertainReal` Defines an set of uncertain real numbers with the same number of degrees-of-freedom. Correlation between any pairs of this set of uncertain numbers defined will not invalidate degrees-of-freedom calculations. (see: R Willink, *Metrologia* 44 (2007) 340-349, Sec. 4.1) **Example**:: # Example from GUM-H2 >>> x = [4.999,19.661E-3,1.04446] >>> u = [3.2E-3,9.5E-6,7.5E-4] >>> labels = ['V','I','phi'] >>> v,i,phi = multiple_ureal(x,u,4,labels) >>> set_correlation(-0.36,v,i) >>> set_correlation(0.86,v,phi) >>> set_correlation(-0.65,i,phi) >>> r = v/i*cos(phi) >>> r ureal(127.732169928102...,0.0699787279883717...,4.0) """ if len(x_seq) != len(u_seq): raise RuntimeError("unequal length sequences: x={!r} u={!r}".format( x_seq, u_seq)) if label_seq is None: label_seq = [None] * len(x_seq) elif is_sequence(label_seq): if len(x_seq) != len(label_seq): raise RuntimeError( "unequal length sequences: x={!r} label_seq={!r}".format( x_seq, u_seq)) else: raise RuntimeError("invalid `label_seq`: {!r}".format(label_seq)) rtn = [ # NB `ureal` creates constant objects when u == 0 ureal(x_i, u_i, df, label=l_i, independent=False) for x_i, u_i, l_i in izip(x_seq, u_seq, label_seq) ] # Only non-constant UNs can be collected in an ensemble lib.real_ensemble( [un_i for un_i in rtn if not lib._is_uncertain_real_constant(un_i)], df) # All uncertain numbers are returned, including the constants return rtn