def correlated_values(param_names, roofitresult): """ Return symbolic values from a `RooFitResult` taking into account covariance This is useful for numerically computing the uncertainties for expressions using correlated values arising from a fit. Parameters ---------- param_names: list of strings A list of parameters to extract from the result. The order of the names is the order of the return value. roofitresult : RooFitResult A RooFitResult from a fit. Returns ------- list of correlated values from the uncertainties package. Examples -------- .. sourcecode:: python # Fit a pdf to a histogram pdf = some_roofit_pdf_with_variables("f(x, a, b, c)") fitresult = pdf.fitTo(histogram, ROOT.RooFit.Save()) a, b, c = correlated_values(["a", "b", "c"], fitresult) # Arbitrary math expression according to what the `uncertainties` # package supports, automatically computes correct error propagation sum_value = a + b + c value, error = sum_value.nominal_value, sum_value.std_dev() """ pars = roofitresult.floatParsFinal() #pars.Print() pars = [pars[i] for i in range(pars.getSize())] parnames = [p.GetName() for p in pars] values = [(p.getVal(), p.getError()) for p in pars] #values = [as_ufloat(p) for p in pars] matrix = asrootpy(roofitresult.correlationMatrix()).to_numpy() uvalues = U.correlated_values_norm(values, matrix.tolist()) uvalues = dict((n, v) for n, v in zip(parnames, uvalues)) assert all( n in uvalues for n in parnames), ("name {0} isn't in parameter list {1}".format( n, parnames)) # Return a tuple in the order it was asked for return tuple(uvalues[n] for n in param_names)
def correlated_values(param_names, roofitresult): """ Return symbolic values from a `RooFitResult` taking into account covariance This is useful for numerically computing the uncertainties for expressions using correlated values arising from a fit. Parameters ---------- param_names: list of strings A list of parameters to extract from the result. The order of the names is the order of the return value. roofitresult : RooFitResult A RooFitResult from a fit. Returns ------- list of correlated values from the uncertainties package. Examples -------- .. sourcecode:: python # Fit a pdf to a histogram pdf = some_roofit_pdf_with_variables("f(x, a, b, c)") fitresult = pdf.fitTo(histogram, ROOT.RooFit.Save()) a, b, c = correlated_values(["a", "b", "c"], fitresult) # Arbitrary math expression according to what the `uncertainties` # package supports, automatically computes correct error propagation sum_value = a + b + c value, error = sum_value.nominal_value, sum_value.std_dev() """ pars = roofitresult.floatParsFinal() #pars.Print() pars = [pars[i] for i in range(pars.getSize())] parnames = [p.GetName() for p in pars] values = [(p.getVal(), p.getError()) for p in pars] #values = [as_ufloat(p) for p in pars] matrix = asrootpy(roofitresult.correlationMatrix()).to_numpy() uvalues = U.correlated_values_norm(values, matrix.tolist()) uvalues = dict((n, v) for n, v in zip(parnames, uvalues)) assert all(n in uvalues for n in parnames), ( "name {0} isn't in parameter list {1}".format(n, parnames)) # Return a tuple in the order it was asked for return tuple(uvalues[n] for n in param_names)
def correlated(input1, input2, operation="+", correlation=1.0): corr_matrix = np.array([[1.0, correlation], [correlation, 1.0]]) (output1, output2) = unc.correlated_values_norm([(input1.n, input1.s), (input2.n, input2.s)], corr_matrix) if operation == "+": return output1 + output2 elif operation == "-": return output1 - output2 elif operation == "*": return output1 * output2 elif operation == "/": return output1 / output2 if output2 > 0.0 else unc.ufloat(np.inf, np.inf) else: return unc.float(np.nan, np.nan)
def correlated(input1, input2, operation="+", correlation=1.0): corr_matrix = np.array([[1., correlation], [correlation, 1.]]) (output1, output2) = unc.correlated_values_norm([(input1.n, input1.s), (input2.n, input2.s)], corr_matrix) if operation == "+": return output1 + output2 elif operation == "-": return output1 - output2 elif operation == "*": return output1 * output2 elif operation == "/": return output1 / output2 if output2 > 0. else unc.ufloat( np.inf, np.inf) else: return unc.float(np.nan, np.nan)
def test_correlated_values_correlation_mat(): ''' Tests the input of correlated value. Test through their correlation matrix (instead of the covariance matrix). ''' x = ufloat((1, 0.1)) y = ufloat((2, 0.3)) z = -3 * x + y cov_mat = uncertainties.covariance_matrix([x, y, z]) std_devs = numpy.sqrt(numpy.array(cov_mat).diagonal()) corr_mat = cov_mat / std_devs / std_devs[numpy.newaxis].T # We make sure that the correlation matrix is indeed diagonal: assert (corr_mat - corr_mat.T).max() <= 1e-15 # We make sure that there are indeed ones on the diagonal: assert (corr_mat.diagonal() - 1).max() <= 1e-15 # We try to recover the correlated variables through the # correlation matrix (not through the covariance matrix): nominal_values = [v.nominal_value for v in (x, y, z)] std_devs = [v.std_dev() for v in (x, y, z)] x2, y2, z2 = uncertainties.correlated_values_norm( list(zip(nominal_values, std_devs)), corr_mat) # matrices_close() is used instead of _numbers_close() because # it compares uncertainties too: # Test of individual variables: assert matrices_close(numpy.array([x]), numpy.array([x2])) assert matrices_close(numpy.array([y]), numpy.array([y2])) assert matrices_close(numpy.array([z]), numpy.array([z2])) # Partial correlation test: assert matrices_close(numpy.array([0]), numpy.array([z2 - (-3 * x2 + y2)])) # Test of the full covariance matrix: assert matrices_close( numpy.array(cov_mat), numpy.array(uncertainties.covariance_matrix([x2, y2, z2])))
def test_correlated_values_correlation_mat(): ''' Tests the input of correlated value. Test through their correlation matrix (instead of the covariance matrix). ''' x = ufloat((1, 0.1)) y = ufloat((2, 0.3)) z = -3*x+y cov_mat = uncertainties.covariance_matrix([x, y, z]) std_devs = numpy.sqrt(numpy.array(cov_mat).diagonal()) corr_mat = cov_mat/std_devs/std_devs[numpy.newaxis].T # We make sure that the correlation matrix is indeed diagonal: assert (corr_mat-corr_mat.T).max() <= 1e-15 # We make sure that there are indeed ones on the diagonal: assert (corr_mat.diagonal()-1).max() <= 1e-15 # We try to recover the correlated variables through the # correlation matrix (not through the covariance matrix): nominal_values = [v.nominal_value for v in (x, y, z)] std_devs = [v.std_dev() for v in (x, y, z)] x2, y2, z2 = uncertainties.correlated_values_norm( list(zip(nominal_values, std_devs)), corr_mat) # matrices_close() is used instead of _numbers_close() because # it compares uncertainties too: # Test of individual variables: assert matrices_close(numpy.array([x]), numpy.array([x2])) assert matrices_close(numpy.array([y]), numpy.array([y2])) assert matrices_close(numpy.array([z]), numpy.array([z2])) # Partial correlation test: assert matrices_close(numpy.array([0]), numpy.array([z2-(-3*x2+y2)])) # Test of the full covariance matrix: assert matrices_close( numpy.array(cov_mat), numpy.array(uncertainties.covariance_matrix([x2, y2, z2])))
def geometric_elements_with_uncertainties(thiele_innes_parameters, thiele_innes_parameters_errors=None, correlation_matrix=None, post_process=False, return_angles_in_deg=True): """ Return geometrical orbit elements a, omega, OMEGA, i. If errors are not given they are assumed to be 0 and correlation matrix is set to identity. Complement to the pystrometry.geometric_elements function that allows to compute parameter uncertainties as well. Parameters ---------- thiele_innes_parameters : array Array of Thiele Innes parameters [A,B,F,G] in milli-arcsecond thiele_innes_parameters_errors : array, optional Array of the errors of the Thiele Innes parameters [A,B,F,G] in milli-arcsecond correlation_matrix : (4, 4) array, optional Correlation matrix for the Thiele Innes parameters [A,B,F,G] Returns ------- geometric_parameters : array Orbital elements [a_mas, omega_deg, OMEGA_deg, i_deg] geometric_parameters_errors : array Errors of the orbital elements [a_mas, omega_deg, OMEGA_deg, i_deg] """ # Checks on the errors and correlation matrix if (thiele_innes_parameters_errors is None) and (correlation_matrix is None): # Define errors to 0 and correlation matrix to identity thiele_innes_parameters_errors = [0, 0, 0, 0] correlation_matrix = np.identity(4) elif (thiele_innes_parameters_errors is not None) and (correlation_matrix is not None): # If both are given continue to the calculation pass else: # If either one of them is provided but not the other raise an error raise ValueError("thieles_innes_parameters_erros and correlation_matrix must be" \ "specified together.") # Define uncorrelated (value, uncertainty) pairs A_u = (thiele_innes_parameters[0], thiele_innes_parameters_errors[0]) B_u = (thiele_innes_parameters[1], thiele_innes_parameters_errors[1]) F_u = (thiele_innes_parameters[2], thiele_innes_parameters_errors[2]) G_u = (thiele_innes_parameters[3], thiele_innes_parameters_errors[3]) # Create correlated quantities A, B, F, G = correlated_values_norm([A_u, B_u, F_u, G_u], correlation_matrix) p = (A**2 + B**2 + G**2 + F**2) / 2. q = A * G - B * F a_mas = unp.sqrt(p + unp.sqrt(p**2 - q**2)) i_rad = unp.arccos(q / (a_mas**2.)) omega_rad = (unp.arctan2(B - F, A + G) + unp.arctan2(-B - F, A - G)) / 2. OMEGA_rad = (unp.arctan2(B - F, A + G) - unp.arctan2(-B - F, A - G)) / 2. if post_process: # convert angles to nominal ranges omega_rad, OMEGA_rad = pystrometry.adjust_omega_OMEGA( omega_rad, OMEGA_rad) if return_angles_in_deg: # Convert radians to degrees i_deg = i_rad * 180 / np.pi omega_deg = omega_rad * 180 / np.pi OMEGA_deg = OMEGA_rad * 180 / np.pi else: i_deg = i_rad omega_deg = omega_rad OMEGA_deg = OMEGA_rad # Extract nominal values and standard deviations geometric_parameters = np.array([ unp.nominal_values(a_mas), unp.nominal_values(omega_deg), unp.nominal_values(OMEGA_deg), unp.nominal_values(i_deg) ]) geometric_parameters_errors = np.array([ unp.std_devs(a_mas), unp.std_devs(omega_deg), unp.std_devs(OMEGA_deg), unp.std_devs(i_deg) ]) return geometric_parameters, geometric_parameters_errors
def calculate(self, dataframe, ibin): # >>> acceptance selection var0 = [True,]*len(dataframe) if self.delR: var0 = var0 & (dataframe['delR'] > self.delR) if self.delDxy: var0 = var0 & (dataframe['delDxy'] < self.delDxy) if self.delDz: var0 = var0 & (dataframe['delDz'] < self.delDz) if all(var0): df = dataframe.copy() else: df = dataframe.loc[var0].copy() # <<< acceptance selection # >>> columns for tnp efficiency df['pass_muon'] = df['muon_ID'] >= self.ID df['pass_antiMuon'] = df['antiMuon_ID'] >= self.ID if self.pfIso: df['pass_muon'] = df['pass_muon'] & (df['muon_pfIso'] < self.pfIso) df['pass_antiMuon'] = df['pass_antiMuon'] & (df['antiMuon_pfIso'] < self.pfIso) if self.tkIso: df['pass_muon'] = df['pass_muon'] & (df['muon_tkIso'] < self.tkIso) df['pass_antiMuon'] = df['pass_antiMuon'] & (df['antiMuon_tkIso'] < self.tkIso) df['pass_muon_antiMuon'] = df['pass_muon'] & df['pass_antiMuon'] nMinus = df['pass_muon'].sum() nPlus = df['pass_antiMuon'].sum() nPlusMinus = df['pass_muon_antiMuon'].sum() if self.HLT: df['pass_muon_HLT'] = df['pass_muon_antiMuon'] & df['muon_hlt_{0}'.format(self.HLT)] df['pass_antiMuon_HLT'] = df['pass_muon_antiMuon'] & df['antiMuon_hlt_{0}'.format(self.HLT)] df['pass_muon_antiMuon_HLT'] = df['pass_muon_HLT'] & df['pass_antiMuon_HLT'] nMinus_HLT = df['pass_muon_HLT'].sum() nPlus_HLT = df['pass_antiMuon_HLT'].sum() nPlusMinus_HLT = df['pass_muon_antiMuon_HLT'].sum() # <<< columns for tnp efficiency # >>> columns for true efficiency if self.HLT: df['reco'] = df['pass_muon_antiMuon'] & (df['pass_muon_HLT'] | df['pass_antiMuon_HLT']) else: df['reco'] = df['pass_muon_antiMuon'] df['not_reco'] = ~df['reco'] nZReco = df['reco'].sum() nZNotReco = df['not_reco'].sum() # <<< columns for true efficiency # >>> construct full covariance matrix if self.HLT: corr_matrix = df[['reco','not_reco','pass_muon_antiMuon','pass_muon','pass_antiMuon','pass_muon_antiMuon_HLT','pass_muon_HLT','pass_antiMuon_HLT']].corr() nZReco, nZNotReco, nPlusMinus, nMinus, nPlus, nPlusMinus_HLT, nMinus_HLT, nPlus_HLT = unc.correlated_values_norm( [(nZReco, np.sqrt(nZReco)), (nZNotReco, np.sqrt(nZNotReco)), (nPlusMinus, np.sqrt(nPlusMinus)), (nMinus, np.sqrt(nMinus)), (nPlus, np.sqrt(nPlus)), (nPlusMinus_HLT, np.sqrt(nPlusMinus_HLT)), (nMinus_HLT, np.sqrt(nMinus_HLT)), (nPlus_HLT, np.sqrt(nPlus_HLT))], corr_matrix) else: corr_matrix = df[['reco','not_reco','pass_muon_antiMuon','pass_muon','pass_antiMuon']].corr() nZReco, nZNotReco, nPlusMinus, nMinus, nPlus = unc.correlated_values_norm( [(nZReco, np.sqrt(nZReco)), (nZNotReco, np.sqrt(nZNotReco)), (nPlusMinus, np.sqrt(nPlusMinus)), (nMinus, np.sqrt(nMinus)), (nPlus, np.sqrt(nPlus))], corr_matrix) # <<< construct full covariance matrix # >>> compute true and tnp efficiency eff_true = nZReco / (nZNotReco + nZReco) MuPlusEff = nPlusMinus/nMinus MuMinusEff = nPlusMinus/nPlus eff_tnp = MuPlusEff * MuMinusEff if self.HLT: MuPlusEff_HLT = nPlusMinus_HLT/nMinus_HLT MuMinusEff_HLT = nPlusMinus_HLT/nPlus_HLT eff_tnpZ = (1 - (1 - MuPlusEff_HLT) * (1 - MuMinusEff_HLT) ) * eff_tnp # <<< compute true and tnp efficiency else: eff_tnpZ = eff_tnp # >>> store self.eff_corr.append(unc.covariance_matrix([eff_tnpZ, eff_true])) self.eff_tnp.append(eff_tnp) self.eff_tnpZ.append(eff_tnpZ) self.eff_true.append(eff_true)
import numpy as np import ROOT import matplotlib.pyplot as plt import uncertainties as unc lPHYS = np.array([36.330, 41.480, 59.830]) lPHYS_Unc = np.array([0.424, 0.962, 1.501]) cPHYS = np.array([[1.00, 0.20, 0.41], [0.20, 1.00, 0.34], [0.41, 0.34, 1.00]]) uPHYS = unc.correlated_values_norm(zip(lPHYS, lPHYS_Unc), cPHYS) # --- from combination of high pileup Z counts hasZ = [0, 1, 1, 0] lZ = np.array([41.480, 59.830]) lZ_Unc = np.array([0.637, 1.486]) cZ = np.array([[1.00, 1.00], [1.00, 1.00]]) uZ = unc.correlated_values_norm(zip(lZ, lZ_Unc), cZ) lComb = np.array([36.330, 41.480, 59.830]) lComb_Unc = np.array([0.405, 0.536, 1.060]) cComb = np.array([[1.00, 0.81, 0.56], [0.81, 1.00, 0.94], [0.56, 0.94, 1.00]]) uComb = unc.correlated_values_norm(zip(lComb, lComb_Unc), cComb) # # --- from combination of 2017H lumi with high pileup Z counts # hasZ = [1,1,1,1] # lZ = np.array([36.330,41.480,59.830])
def test_uncertainties_comparison_general(): import uncertainties from uncertainties import ufloat # compare error propagation. x = UQ_( '2.5 +/- 0.5 m' ) y = UQ_( '2.5 +/- 0.5 m' ) w = x xx = ufloat( 2.5, 0.5 ) yy = ufloat( 2.5, 0.5 ) ww = xx z = x+y zz = xx+yy assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) assert Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) z = x-y/2 zz = xx-yy/2 assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) assert Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) z = x*y zz = xx*yy assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) assert Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) z = x/y zz = xx/yy assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) # linear approximation differs here! assert not Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) z = w - x zz = ww - xx assert Close( z.nominal.magnitude, 0, 1e-5 ) assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) assert Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) # add correlation x.correlated(y,1) (xx,yy) = uncertainties.correlated_values_norm( [(2.5,0.5), (2.5,0.5)], [ [1,1],[1,1] ] ) z = x - y zz = xx - yy assert Close( z.nominal.magnitude, 0, 1e-5 ) assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) assert Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) num = 2 data = [ UQ_('2 +/- 0.1 m') ] * num ddata = [ ufloat(2,0.1) ] * num z = sum(data) zz = sum(ddata) assert Close( z.nominal.magnitude, 2*2, 1e-5 ) assert Close( z.uncertainty.magnitude, (2*0.1), 1e-5 ) assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) assert Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) num = 10 data = [ UQ_('2 +/- 0.1 m') ] * num ddata = [ ufloat(2,0.1) ] * num z = sum(data) zz = sum(ddata) assert Close( z.nominal.magnitude, 10*2, 1e-5 ) assert Close( z.uncertainty.magnitude, (10*0.1), 1e-5 ) assert Close( z.nominal.magnitude, zz.nominal_value, 1e-5 ) assert Close( z.uncertainty.magnitude, zz.std_dev, 1e-5 ) zz = 2*xx ww = zz + yy z = 2*x w = z + y corr = uncertainties.correlation_matrix( [xx,yy,zz,ww] ) assert x.correlation(x) == corr[0][0] assert x.correlation(y) == corr[0][1] assert x.correlation(z) == corr[0][2] assert x.correlation(w) == corr[0][3] assert x.correlation(x) == corr[0][0] assert y.correlation(x) == corr[1][0] assert z.correlation(x) == corr[2][0] assert w.correlation(x) == corr[3][0]