Source code for experiment.expResultObj

"""
.. module:: expResultObj
   :synopsis: Contains class that encapsulates an experimental result

.. moduleauthor:: Veronika Magerl <v.magerl@gmx.at>
.. moduleauthor:: Andre Lessa <lessa.a.p@gmail.com>

"""

import os
from smodels.experiment import infoObj
from smodels.experiment import txnameObj
from smodels.experiment import datasetObj
from smodels.experiment.exceptions import DatabaseNotFoundException
from smodels.tools.physicsUnits import fb

from smodels.tools.smodelsLogging import logger

[docs]class ExpResult(object): """ Object containing the information and data corresponding to an experimental result (experimental conference note or publication). :ivar path: path to the experimental result folder (i.e. ATLAS-CONF-2013-047) :ivar globalInfo: Info object holding the data in <path>/globalInfo.txt :ivar datasets: List of DataSet objects corresponding to the dataset folders in <path> """ def __init__(self, path=None): """ :param path: Path to the experimental result folder """ if path and os.path.isdir(path): self.path = path if not os.path.isfile(os.path.join(path, "globalInfo.txt")): logger.error("globalInfo.txt file not found in " + path) raise TypeError self.globalInfo = infoObj.Info(os.path.join(path, "globalInfo.txt")) self.datasets = [] folders=[] for root, _, files in os.walk(path): folders.append ( (root, files) ) folders.sort() for root, files in folders: if 'dataInfo.txt' in files: # data folder found # Build data set try: dataset = datasetObj.DataSet(root, self.globalInfo) self.datasets.append(dataset) except TypeError: continue def __eq__(self, other ): if self.globalInfo != other.globalInfo: return False if len(self.datasets) != len ( other.datasets ): return False for (myds,otherds) in zip ( self.datasets, other.datasets ): if myds != otherds: return False return True def __ne__(self, other ): return not self.__eq__ ( other ) def __str__(self): label = self.globalInfo.getInfo('id') + ": " dataIDs = [dataset.dataInfo.dataId for dataset in self.datasets] if dataIDs: for dataid in dataIDs: if dataid: label += dataid + "," label = label[:-1] label += ':' txnames = [] for dataset in self.datasets: for txname in dataset.txnameList: tx = txname.txName if not tx in txnames: txnames.append(tx) if isinstance(txnames, list): for txname in txnames: label += txname + ',' else: label += txnames + ',' return label[:-1]
[docs] def getDataset(self, dataId ): """ retrieve dataset by dataId """ for dataset in self.datasets: if dataset.dataInfo.dataId == dataId: return dataset return None
[docs] def getTxNames(self): """ Returns a list of all TxName objects appearing in all datasets. """ txnames = [] for dataset in self.datasets: txnames += dataset.txnameList return txnames
[docs] def getEfficiencyFor ( self, txname, mass, dataset=None): """ Convenience function. Get the efficiency for a specific dataset for a a specific txname. Equivalent to: self.getDataset ( dataset ).getEfficiencyFor ( txname, mass ) """ dataset = self.getDataset ( dataset ) if dataset: return dataset.getEfficiencyFor ( txname, mass ) return None
[docs] def getUpperLimitFor(self, dataID=None, alpha=0.05, expected=False, txname=None, mass=None, compute=False): """ Computes the 95% upper limit (UL) on the signal cross section according to the type of result. For an Efficiency Map type, returns the UL for the signal*efficiency for the given dataSet ID (signal region). For an Upper Limit type, returns the UL for the signal*BR for for the given mass array and Txname. :param dataID: dataset ID (string) (only for efficiency-map type results) :param alpha: Can be used to change the C.L. value. The default value is 0.05 (= 95% C.L.) (only for efficiency-map results) :param expected: Compute expected limit, i.e. Nobserved = NexpectedBG (only for efficiency-map results) :param txname: TxName object or txname string (only for UL-type results) :param mass: Mass array with units (only for UL-type results) :param compute: If True, the upper limit will be computed from expected and observed number of events. If False, the value listed in the database will be used instead. :return: upper limit (Unum object) """ if self.datasets[0].dataInfo.dataType == 'efficiencyMap': if not dataID or not isinstance(dataID, str): logger.error("The data set ID must be defined when computing ULs" \ " for efficiency-map results.") return False useDataset = False for dataset in self.datasets: if dataset.dataInfo.dataId == dataID: useDataset = dataset break if useDataset is False: logger.error("The data set ID not found.") return False if compute: upperLimit = useDataset.getSRUpperLimit(alpha, expected, compute) else: if expected: upperLimit = useDataset.dataInfo.expectedUpperLimit else: upperLimit = useDataset.dataInfo.upperLimit if (upperLimit/fb).normalize()._unit: logger.error("Upper limit defined with wrong units for %s and %s" %(dataset.globalInfo.id,dataset.dataInfo.dataId)) return False elif self.datasets[0].dataInfo.dataType == 'upperLimit': if not txname or not mass: logger.error("A TxName and mass array must be defined when \ computing ULs for upper-limit results.") return False if not isinstance(txname, txnameObj.TxName) and \ not isinstance(txname, str): logger.error("txname must be a TxName object or a string") return False if not isinstance(mass, list): logger.error("mass must be a mass array") return False for tx in self.getTxNames(): if tx == txname or tx.txName == txname: if expected: if not tx.txnameDataExp: upperLimit = None else: upperLimit = tx.txnameDataExp.getValueFor(mass) else: upperLimit = tx.txnameData.getValueFor(mass) else: logger.warning("Unkown data type: %s. Data will be ignored.", self.datasets[0].dataInfo.dataType) return upperLimit
[docs] def getValuesFor(self, attribute=None): """ Returns a list for the possible values appearing in the ExpResult for the required attribute (sqrts,id,constraint,...). If there is a single value, returns the value itself. :param attribute: name of a field in the database (string). If not defined it will return a dictionary with all fields and their respective values :return: list of values or value """ fieldDict = self.__dict__.items()[:] valuesDict = {} while fieldDict: for field, value in fieldDict[:]: if not '<smodels.experiment' in str(value): if not field in valuesDict: valuesDict[field] = [value] else: valuesDict[field].append(value) else: if isinstance(value, list): for entry in value: fieldDict += entry.__dict__.items()[:] else: fieldDict += value.__dict__.items()[:] fieldDict.remove((field, value)) # Try to keep only the set of unique values for key, val in valuesDict.items(): try: valuesDict[key] = list(set(val)) except TypeError: pass if not attribute: return valuesDict elif not attribute in valuesDict.keys(): logger.warning("Could not find field %s in %s", attribute, self.path) return False else: return valuesDict[attribute]
[docs] def getAttributes(self, showPrivate=False): """ Checks for all the fields/attributes it contains as well as the attributes of its objects if they belong to smodels.experiment. :param showPrivate: if True, also returns the protected fields (_field) :return: list of field names (strings) """ fields = self.getValuesFor().keys() fields = list(set(fields)) if not showPrivate: for field in fields[:]: if "_" == field[0]: fields.remove(field) return fields
[docs] def getTxnameWith(self, restrDict={}): """ Returns a list of TxName objects satisfying the restrictions. The restrictions specified as a dictionary. :param restrDict: dictionary containing the fields and their allowed values. E.g. {'txname' : 'T1', 'axes' : ....} The dictionary values can be single entries or a list of values. For the fields not listed, all values are assumed to be allowed. :return: list of TxName objects if more than one txname matches the selection criteria or a single TxName object, if only one matches the selection. """ txnameList = [] for tag, value in restrDict.items(): for txname in self.getTxNames(): txval = txname.getInfo(tag) if txval is False: continue elif txval == value: txnameList.append(txname) if len(txnameList) == 1: txnameList = txnameList[0] return txnameList