68 def __init__(self, tableObj, underflowThreshold, overflowThreshold):
69 '''ROOT table directory object, storing subdirs with histograms'''
70 self.tableObj = tableObj
72 '''Dictionary storing the columns. Each column is identified by given name (key)'''
73 self.columns = OrderedDict()
75 '''Map to cache expected histograms'''
78 '''List with expected histograms'''
79 self.expectedHistograms = []
81 '''Number of underflow bins after which warning will be saved to metadata tree'''
82 self.underflowThreshold = underflowThreshold
84 '''Number of overflow bins after which warning will be saved to metadata tree'''
85 self.overflowThreshold = overflowThreshold
96 def getHistogram(self, name):
97 ''' @brief Return cached histogram with given name'''
98 if name not in self.histograms:
99 log.error("Histogram %s not found!", name)
101 return self.histograms[name]
104 def cacheHistograms(self, dirName, prefix):
105 ''' @brief Cache histograms in map for given directory
107 Save histograms in the map by their short name as a key. If the histogram
108 with a given name is not found function logs an error.
110 @param[in] dirName Name of subdirectory to look for histograms
111 @param[in] prefix Prefix of the histogram name
114 dirKey = [key for key in self.tableObj.GetListOfKeys() if key.GetName() == dirName]
116 log.error("Subdirectory %s not found", dirName)
118 dirObj = dirKey[0].ReadObj()
119 for histName in self.expectedHistograms:
120 fullHistName = prefix + dirName + '_' + histName
121 hist = dirObj.Get(fullHistName)
124 log.debug("Full name %s", fullHistName)
125 log.debug("Directory: %s", dirObj.ls())
126 log.error("Expected histogram %s not found", histName)
128 # Check for under and overflows
129 # Under/overflow bin stores weighted value - we cannot base on entries
130 histIntegral = hist.Integral(0, hist.GetNbinsX() + 1) # Integral including overflows
131 overflowThreshold = self.overflowThreshold * histIntegral
132 overflow = hist.GetBinContent(hist.GetNbinsX() + 1)
133 if overflow > overflowThreshold:
134 log.warning("Histogram {0} contains overflow of {1}".format(fullHistName, overflow))
135 self.warningMsg.append("Overflow of {0} ({1} histogram integral) in {2}".format(overflow, histIntegral, fullHistName))
137 underflowThreshold = self.underflowThreshold * histIntegral
138 underflow = hist.GetBinContent(0)
139 if underflow > underflowThreshold and not ignoreUnderflow(fullHistName):
140 log.warning("Histogram {0} contains underflow of {1}".format(fullHistName, underflow))
141 self.warningMsg.append("Underflow of {0} ({1} histogram integral) in {2}".format(underflow, histIntegral, fullHistName))
143 self.histograms[histName] = hist
146 def getXWeightedIntegral(self, histName, isLog = True):
147 ''' @brief Get "total" value by integrating over a histogram, weighting every entry by its x-axis mean.
149 @param[in] histName Histogram name
150 @param[in] isLog If histogram is log x-axis, modifies how x-axis mean is computed for each bin.
152 @return Total value of the histogram.
155 hist = self.getHistogram(histName)
160 for i in range(1, hist.GetXaxis().GetNbins()+1):
162 total += hist.GetBinContent(i) * hist.GetXaxis().GetBinCenterLog(i)
164 total += hist.GetBinContent(i) * hist.GetXaxis().GetBinCenter(i)
169 def saveToFile(self, fileName):
170 ''' @brief Function to save table content to csv file with specified fileName
172 @param[in] fileName Name of the file to save the table
175 from TrigCostAnalysis.Util import convert
178 with open(fileName, mode='w') as csvFile:
179 csvWriter = csv.writer(csvFile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
184 for column in self.columns.values():
185 header.append(column.header)
186 desc.append(column.headerDesc)
187 content = [ convert(entry) for entry in column.content ]
190 csvWriter.writerow(header)
191 csvWriter.writerow(desc)
193 # Transpose columns to rows
194 rows = list(zip(*rows))
196 csvWriter.writerow(row)
199 def normalizeColumns(self, denominator):
200 ''' @brief Perform normalization on marked columns
202 @param[in] denominator Value to normalize choosen columns
204 for columnName, column in self.columns.items():
205 if column.needNormalizing:
206 if fabs(denominator) < 1e-10:
207 log.error("Normalise denominator is zero. Setting %s to zero.", columnName)
208 column.content = [0] * len(column.content)
210 column.content = [entry/denominator for entry in column.content]
213 def fillTable(self, prefix=''):
214 ''' @brief Fill the table based on ROOT directory's content.'''
219 for hist in self.tableObj.GetListOfKeys():
220 self.cacheHistograms(hist.GetName(), prefix)
221 self.fillColumns(hist.GetName())
223 self.postProcessing()