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)
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)
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)
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)
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
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