Exemple #1
0
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
Exemple #2
0
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)
        )
Exemple #3
0
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])
Exemple #4
0
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
Exemple #5
0
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))
Exemple #6
0
    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)
Exemple #7
0
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))
Exemple #8
0
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)
Exemple #9
0
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
Exemple #10
0
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))
Exemple #11
0
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