ATLAS Offline Software
TableConstructorBase.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
4 #
5 
6 '''
7 @file TableConstructorBase.py
8 @brief Defines TableConstructor base class to read ROOT histograms and convert it to csv table
9 '''
10 
11 from math import fabs
12 from collections import OrderedDict
13 from TrigCostAnalysis.CostMetadataUtil import ignoreUnderflow
14 from AthenaCommon.Logging import logging
15 log = logging.getLogger('TableConstructor')
16 
17 
18 class Column:
19  ''' @brief Class representing a single csv column
20 
21  Used in TableConstructor to store header description and values of csv column
22  '''
23 
24  def __init__(self, header = "Unknown", headerDesc = "Unknown", needNormalizing = False):
25  '''Column header'''
26  self.header = header
27 
28  '''Column description'''
29  self.headerDesc = headerDesc
30 
31  '''The list storing column content'''
32  self.content = []
33 
34  '''Flag if column needs normalization'''
35  self.needNormalizing = needNormalizing
36 
37 
38  def addValue(self, value, index=-1):
39  ''' @brief Add value to the column '''
40  if index < 0 or index >= len(self.content):
41  self.content.append(value)
42  else:
43  self.content[index] += value
44 
45  def updateValue(self, value, index=-1):
46  ''' @brief Add value to the column '''
47  if index < 0 or index >= len(self.content):
48  self.content.append(value)
49  else:
50  self.content[index] = value
51 
52  def getValue(self, index):
53  ''' @brief Get value from column with given index'''
54  if index >= len(self.content):
55  log.error("Index out of range")
56  return None
57  else:
58  return self.content[index]
59 
60 
62  ''' @brief Class representing a single table
63 
64  Base class trepresenting table. It defines basic behavior common for
65  all the tables like caching histograms or saving table to file.
66  '''
67 
68  def __init__(self, tableObj, underflowThreshold, overflowThreshold):
69  '''ROOT table directory object, storing subdirs with histograms'''
70  self.tableObj = tableObj
71 
72  '''Dictionary storing the columns. Each column is identified by given name (key)'''
73  self.columns = OrderedDict()
74 
75  '''Map to cache expected histograms'''
76  self.histograms = {}
77 
78  '''List with expected histograms'''
80 
81  '''Number of underflow bins after which warning will be saved to metadata tree'''
82  self.underflowThreshold = underflowThreshold
83 
84  '''Number of overflow bins after which warning will be saved to metadata tree'''
85  self.overflowThreshold = overflowThreshold
86 
87  '''Overflow log'''
88  self.warningMsg = []
89 
90 
91  def getWarningMsgs(self):
92  ''' @brief Raturn warning messages concerning histogram under/over flows '''
93  return self.warningMsg
94 
95 
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)
100  return None
101  return self.histograms[name]
102 
103 
104  def cacheHistograms(self, dirName, prefix):
105  ''' @brief Cache histograms in map for given directory
106 
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.
109 
110  @param[in] dirName Name of subdirectory to look for histograms
111  @param[in] prefix Prefix of the histogram name
112 
113  '''
114  dirKey = [key for key in self.tableObj.GetListOfKeys() if key.GetName() == dirName]
115  if not dirKey:
116  log.error("Subdirectory %s not found", dirName)
117 
118  dirObj = dirKey[0].ReadObj()
119  for histName in self.expectedHistograms:
120  fullHistName = prefix + dirName + '_' + histName
121  hist = dirObj.Get(fullHistName)
122 
123  if not hist:
124  log.debug("Full name %s", fullHistName)
125  log.debug("Directory: %s", dirObj.ls())
126  log.error("Expected histogram %s not found", histName)
127  else:
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))
136 
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))
142 
143  self.histograms[histName] = hist
144 
145 
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.
148 
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.
151 
152  @return Total value of the histogram.
153  '''
154 
155  hist = self.getHistogram(histName)
156  if not hist:
157  return 0
158 
159  total = 0
160  for i in range(1, hist.GetXaxis().GetNbins()+1):
161  if isLog:
162  total += hist.GetBinContent(i) * hist.GetXaxis().GetBinCenterLog(i)
163  else:
164  total += hist.GetBinContent(i) * hist.GetXaxis().GetBinCenter(i)
165 
166  return total
167 
168 
169  def saveToFile(self, fileName):
170  ''' @brief Function to save table content to csv file with specified fileName
171 
172  @param[in] fileName Name of the file to save the table
173  '''
174 
175  from TrigCostAnalysis.Util import convert
176  import csv
177 
178  with open(fileName, mode='w') as csvFile:
179  csvWriter = csv.writer(csvFile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
180 
181  header = []
182  desc = []
183  rows = []
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 ]
188  rows.append(content)
189 
190  csvWriter.writerow(header)
191  csvWriter.writerow(desc)
192 
193  # Transpose columns to rows
194  rows = list(zip(*rows))
195  for row in rows:
196  csvWriter.writerow(row)
197 
198 
199  def normalizeColumns(self, denominator):
200  ''' @brief Perform normalization on marked columns
201 
202  @param[in] denominator Value to normalize choosen columns
203  '''
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)
209  else:
210  column.content = [entry/denominator for entry in column.content]
211 
212 
213  def fillTable(self, prefix=''):
214  ''' @brief Fill the table based on ROOT directory's content.'''
215 
216  self.defineColumns()
217 
218  # Fill the columns
219  for hist in self.tableObj.GetListOfKeys():
220  self.cacheHistograms(hist.GetName(), prefix)
221  self.fillColumns(hist.GetName())
222 
223  self.postProcessing()
224 
225 
226  def defineColumns(self):
227  ''' @brief Define the columns for the table
228 
229  Columns should be objects of class Column, added to the map self.columns.
230  '''
231  log.error("Should not call defineColumns on base class!")
232 
233 
234  def fillColumns(self, histName=''):
235  ''' @brief Fill the columns with values '''
236  log.error("Should not call fillColumns on base class!")
237 
238 
239  def postProcessing(self):
240  ''' @brief Additional operations
241 
242  Normalization in performed separatly!
243  '''
244  pass
python.TableConstructorBase.TableConstructorBase.warningMsg
warningMsg
Definition: TableConstructorBase.py:88
python.TableConstructorBase.Column.updateValue
def updateValue(self, value, index=-1)
Definition: TableConstructorBase.py:45
python.CostMetadataUtil.ignoreUnderflow
def ignoreUnderflow(histogramName)
Definition: CostMetadataUtil.py:128
python.TableConstructorBase.Column.needNormalizing
needNormalizing
Definition: TableConstructorBase.py:35
python.TableConstructorBase.TableConstructorBase.saveToFile
def saveToFile(self, fileName)
Definition: TableConstructorBase.py:169
vtune_athena.format
format
Definition: vtune_athena.py:14
python.TableConstructorBase.TableConstructorBase.tableObj
tableObj
Definition: TableConstructorBase.py:70
python.TableConstructorBase.TableConstructorBase.getWarningMsgs
def getWarningMsgs(self)
Definition: TableConstructorBase.py:91
python.TableConstructorBase.TableConstructorBase.histograms
histograms
Definition: TableConstructorBase.py:76
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.TableConstructorBase.TableConstructorBase.postProcessing
def postProcessing(self)
Definition: TableConstructorBase.py:239
python.TableConstructorBase.TableConstructorBase
Definition: TableConstructorBase.py:61
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:805
python.TableConstructorBase.Column.__init__
def __init__(self, header="Unknown", headerDesc="Unknown", needNormalizing=False)
Definition: TableConstructorBase.py:24
python.TableConstructorBase.Column.addValue
def addValue(self, value, index=-1)
Definition: TableConstructorBase.py:38
python.TableConstructorBase.Column.header
header
Definition: TableConstructorBase.py:26
python.TableConstructorBase.TableConstructorBase.overflowThreshold
overflowThreshold
Definition: TableConstructorBase.py:85
python.TableConstructorBase.Column
Definition: TableConstructorBase.py:18
python.TableConstructorBase.Column.content
content
Definition: TableConstructorBase.py:32
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.TableConstructorBase.TableConstructorBase.defineColumns
def defineColumns(self)
Definition: TableConstructorBase.py:226
python.TableConstructorBase.TableConstructorBase.underflowThreshold
underflowThreshold
Definition: TableConstructorBase.py:82
python.TableConstructorBase.TableConstructorBase.normalizeColumns
def normalizeColumns(self, denominator)
Definition: TableConstructorBase.py:199
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
python.TableConstructorBase.Column.getValue
def getValue(self, index)
Definition: TableConstructorBase.py:52
TMVAToMVAUtils::convert
std::unique_ptr< MVAUtils::BDT > convert(TMVA::MethodBDT *bdt, bool isRegression=true, bool useYesNoLeaf=false)
Definition: TMVAToMVAUtils.h:114
python.TableConstructorBase.TableConstructorBase.columns
columns
Definition: TableConstructorBase.py:73
Trk::open
@ open
Definition: BinningType.h:40
python.TableConstructorBase.TableConstructorBase.cacheHistograms
def cacheHistograms(self, dirName, prefix)
Definition: TableConstructorBase.py:104
python.TableConstructorBase.TableConstructorBase.getHistogram
def getHistogram(self, name)
Definition: TableConstructorBase.py:96
python.TableConstructorBase.TableConstructorBase.expectedHistograms
expectedHistograms
Definition: TableConstructorBase.py:79
python.TableConstructorBase.TableConstructorBase.fillColumns
def fillColumns(self, histName='')
Definition: TableConstructorBase.py:234
python.TableConstructorBase.TableConstructorBase.fillTable
def fillTable(self, prefix='')
Definition: TableConstructorBase.py:213
python.TableConstructorBase.TableConstructorBase.__init__
def __init__(self, tableObj, underflowThreshold, overflowThreshold)
Definition: TableConstructorBase.py:68
python.TableConstructorBase.TableConstructorBase.getXWeightedIntegral
def getXWeightedIntegral(self, histName, isLog=True)
Definition: TableConstructorBase.py:146
python.TableConstructorBase.Column.headerDesc
headerDesc
Definition: TableConstructorBase.py:29