Beispiel #1
0
    def test_with_dof_2(self):
        """
        Same test as above, but now ensure that the 
        ensemble calculation can deal with influences 
        that are not in sequence
        """
        TOL = 1E-10

        # The dummy variables mean that those included
        # in the ensemble will not be a consecutive
        # series of IDs, provided we include the
        # dummies in the equations, as is done below
        # with zero weight.
        v = ureal(4.999, 0.0032, 5, independent=False)
        dummy_1 = ureal(1, 1)
        i = ureal(0.019661, 0.0000095, 5, independent=False)
        dummy_2 = ureal(1, 1)
        phi = ureal(1.04446, 0.00075, 5, independent=False)
        dummy_3 = ureal(1, 1)

        real_ensemble([v, i, phi], 5)

        set_correlation(-0.36, v, i)
        set_correlation(0.86, v, phi)
        set_correlation(-0.65, i, phi)

        r = v * cos(phi) / i + (0 * dummy_1)
        x = v * sin(phi) / i + (0 * dummy_2)
        z = v / i + (0 * dummy_3)

        # The presence of finite DoF should not alter previous results
        equivalent(uncertainty(r), 0.0699787279884, TOL)
        equivalent(uncertainty(x), 0.295716826846, TOL)
        equivalent(uncertainty(z), 0.236602971835, TOL)

        equivalent(math.sqrt(welch_satterthwaite(r)[0]), uncertainty(r), TOL)
        equivalent(math.sqrt(welch_satterthwaite(x)[0]), uncertainty(x), TOL)
        equivalent(math.sqrt(welch_satterthwaite(z)[0]), uncertainty(z), TOL)

        equivalent(get_correlation(r, x), -0.591484610819, TOL)
        equivalent(get_correlation(x, z), 0.992797472722, TOL)
        equivalent(get_correlation(r, z), -0.490623905441, TOL)

        equivalent(dof(r), 5, TOL)
        equivalent(dof(x), 5, TOL)
        equivalent(dof(z), 5, TOL)
Beispiel #2
0
def line_fit_wtls(x, y, u_x, u_y, a0_b0=None, r_xy=None, label=None):
    """Return a total least-squares straight-line fit 
    
    .. versionadded:: 1.2

    :arg x:     sequence of independent-variable data
    :arg y:     sequence of dependent-variable data 
    :arg u_x:   sequence of uncertainties in ``x``
    :arg u_y:   sequence of uncertainties in ``y``
    :arg a0_b0: a pair of initial estimates for the intercept and slope
    :arg r_xy:  correlation between x-y pairs
    :arg label: suffix labeling the uncertain numbers `a` and `b`

    :returns:   an object containing the fitting results
    :rtype:     :class:`.LineFitWTLS`

    The optional argument ``a_b`` can be used to provide a pair 
    of initial estimates for the intercept and slope. 

    Based on paper by M Krystek and M Anton,
    *Meas. Sci. Technol.* **22** (2011) 035101 (9pp)
    
    **Example**::

        # Pearson-York test data see, e.g., 
        # Lybanon, M. in Am. J. Phys 52 (1) 1984 
        >>> x=[0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4]
        >>> wx=[1000.0,1000.0,500.0,800.0,200.0,80.0,60.0,20.0,1.8,1.0]

        >>> y=[5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5]
        >>> wy=[1.0,1.8,4.0,8.0,20.0,20.0,70.0,70.0,100.0,500.0]

        # standard uncertainties required for weighting
        >>> ux=[1./math.sqrt(wx_i) for wx_i in wx ]
        >>> uy=[1./math.sqrt(wy_i) for wy_i in wy ]

        >>> result = ta.line_fit_wtls(x,y,ux,uy)
        >>> intercept, slope = result.a_b
        >>> intercept
        ureal(5.47991018...,0.29193349...,8)
        >>> slope
        ureal(-0.48053339...,0.057616740...,8)
    
    """
    N = len(x)
    df = N - 2
    if df <= 0 or N != len(y):
        raise RuntimeError("Invalid sequences: len({!r}), len({!r})".format(
            x, y))
    if N != len(u_x) or N != len(u_y):
        raise RuntimeError("Invalid sequences: len({!r}), len({!r})".format(
            u_x, u_y))

    independent = r_xy is None

    x_u = [
        ureal(value(x_i), u_i, inf, None, independent=independent)
        for x_i, u_i in izip(x, u_x)
    ]
    y_u = [
        ureal(value(y_i), u_i, inf, None, independent=independent)
        for y_i, u_i in izip(y, u_y)
    ]
    if not independent:
        for x_i, y_i, r_i in izip(x_u, y_u, r_xy):
            x_i.set_correlation(r_i, y_i)

    result = type_b.line_fit_wtls(x_u, y_u, a_b=a0_b0)

    a, b = result.a_b
    N = result.N
    ssr = result.ssr
    r_ab = a.get_correlation(b)

    a = ureal(a.x,
              a.u,
              df,
              label='a_{}'.format(label) if label is not None else None,
              independent=False)
    b = ureal(b.x,
              b.u,
              df,
              label='b_{}'.format(label) if label is not None else None,
              independent=False)

    real_ensemble((a, b), df)
    a.set_correlation(r_ab, b)

    return LineFitWTLS(a, b, ssr, N)
Beispiel #3
0
def line_fit_rwls(x, y, s_y, label=None):
    """Return a relative weighted least-squares straight-line fit
    
    .. versionadded:: 1.2
    
    The ``s_y`` values are used to scale variability in the ``y`` data.
    It is assumed that the standard deviation of each ``y`` value is 
    proportional to the corresponding ``s_y`` scale factor.
    The unknown common factor in the uncertainties is estimated from the residuals.
    
    :arg x:     sequence of stimulus data (independent-variable)  
    :arg y:     sequence of response data (dependent-variable)  
    :arg s_y:   sequence of scale factors
    :arg label: suffix to label the uncertain numbers `a` and `b`

    :returns:   an object containing regression results
    :rtype:     :class:`.LineFitRWLS`

    **Example**::

        >>> x = [1,2,3,4,5,6]
        >>> y = [3.014,5.225,7.004,9.061,11.201,12.762]
        >>> s_y = [0.2,0.2,0.2,0.4,0.4,0.4]
        >>> fit = type_a.line_fit_rwls(x,y,s_y)
        >>> a, b = fit.a_b
        >>>
        >>> print(fit)
        <BLANKLINE>
        Relative Weighted Least-Squares Results:
        <BLANKLINE>
          Intercept: 1.14(12)
          Slope: 1.973(41)
          Correlation: -0.87
          Sum of the squared residuals: 1.3395217958...
          Number of points: 6
        <BLANKLINE>
          
    """
    N = len(x)
    df = N - 2
    if df <= 0 or N != len(y) or N != len(s_y):
        raise RuntimeError(
            "Invalid sequences: len({!r}), len({!r}), len({!r})".format(
                x, y, s_y))

    x = value_seq(x)
    y = value_seq(y)

    a_, b_, siga, sigb, r_ab, ssr, N = _line_fit_wls(x, y, s_y)

    sigma_hat = math.sqrt(ssr / df)
    siga *= sigma_hat
    sigb *= sigma_hat

    a = ureal(a_,
              siga,
              df,
              label='a_{}'.format(label) if label is not None else None,
              independent=False)
    b = ureal(b_,
              sigb,
              df,
              label='b_{}'.format(label) if label is not None else None,
              independent=False)

    real_ensemble((a, b), df)
    a.set_correlation(r_ab, b)

    return LineFitRWLS(a, b, ssr, N)
Beispiel #4
0
def line_fit(x, y, label=None):
    """Return a least-squares straight-line fit to the data
     
    .. versionadded:: 1.2
    
    :arg x:     sequence of stimulus data (independent-variable)  
    :arg y:     sequence of response data (dependent-variable)  
    :arg label: suffix to label the uncertain numbers `a` and `b`

    :returns:   an object containing regression results
    :rtype:     :class:`.LineFitOLS`

    Performs an ordinary least-squares regression of ``y`` to ``x``.
        
    **Example**::
    
        >>> x = [1,2,3,4,5,6,7,8,9]
        >>> y = [15.6,17.5,36.6,43.8,58.2,61.6,64.2,70.4,98.8]
        >>> result = type_a.line_fit(x,y)
        >>> a,b = result.a_b
        >>> a
        ureal(4.8138888888888...,4.8862063121833...,7)
        >>> b
        ureal(9.4083333333333...,0.8683016476563...,7)

        >>> y_p = a + b*5.5
        >>> dof(y_p)
        7.0
            
    """
    N = len(x)
    df = N - 2
    if df <= 0 or N != len(y):
        raise RuntimeError("Invalid sequences: len({!r}), len({!r})".format(
            x, y))

    x = value_seq(x)
    y = value_seq(y)

    S_x = math.fsum(x)
    S_y = math.fsum(y)

    k = S_x / N
    t = [(x_i - k) for x_i in x]

    S_tt = math.fsum(t_i * t_i for t_i in t)

    b_ = math.fsum(t_i * y_i / S_tt for t_i, y_i in izip(t, y))
    a_ = (S_y - b_ * S_x) / N

    siga = math.sqrt((1.0 + S_x * S_x / (N * S_tt)) / N)
    sigb = math.sqrt(1.0 / S_tt)
    r_ab = -S_x / (N * S_tt * siga * sigb)

    # Sum of squared residuals needed to correctly calculate parameter uncertainties
    f = lambda x_i, y_i: (y_i - a_ - b_ * x_i)**2
    ssr = math.fsum(f(x_i, y_i) for x_i, y_i in izip(x, y))

    data_u = math.sqrt(ssr / df)
    siga *= data_u
    sigb *= data_u

    a = ureal(a_,
              siga,
              df=df,
              label='a_{}'.format(label) if label is not None else None,
              independent=False)
    b = ureal(b_,
              sigb,
              df=df,
              label='b_{}'.format(label) if label is not None else None,
              independent=False)

    real_ensemble((a, b), df)
    a.set_correlation(r_ab, b)

    return LineFitOLS(a, b, ssr, N)
Beispiel #5
0
def multi_estimate_real(seq_of_seq, labels=None):
    """Return a sequence of uncertain real numbers 

    :arg seq_of_seq: a sequence of sequences of data
    :arg labels: a sequence of `str` labels 
    
    :rtype: seq of :class:`~lib.UncertainReal`

    The sequences in ``seq_of_seq`` must all be the same length.
    Each sequence contains 
    a sample of data associated with a particular quantity. 
    An uncertain number will be created for the quantity
    from sample statistics. The covariance 
    between the different quantities will also be evaluated.
    
    A sequence of elementary uncertain numbers is returned. These uncertain numbers 
    are considered to be related, allowing a degrees-of-freedom calculations 
    to be performed on derived quantities. 

    **Example**::
    
        # From Appendix H2 in the GUM
        
        >>> V = [5.007,4.994,5.005,4.990,4.999]
        >>> I = [19.663E-3,19.639E-3,19.640E-3,19.685E-3,19.678E-3]
        >>> phi = [1.0456,1.0438,1.0468,1.0428,1.0433]
        >>> v,i,p = type_a.multi_estimate_real((V,I,phi),labels=('V','I','phi'))
        >>> v
        ureal(4.999000...,0.0032093613071761...,4, label='V')
        >>> i
        ureal(0.019661,9.471008394041335...e-06,4, label='I')
        >>> p
        ureal(1.044460...,0.0007520638270785...,4, label='phi')
        
        >>> r = v/i*cos(p)
        >>> r
        ureal(127.732169928102...,0.071071407396995...,4.0)
        
    """
    M = len(seq_of_seq)
    N = len(seq_of_seq[0])

    if labels is not None and len(labels) != M:
        raise RuntimeError("Incorrect number of labels: '{!r}'".format(labels))

    # Calculate the deviations from the mean for each sequence
    means = []
    dev = []
    for i, seq_i in enumerate(seq_of_seq):
        if len(seq_i) != N:
            raise RuntimeError("{:d}th sequence length inconsistent".format(i))

        mu_i = value(sum(seq_i) / N)
        means.append(mu_i)
        dev.append(tuple(value(x_j) - mu_i for x_j in seq_i))

    # calculate the covariance matrix
    N_N_1 = N * (N - 1)
    u = []
    cv = []  # M elements of len M-1, M-2, ...
    for i, seq_i in enumerate(dev):
        u_i = math.sqrt(math.fsum(d_i**2 for d_i in seq_i) / N_N_1)
        u.append(u_i)
        cv.append([])
        for seq_j in dev[i + 1:]:
            cv[i].append(
                math.fsum(d_i * d_j
                          for d_i, d_j in izip(seq_i, seq_j)) / N_N_1)

    # Create a list of elementary uncertain numbers
    # to return a list of standard uncertainties
    # to normalise the CV matrix.
    df = N - 1
    rtn = []
    for i in xrange(M):
        mu_i = means[i]
        u_i = u[i]
        l_i = labels[i] if labels is not None else ""
        rtn.append(ureal(mu_i, u_i, df, l_i, independent=False))

    # Create the list of ensemble id's,
    # assign it to the register in the context,
    # set the correlation between nodes
    real_ensemble(rtn, df)

    for i in xrange(M):
        u_i = u[i]
        un_i = rtn[i]

        for j in xrange(M - 1 - i):
            cv_ij = cv[i][j]
            if cv_ij != 0.0:
                r = cv_ij / (u_i * u[i + j + 1])
                un_j = rtn[i + j + 1]
                set_correlation_real(un_i, un_j, r)

    return rtn
Beispiel #6
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