7 @file TableConstructorBase.py
8 @brief Defines TableConstructor base class to read ROOT histograms and convert it to csv table
12 from collections
import OrderedDict
13 from TrigCostAnalysis.CostMetadataUtil
import ignoreUnderflow
14 from AthenaCommon.Logging
import logging
15 log = logging.getLogger(
'TableConstructor')
19 ''' @brief Class representing a single csv column
21 Used in TableConstructor to store header description and values of csv column
24 def __init__(self, header = "Unknown", headerDesc = "Unknown", needNormalizing = False):
28 '''Column description'''
31 '''The list storing column content'''
34 '''Flag if column needs normalization'''
39 ''' @brief Add value to the column '''
40 if index < 0
or index >= len(self.
content):
46 ''' @brief Add value to the column '''
47 if index < 0
or index >= len(self.
content):
53 ''' @brief Get value from column with given index'''
55 log.error(
"Index out of range")
62 ''' @brief Class representing a single table
64 Base class trepresenting table. It defines basic behavior common for
65 all the tables like caching histograms or saving table to file.
68 def __init__(self, tableObj, underflowThreshold, overflowThreshold):
69 '''ROOT table directory object, storing subdirs with histograms'''
72 '''Dictionary storing the columns. Each column is identified by given name (key)'''
75 '''Map to cache expected histograms'''
78 '''List with expected histograms'''
81 '''Number of underflow bins after which warning will be saved to metadata tree'''
84 '''Number of overflow bins after which warning will be saved to metadata tree'''
92 ''' @brief Raturn warning messages concerning histogram under/over flows '''
97 ''' @brief Return cached histogram with given name'''
99 log.error(
"Histogram %s not found!", name)
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()
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)
130 histIntegral = hist.Integral(0, hist.GetNbinsX() + 1)
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))
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))
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.
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)
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)
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)
194 rows =
list(zip(*rows))
196 csvWriter.writerow(row)
200 ''' @brief Perform normalization on marked columns
202 @param[in] denominator Value to normalize choosen columns
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]
214 ''' @brief Fill the table based on ROOT directory's content.'''
219 for hist
in self.
tableObj.GetListOfKeys():
227 ''' @brief Define the columns for the table
229 Columns should be objects of class Column, added to the map self.columns.
231 log.error(
"Should not call defineColumns on base class!")
235 ''' @brief Fill the columns with values '''
236 log.error(
"Should not call fillColumns on base class!")
240 ''' @brief Additional operations
242 Normalization in performed separatly!