# ICE Revision: $Id$
"""Data structure to do some calculations on the results from
SpreadSheetData-methods metrics and compare that are organized in 2
dimensions"""
import sys
from PyFoam.Basics.TableData import TableData
from PyFoam.Error import error
from math import *
import collections
[docs]class Data2DStatistics(object):
"""Oranize statistics about data in 2D-Tables and do basic
calculations on it"""
def __init__(self,metrics,
compare=None,
small=1e-10,
noStrings=False,
failureValue=None):
"""
:param metrics: metrics of the data
:param compare: metrics of the comparsion with another data-set
:param small: the value that is considered to be close to 0
:param noStrings: only put numbers into the tables
:param failureValue: the value to use if an evaluation fails
"""
self.__metrics=metrics
self.__compare=compare
self.small=small
self.noStrings=noStrings
self.failureValue=failureValue
def _getLabels(self):
"""Return a tuple with the names of the rows and the
columns. Assumes that the names for the first data-set are
valid for all"""
colNames=list(self.__metrics.keys())
rowNames=list(self.__metrics[colNames[0]].keys())
return rowNames,colNames
def _makeEmptyTable(self):
"""Create an empty table to fill the data in"""
r,c=self._getLabels()
return TableData(r,c)
def _extractTable(self,name,data=None):
"""Extract data and fill it into a data-table
:param name: name of the entry that should be got
:param data: the dataset. If unset then self.__metrics is used"""
if data==None:
data=self.__metrics
tab=self._makeEmptyTable()
row,col=self._getLabels()
for r in row:
for c in col:
tab[(r,c)]=data[c][r][name]
return tab
[docs] def names(self):
"Valid data names"
row,col=self._getLabels()
return list(self.__metrics[col[0]][row[0]].keys())
[docs] def compare(self):
"""Get a separate Data2DStatistics with the compare-data (if
present)"""
if self.__compare==None:
error("No compare data present")
return Data2DStatistics(self.__compare)
def __getitem__(self,name):
return self._extractTable(name)
[docs] def func(self,func,val):
"""Evaluate a function on the data
:param func: either a callable function or a string that evaluates to a callable
:param val: name of the data value to use"""
if isinstance(func, collections.Callable):
f=func
elif type(func)==str:
f=eval(func)
if not isinstance(f, collections.Callable):
error(func,"does not evaluate to a callable")
else:
error(func,"is neither callable nor a string")
tab=self._makeEmptyTable()
row,col=self._getLabels()
data=self[val]
for r in row:
for c in col:
tab[(r,c)]=f(data[(r,c)])
return tab
[docs] def range(self):
"""Return a table with the ranges of the data"""
minD=self._extractTable("min")
maxD=self._extractTable("max")
tab=self._makeEmptyTable()
row,col=self._getLabels()
for r in row:
for c in col:
tab[(r,c)]=(minD[(r,c)],maxD[(r,c)])
return tab
def __relativeErrorInternal(self,name):
"""Return a table with the relative error
:param name: spcifies the name under which the error is found in the data"""
dataRange=self.range()
if self.__compare==None:
error("Need comparison data for relative error")
maxError=self._extractTable(name,self.__compare)
rErr=self._makeEmptyTable()
row,col=self._getLabels()
for r in row:
for c in col:
rng=(lambda r:r[1]-r[0])(dataRange[(r,c)])
mx=maxError[(r,c)]
if rng>self.small:
try:
rErr[(r,c)]=mx/rng
except TypeError:
e = sys.exc_info()[1] # Needed because python 2.5 does not support 'as e'
rErr[(r,c)]=self.failureValue
if self.failureValue==None:
raise e
elif mx>self.small:
if self.noStrings:
rErr[(r,c)]=0.
else:
rErr[(r,c)]="constant (%g)" % mx
else:
if self.noStrings:
rErr[(r,c)]=0.
else:
rErr[(r,c)]="constant =="
return rErr
[docs] def relativeError(self):
"""Return a table with the relative error"""
return self.__relativeErrorInternal("max")
[docs] def relativeAverageError(self):
"""Return a table with the relative average error"""
return self.__relativeErrorInternal("wAverage")
# Should work with Python3 and Python2