Source code for performanceQuantiles

#!/usr/bin/env python3
"""
Digraph3 collection of python3 modules for 
Algorithmic Decision Theory applications

Module for incremental performance quantiles computation

Copyright (C) 2016-2023 Raymond Bisdorff

    This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR ANY PARTICULAR PURPOSE.  See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

######################################

__version__ = "$Revision: Python 3.10"


"""
from time import time
from decimal import Decimal
from perfTabs import PerformanceTableau

[docs] class PerformanceQuantiles(PerformanceTableau): """ Implements the incremental performance quantiles representation of a given performance tableau. *Parameters*: * *perfTab*: may be either a PerformanceTableau object or the name of a previously saved PerformanceQuantiles instance * *NumberOfBins* may be either 'quartiles', 'deciles', ... or 'n', the integer number of bins. Example python session: >>> import performanceQuantiles >>> from randomPerfTabs import RandomCBPerformanceTableau >>> from randomPerfTabs import RandomPerformanceGenerator as PerfTabGenerator >>> nbrActions=1000 >>> nbrCrit = 7 >>> tp = RandomCBPerformanceTableau(numberOfActions=nbrActions, ... numberOfCriteria=nbrCrit,seed=105) >>> pq = performanceQuantiles.PerformanceQuantiles(tp,'quartiles', ... LowerClosed=True,Debug=False) >>> pq.showLimitingQuantiles(ByObjectives=True) *---- performance quantiles -----* Costs criteria | weights | '0.0' '0.25' '0.5' '0.75' '1.0' ---------|-------------------------------------------------- 'c1' | 6 | -97.12 -65.70 -46.08 -24.96 -1.85 Benefits criteria | weights | '0.0' '0.25' '0.5' '0.75' '1.0' ---------|-------------------------------------------------- 'b1' | 1 | 2.11 27.92 48.76 68.94 98.69 'b2' | 1 | 0.00 3.00 5.00 7.00 10.00 'b3' | 1 | 1.08 30.41 50.57 69.01 97.23 'b4' | 1 | 0.00 3.00 5.00 7.00 10.00 'b5' | 1 | 1.84 29.77 50.62 70.14 96.40 'b6' | 1 | 0.00 3.00 5.00 7.00 10.00 >>> tpg = PerfTabGenerator(tp,seed=105) >>> newActions = tpg.randomActions(100) >>> pq.updateQuantiles(newActions,historySize=None) >>> pq.showHTMLLimitingQuantiles(Transposed=True) .. image:: examplePerfQuantiles.png :alt: Example limiting quantiles html show method :width: 400 px :align: center """ def __repr__(self): """ Default presentation method for PerformanceQuantiles instances """ reprString = '*------- PerformanceQuantiles instance description ------*\n' reprString += 'Instance class : %s\n' % self.__class__.__name__ try: reprString += 'Seed : %s\n' % str(self.randomSeed) except: pass reprString += 'Instance name : %s\n' % self.name #reprString += '# Actions : %d\n' % len(self.actions) try: reprString += 'Objectives : %d\n' % len(self.objectives) except: pass reprString += 'Criteria : %d\n' % len(self.criteria) reprString += 'Quantiles : %d\n' % (len(self.quantilesFrequencies)-1) reprString += 'History sizes : %s\n' % self.historySizes reprString += 'Attributes : %s\n' % list(self.__dict__.keys()) return reprString def __init__(self,perfTab=None,numberOfBins=4, LowerClosed=True, Debug=False): from copy import deepcopy from collections import OrderedDict, defaultdict from randomPerfTabs import RandomCBPerformanceTableau,\ Random3ObjectivesPerformanceTableau,\ RandomPerformanceTableau ## if not LowerClosed: ## LowerClosed=True ## print('Note: Only lower-closed quantile classes are implemented') if type(perfTab) != str: self.perfTabType = perfTab.__class__.__name__ try: self.valueDigits = perfTab.valueDigits except: self.valueDigits = 2 actionsTypeStatistics = {} for x in perfTab.actions: if type(perfTab) == RandomCBPerformanceTableau: xType = perfTab.actions[x]['type'] elif type(perfTab) == Random3ObjectivesPerformanceTableau: self.objectiveSupportingTypes = perfTab.objectiveSupportingTypes xType = 'Soc' + perfTab.actions[x]['profile']['Soc'] xType += 'Eco' + perfTab.actions[x]['profile']['Eco'] xType += 'Env' + perfTab.actions[x]['profile']['Env'] else: xType = 'NA' try: actionsTypeStatistics[xType] += 1 except: actionsTypeStatistics[xType] = 1 self.actionsTypeStatistics = actionsTypeStatistics try: self.objectives = perfTab.objectives except: self.objectives = None try: self.commonScale = perfTab.commonScale except: pass try: self.OrdinalScales = perfTab.OrdinalScales except: pass try: self.BigData = perfTab.BigData except: pass try: self.missingDataProbability = perfTab.missingDataProbability except: pass try: self.NA = perfTab.NA except: self.NA = Decimla('-999.00') self.criteria = deepcopy(perfTab.criteria) self.LowerClosed = LowerClosed self.quantilesFrequencies = self._computeQuantilesFrequencies(numberOfBins,Debug=Debug) np = len(self.quantilesFrequencies) limitingQuantiles = {} cdf = {} self.historySizes = {} for g in self.criteria: self.historySizes[g] = 0 limitingQuantiles[g] = self._computeLimitingQuantiles(perfTab,g) if Debug: print(g,limitingQuantiles[g]) cdf[g] = OrderedDict([(limitingQuantiles[g][i],self.quantilesFrequencies[i]) for i in range(np)]) self.limitingQuantiles = limitingQuantiles self.cdf = cdf elif type(perfTab) == str: # a stored instance file name is given fileName = perfTab + '.py' argDict = {} fi = open(fileName,'r') exec(compile(fi.read(), fileName, 'exec'),argDict) fi.close() self.name = str(perfTab) try: self.objectives = argDict['objectives'] except: pass try: self.objectiveSupportingTypes = argDict['objectiveSupportingTypes'] except: pass try: self.commonScale = argDict['commonScale'] except: pass try: self.OrdinalScales = argDict['OrdinalScales'] except: pass try: self.BigData = argDict['BigData'] except: pass try: self.missingDataProbability = argDict['missingDataProbability'] except: pass try: self.NA = argDict['NA'] except: pass self.criteria = argDict['criteria'] self.quantilesFrequencies = argDict['quantilesFrequencies'] self.historySizes = argDict['historySizes'] self.LowerClosed = argDict['LowerClosed'] self.NA = argDict['NA'] self.limitingQuantiles = argDict['limitingQuantiles'] cdf = {} np = len(self.quantilesFrequencies) for g in self.criteria: cdf[g] = {} for i in range(np): cdf[g][self.limitingQuantiles[g][i]] = self.quantilesFrequencies[i] #cdf[g] = OrderedDict([(self.limitingQuantiles[g][i],self.quantilesFrequencies[i]) for i in range(np)]) self.cdf = cdf self.perfTabType = argDict['perfTabType'] #--------- private class methods def _computeQuantilesFrequencies(self,x,Debug=False): """ renders the quantiles frequencies """ from math import floor if isinstance(x,int): n = x elif x == 'quartiles': n = 4 elif x == 'quintiles': n = 5 elif x == 'sextiles': n = 6 elif x == 'heptiles': n = 7 elif x == 'octiles': n = 8 elif x == 'deciles': n = 10 elif x == 'dodeciles': n = 20 elif x == 'centiles': n = 100 else: print("""Error: numberOfBins must be either an integer, None or a string out of ['quartiles','quintiles','sextiles','heptiles 'octiles','deciles','dodeciles','centiles']""") return quantilesFrequencies = [] for i in range(n+1): freqStr = '%.6f' % (Decimal(str(i)) / Decimal(str(n))) quantilesFrequencies.append(Decimal(freqStr)) self.name = '%d-tiled_performances' % n if Debug: print(x,quantilesFrequencies) return quantilesFrequencies def _computeLimitingQuantiles(self,perfTab,g,Debug=False): """ Renders the list of limiting quantiles *q(p)* on criteria *g* for *p* in *frequencies* """ from math import floor from copy import copy, deepcopy critg = self.criteria[g] NA = self.NA gValues = [] for x in perfTab.actions: if Debug: print('g,x,evaluation[g][x]',g,x,perfTab.evaluation[g][x]) if perfTab.evaluation[g][x] != NA: if critg['weight'] > Decimal('0'): gValues.append(perfTab.evaluation[g][x]) else: gValues.append(-perfTab.evaluation[g][x]) gValues.sort() if critg['weight'] > Decimal('0'): self.criteria[g]['minValue'] = gValues[0] self.criteria[g]['maxValue'] = gValues[-1] else: self.criteria[g]['minValue'] = -gValues[0] self.criteria[g]['maxValue'] = -gValues[-1] ## if PrefThresholds: ## try: ## gPrefThrCst = self.criteria[g]['thresholds']['pref'][0] ## gPrefThrSlope = self.criteria[g]['thresholds']['pref'][1] ## except: ## gPrefThrCst = Decimal('0') ## gPrefThrSlope = Decimal('0') n = len(gValues) self.historySizes[g] = n if Debug: print('g,n,gValues',g,n,gValues) ## if n > 0: ## nf = Decimal(str(n+1)) nf = Decimal(str(n)) quantilesFrequencies = self.quantilesFrequencies #limitingQuantiles = [Decimal(str(q)) for q in frequencies] #limitingQuantiles.sort() #self.limitingQuantiles = limitingQuantiles if Debug: print(quantilesFrequencies) # computing the quantiles on criterion g gQuantiles = [] if self.LowerClosed: # we ignore the 1.00 quantile and replace it with +infty for q in quantilesFrequencies: r = (Decimal(str(nf)) * q) rq = int(floor(r)) if Debug: print('r,rq',r,rq, end=' ') if rq < (n-1): quantile = gValues[rq]\ + ((r-Decimal(str(rq)))*(gValues[rq+1]-gValues[rq])) ## if rq > 0 and PrefThresholds: ## quantile += gPrefThrCst + quantile*gPrefThrSlope else : #if self.criteria[g]['preferenceDirection'] == 'min': #quantile = Decimal('100.0') quantile = gValues[-1] #else: #quantile = Decimal('200.0') #quantile = gValues[-1] if Debug: print('quantile',quantile) gQuantiles.append(quantile) else: # upper closed categories # we ignore the quantile 0.0 and replace it with -\infty for q in quantilesFrequencies: r = (Decimal(str(nf)) * q) rq = int(floor(r)) if Debug: print('r,rq',r,rq, end=' ') if rq == 0: #if self.criteria[g]['preferenceDirection'] == 'min': #quantile = Decimal('-200.0') quantile = gValues[0] #else: #quantile = Decimal('-100.0') #quantile = gValues[0] elif rq < (n-1): quantile = gValues[rq]\ + ((r-Decimal(str(rq)))*(gValues[rq+1]-gValues[rq])) ## if PrefThresholds: ## quantile -= gPrefThrCst - quantile*gPrefThrSlope else: if n > 0: quantile = gValues[n-1] else: #if self.criteria[g]['preferenceDirection'] == 'min': quantile = gValues[-1] #else: #quantile = gValues[-1] if Debug: print('quantile',quantile) gQuantiles.append(quantile) ## else: ## gQuantiles = [] #if critg['weight'] < Decimal('0'): # gQuantiles = [-quantile for quantile in gQuantiles] if Debug: print(g,self.LowerClosed,self.criteria[g]['preferenceDirection'],gQuantiles) return gQuantiles def _htmlLimitingQuantiles(self,Sorted=False, Transposed=False,ndigits=2, ContentCentered=True, title=None): """ Renders the limiting quantiles in table format: citerion x limitss in html format. """ criteria = self.criteria if title is None: html = '<h1>Performance quantiles</h1>' else: html = '<h1>%s</h1>' % title historySizes = [self.historySizes[g] for g in self.historySizes] html += '<p>Sampling sizes between %d and %d.</p>' % ( min(historySizes),max(historySizes)) ## if self.LowerClosed: ## html += '<p>Quantile bins %s.</p>' % ('lowerclosed') ## else: ## html += '<p>Quantile bins %s.</p>' % ('upperclosed') criteriaKeys = [g for g in self.criteria] if Sorted: criteriaKeys.sort() limitingQuantiles = self.limitingQuantiles quantilesFrequencies = self.quantilesFrequencies nq = len(quantilesFrequencies) if ContentCentered: alignFormat = 'center' else: alignFormat = 'right' if Transposed: html += '<table style="background-color:White;" border="1">' html += '<tr bgcolor="#9acd32"><th>criterion</th>' for x in quantilesFrequencies: xName = '%.2f' % x html += '<th bgcolor="#FFF79B">%s</th>' % (xName) html += '</tr>' for g in criteriaKeys: try: gName = criteria[g]['shortName'] except: gName = str(g) html += '<tr><th bgcolor="#FFF79B">%s</th>' % (gName) for i in range(nq): formatString = '<td align="%s">%% .%df</td>' % (alignFormat,ndigits) if criteria[g]['preferenceDirection'] == 'max': value = min(criteria[g]['scale'][1],max(criteria[g]['scale'][0],limitingQuantiles[g][i])) else: if criteria[g]['weight'] > Decimal('0'): value = max(-criteria[g]['scale'][1],min(-criteria[g]['scale'][0],limitingQuantiles[g][i])) else: value = max(criteria[g]['scale'][0],min(criteria[g]['scale'][1],-limitingQuantiles[g][i])) html += formatString % (value) html += '</tr>' html += '</table>' else: html += '<table style="background-color:White;" border="1">' html += '<tr bgcolor="#9acd32"><th>criterion</th>' for g in criteriaKeys: try: gName = criteria[g]['shortName'] except: gName = str(g) html += '<th bgcolor="#FFF79B">%s</th>' % (gName) html += '</tr>' for i in range(nq): xName = '%.2f' % quantilesFrequencies[i] html += '<tr><th bgcolor="#FFF79B">%s</th>' % (xName) for g in criteriaKeys: formatString = '<td align="%s">%% .%df</td>' % (alignFormat,ndigits) if criteria[g]['preferenceDirection'] == 'max': value = min(criteria[g]['scale'][1],max(criteria[g]['scale'][0],limitingQuantiles[g][i])) else: if criteria[g]['weight'] > Decimal('0'): value = max(-criteria[g]['scale'][1],min(-criteria[g]['scale'][0],limitingQuantiles[g][i])) else: value = max(criteria[g]['scale'][0],min(criteria[g]['scale'][1],-limitingQuantiles[g][i])) html += formatString % (value) html += '</tr>' html += '</table>' return html def _interpolateQuantile(self,x,newq,newp,Debug=False): #Debug = self.Debug #Debug = True if Debug: print('==>>?') print('x') print(x) print('newq') print(newq) print('newp') print(newp) np = len(newp) i = 0 while i < np-1: if Debug: print(i) if x < newp[i]: ix = i+1 if Debug: print('x < newp[i]', x, newp[i],ix, newp[i-1],newq[ix-1],newp[i],newq[ix]) # nsq[0] diffp = newp[i]-newp[i-1] if diffp > Decimal('0.0'): res = newq[ix-1]+ ((x-newp[i-1])/diffp)*(newq[ix]-newq[ix-1]) else: # avoid dividing by 0 res = newq[ix-1] if Debug: print('res', res) return res #i = np elif x == newp[i+1]: ix = i+1 res = newq[ix] if Debug: print('x == newp[i]',ix, newp[ix],newq[ix],res) return res else: # x > newp[i] ix = i+1 i += 1 if Debug: print('x > newp[i]', i,ix) # if x is in the last quantile class if i == np-1: if x < newp[i]: ix = i if Debug: print('x < newp[i]', x, newp[i],ix, newp[i-1],newq[ix-1],newp[i],newq[ix]) # nsq[0] diffp = newp[i]-newp[i-1] if diffp > Decimal('0.0'): res = newq[ix-1]+ ((x-newp[i-1])/diffp)*(newq[ix]-newq[ix-1]) else: # avoid dividing by 0 res = newq[ix-1] if Debug: print('res', res) return res i = np else: # x == newp[i]: ix = i res = newq[ix] if Debug: print('x == newp[i]',ix, newp[ix],newq[ix],res) return res def _updateCriterionQuantiles(self,g,newValues,historySize=None,Debug=False): """ See lecture about the iq-agent of the MICS-3 Computational Statistics Course. """ #Debug = self.Debug #Debug = True if Debug: print('==>>', g,newValues) from collections import OrderedDict # get present state of the quantiles p = self.quantilesFrequencies np = len(p) q = self.limitingQuantiles[g] cdf = self.cdf[g] if historySize is None: t = self.historySizes[g] else: t = historySize oldfrq = [p[i]*(t+1) for i in range(np)] if Debug: print('q', q) print('p', p) print('cdf', cdf) print('oldfrq', oldfrq) print(t) # new observations nv = [] for x in newValues: if x != self.NA: ## if self.criteria[g]['weight'] < Decimal('0'): ## nv.append(-x) ## else: nv.append(x) nv.sort() nt = len(nv) if Debug: print('nv,nt', nv,nt) ### if nt > 0: # there may be solely missing values observed on g # Init results newq and newp # Init indexes: i in q & p & oldfrq, j in nv, ins = # insertions newp = [Decimal('0.0')] if nv[0] < q[0]: self.criteria[g]['minValue'] = nv[0] newq= [nv[0]] ins = 1 j = 1 i = 0 elif nv[0] > q[0] : newq = [q[0]] j = 0 ins = 0 i = 1 else: newq = [q[0]] j = 1 ins = 0 i = 1 if nv[-1] > q[-1]: self.criteria[g]['maxValue'] = nv[-1] # compute new cumulative densities while i < np: while j < nt and i < np: #print(i,j) if nv[j] > q[i]: newq.append(q[i]) # ins += 0 newp.append(cdf[q[i]]+ins) i += 1 elif nv[j] < q[i]: if nv[j] > newq[-1]: newq.append(nv[j]) # ins += 1 # interpolate cdf of nv[j] cdfnv = cdf[q[i-1]] + (nv[j]-q[i-1])/(q[i]-q[i-1])*cdf[q[i]] + ins newp.append(cdfnv) else: newp[-1] += 1 ins += 1 j +=1 else: # nv[j] = q[i] newp[-1] += 1 ins += 1 j += 1 if j == nt: for ni in range(i,np): newq.append(q[ni]) newp.append(cdf[q[ni]]+ins) #ins += 0 i = np for nj in range(j,nt): ins += 1 if newq[-1] < nv[nj]: newq.append(nv[nj]) newp.append(newp[-1]+1) else: newp[-1] += 1 if Debug: self.newp = newp self.newq = newq print('newp', newp) print('newq', newq) # renormalising frequencies if Debug: print('#inserts = %d' % ins) t += len(nv) for i in range(len(newq)): newp[i] /= newp[-1] if Debug: print('p q (t+1)*q') for i in range(len(newq)): print('%.3f \t %.2f \t %.2f' % (newp[i],newq[i],newp[i]*(t)) ) print('t = %d' % t) # compute new state by interpolation nstate = {p[0]:newq[0]} if Debug: print('interpolate ==>> ',p) np = len(p) for i in range(1,np): x = p[i] if Debug: print('pi', i, x) nstate[x] = self._interpolateQuantile(x,newq,newp,Debug=Debug) if Debug: print('nstate', nstate) # store new state q = [nstate[x] for x in nstate] q.sort() self.limitingQuantiles[g] = q cdf = {} for p in nstate: cdf[nstate[p]] = p self.cdf[g] = cdf self.historySizes[g] = t #------------- public class methods
[docs] def computeQuantileProfile(self,p,qFreq=None,Debug=False): """ Renders the quantile *q(p)* on all the criteria. """ from collections import OrderedDict from decimal import Decimal x = Decimal('%.2f' % p) if qFreq is None: qFreq = self.quantilesFrequencies quantiles = OrderedDict() for g in self.criteria: q = self.limitingQuantiles[g] quantiles[g] = self._interpolateQuantile(x,q,qFreq) if Debug: print(x, quantiles[g], q) return quantiles
[docs] def save(self,fileName='tempPerfQuant',valueDigits=2): """ Persistant storage of a PerformanceQuantiles instance. """ print('*--- Saving performance quantiles in file: <' + str(fileName) + '.py> ---*') valueString = 'Decimal("%%.%df"),\n' % (valueDigits) objectives = self.objectives fileNameExt = str(fileName)+str('.py') fo = open(fileNameExt, 'w') fo.write('# Saved performance quantiles: \n') fo.write('from decimal import Decimal\n') fo.write('from collections import OrderedDict\n') # perfTabType fo.write('perfTabType = \'%s\'\n' % self.perfTabType) # objectives try: fo.write('objectiveSupportingTypes = %s\n' % str(self.objectiveSupportingTypes) ) except: pass fo.write('objectives = OrderedDict([\n') if objectives is not None: for obj in objectives: fo.write('(\'%s\', {\n' % str(obj)) for it in self.objectives[obj].keys(): fo.write('\'%s\': %s,\n' % (it,repr(self.objectives[obj][it]))) fo.write('}),\n') fo.write('])\n') # criteria try: fo.write('OrdinalScales = %s\n' % str(self.OrdinalScales)) except: pass try: fo.write('BigData = %s\n' % str(self.BigData)) except: pass try: fo.write('missingDataProbability = %s\n' % str(self.missingDataProbability)) except: pass try: fo.write('NA = Decimal(\'%s\')\n' % str(self.NA)) except: pass try: fo.write('commonScale = %s\n' % str(self.commonScale)) except: pass criteria = self.criteria fo.write('criteria = OrderedDict([\n') for g in criteria: fo.write('(\'%s\', {\n' % str(g)) for it in self.criteria[g].keys(): fo.write('\'%s\': %s,\n' % (it,repr(self.criteria[g][it]))) fo.write('}),\n') fo.write('])\n') # quanties frequencies quantilesFrequencies = self.quantilesFrequencies np = len(quantilesFrequencies) fo.write('quantilesFrequencies = [\n') for i in range(np): fo.write(valueString % quantilesFrequencies[i] ) fo.write( ']\n') # history sizes historySizes = self.historySizes fo.write('historySizes = {\n') for g in criteria: fo.write('\'%s\': %d,' % (g,historySizes[g]) ) fo.write( '}\n') # quantile limits fo.write('LowerClosed = %s\n' % repr(self.LowerClosed)) limitingQuantiles = self.limitingQuantiles fo.write('limitingQuantiles = {\n') for g in criteria: fo.write('\'%s\': [\n' % g ) for i in range(np): fo.write(valueString % limitingQuantiles[g][i] ) fo.write('],\n') fo.write( '}\n') fo.close()
[docs] def showActions(self): print("""No decision actions are actually being stored! Only the cumulated density function per criteria of so far observed performance evaluations are kept. The number of so far observed evaluations per criteria are the following: """ ) for g in self.criteria: print(g,self.historySizes[g])
[docs] def showCriteria(self,IntegerWeights=False,Alphabetic=False,ByObjectives=True,Debug=False): """ print Criteria with thresholds and weights. """ ## if self.Debug: ## Debug = True criteria = self.criteria try: objectives = self.objectives except: ByObjectives = False print('*---- criteria -----*') ## sumWeights = Decimal('0.0') ## for g in criteria: ## sumWeights += criteria[g]['weight'] sumWeights = sum([criteria[g]['weight'] for g in criteria]) if ByObjectives: for obj in objectives.keys(): criteriaList = [g for g in criteria if criteria[g]['objective']==obj] for g in criteriaList: try: criterionName = '%s/' % objectives[criteria[g]['objective']]['name'] except: criterionName = '' try: criterionName += criteria[g]['name'] except: pass print(g, repr(criterionName)) print(' Scale =', criteria[g]['scale']) if IntegerWeights: print(' Weight = %d ' % (criteria[g]['weight'])) else: weightg = criteria[g]['weight']/sumWeights print(' Weight = %.3f ' % (weightg)) try: for th in criteria[g]['thresholds']: if Debug: print('-->>>', th,criteria[g]['thresholds'][th][0],criteria[g]['thresholds'][th][1]) print(' Threshold %s : %.2f + %.2fx' %\ (th,criteria[g]['thresholds'][th][0],criteria[g]['thresholds'][th][1])) #print self.criteria[g]['thresholds'][th] #print('; percentile: ',self.computeVariableThresholdPercentile(g,th,Debug)) except: pass #print(self.limitingQuantiles[g]) print(' history size: %d' % self.historySizes[g]) print(' p : quantile(p)') if self.LowerClosed: nq = len(self.quantilesFrequencies) for i in range(nq): q = self.quantilesFrequencies[i] #print(q,self.limitingQuantiles[g]) if i < (nq-1): print('%.2f : %.2f' % (q,self.limitingQuantiles[g][i]) ) else: print('%.2f : %.2f' % (q,self.criteria[g]['maxValue']) ) else: # upperclosed quantile bins nq = len(self.quantilesFrequencies) for i in range(nq): q = self.quantilesFrequencies[i] #print(q,self.limitingQuantiles[g]) if i > 0: print('%.2f : %.2f' % (q,self.limitingQuantiles[g][i]) ) else: print('%.2f : %.2f' % (q,self.criteria[g]['minValue']) ) ## for i in range(len(self.quantilesFrequencies)): ## q = self.quantilesFrequencies[i] ## if i > 0: ## print('%.2f : %.2f' % (q,self.limitingQuantiles[g][i]) ) ## else: ## print('%.2f : %.2f' % (q,self.criteria[g]['minValue']) ) print() else: criteriaList = list(self.criteria.keys()) if Alphabetic: criteriaList.sort() for g in criteriaList: try: criterionName = '%s/' % objectives[criteria[g]['objective']]['name'] except: criterionName = '' try: criterionName += criteria[g]['name'] except: pass print(g, repr(criterionName)) print(' Scale =', criteria[g]['scale']) if IntegerWeights: print(' Weight = %d ' % (criteria[g]['weight'])) else: weightg = criteria[g]['weight']/sumWeights print(' Weight = %.3f ' % (weightg)) try: for th in criteria[g]['thresholds']: if Debug: print('-->>>', th,criteria[g]['thresholds'][th][0],criteria[g]['thresholds'][th][1]) print(' Threshold %s : %.2f + %.2fx'\ % (th,criteria[g]['thresholds'][th][0],criteria[g]['thresholds'][th][1])) #print self.criteria[g]['thresholds'][th] #print('; percentile: ',self.computeVariableThresholdPercentile(g,th,Debug)) except: pass #print(self.limitingQuantiles[g]) print(' history size: %d' % self.historySizes[g]) print(' p : qantile(p') if self.LowerClosed: nq = len(self.quantilesFrequencies) for i in range(nq): q = self.quantilesFrequencies[i] #print(q,self.limitingQuantiles[g]) if i < (nq-1): print('%.2f : %.2f' % (q,self.limitingQuantiles[g][i]) ) else: print('%.2f : %.2f' % (q,self.criteria[g]['maxValue']) ) else: # upperclosed quantile bins nq = len(self.quantilesFrequencies) for i in range(nq): q = self.quantilesFrequencies[i] #print(q,self.limitingQuantiles[g]) if i > 0: print('%.2f : %.2f' % (q,self.limitingQuantiles[g][i]) ) else: print('%.2f : %.2f' % (q,self.criteria[g]['minValue']) ) print()
[docs] def showHTMLLimitingQuantiles(self,Sorted=True, Transposed=False,ndigits=2, ContentCentered=True,title=None, htmlFileName=None): """ shows the html version of the limiting quantiles in a browser window. """ import webbrowser if htmlFileName == None: from tempfile import NamedTemporaryFile fileName = (NamedTemporaryFile(suffix='.html', delete=False,dir='.')).name else: from os import getcwd fileName = getcwd()+'/'+htmlFileName fo = open(fileName,'w') fo.write(self._htmlLimitingQuantiles(Sorted=Sorted,\ Transposed=Transposed,\ ndigits=ndigits, ContentCentered=ContentCentered, title=title)) fo.close() url = 'file://'+fileName webbrowser.open(url,new=2)
[docs] def showLimitingQuantiles(self,ByObjectives=False,Sorted=False,ndigits=2): """ Prints the performance quantile limits in table format: criteria x limits. """ criteria = self.criteria print('*---- performance quantiles -----*') quantiles = self.quantilesFrequencies nq = len(quantiles) limitingQuantiles = self.limitingQuantiles if ByObjectives: objectives = self.objectives for obj in objectives: print(objectives[obj]['name']) criteriaList = [g for g in criteria if criteria[g]['objective']==obj] if Sorted: criteriaList.sort() print('criteria | weights |', end=' ') for x in quantiles: print('\''+str(x)+'\' ', end=' ') print('\n---------|-----------------------------------------') for g in criteriaList: print(' \''+str(g)+'\' | '+str(criteria[g]['weight'])+' | ', end=' ') for i in range(nq): formatString = '%% .%df ' % ndigits if criteria[g]['preferenceDirection'] == 'max': value = min(criteria[g]['scale'][1],max(criteria[g]['scale'][0],limitingQuantiles[g][i])) else: if criteria[g]['weight'] > Decimal('0'): value = max(-criteria[g]['scale'][1],min(-criteria[g]['scale'][0],limitingQuantiles[g][i])) else: value = max(criteria[g]['scale'][0],min(criteria[g]['scale'][1],limitingQuantiles[g][i])) print(formatString % (value), end=' ') print() else: criteriaList = list(self.criteria) if sorted: criteriaList.sort() print('criteria | weights |', end=' ') for x in quantiles: print('\''+str(x)+'\' ', end=' ') print('\n---------|-----------------------------------------') for g in criteriaList: print(' \''+str(g)+'\' | '+str(self.criteria[g]['weight'])+' | ', end=' ') for i in range(nq): formatString = '%% .%df ' % ndigits if criteria[g]['preferenceDirection'] == 'max': value = min(criteria[g]['scale'][1],max(criteria[g]['scale'][0],limitingQuantiles[g][i])) else: if criteria[g]['weight'] > Decimal('0'): value = max(-criteria[g]['scale'][1],min(-criteria[g]['scale'][0],limitingQuantiles[g][i])) else: value = max(criteria[g]['scale'][0],min(criteria[g]['scale'][1],-limitingQuantiles[g][i])) print(formatString % (value), end=' ') print()
[docs] def showCriterionStatistics(self,g,Debug=False): """ show statistics concerning the evaluation distributions on each criteria. """ import math criteria = self.criteria qFreq = self.quantilesFrequencies nc = len(qFreq) frequencies = ['%.2f' % p for p in qFreq] glimitingQuantiles = self.limitingQuantiles[g] #actions = self.actions #n = len(actions) print('*-------- Performance Quantiles statistics -------*') print('Instance name :', self.name) print('Quantiles frequencies :', frequencies) print('Summary for criterion : %s' % g) print(' Criterion name : %s' % criteria[g]['name']) print(' Comment : %s' % self.criteria[g]['comment']) if criteria[g]['preferenceDirection'] == 'max': print(' Performance range : [%.2f-%.2f]'\ % (self.criteria[g]['scale'][0],self.criteria[g]['scale'][1]) ) else: print(' Performance range : [%.2f;%.2f]'\ % (-self.criteria[g]['scale'][1],-self.criteria[g]['scale'][0]) ) print(' Quantiles repartition :') print(' p%\t q(p)') for i in range(nc): print(' %.0f\t %.2f' % (qFreq[i]*100, glimitingQuantiles[i]))
## print(' mean evaluation : %.2f' % (averageEvaluation)) ## print(' standard deviation : %.2f' % (stdDevEvaluation)) ## print(' maximal evaluation : %.2f' % (maxEvaluation)) ## print(' quantile Q3 (x_75) : %.2f' % (quantileQ3)) ## print(' median evaluation : %.2f' % (quantileQ2)) ## print(' quantile Q1 (x_25) : %.2f' % (quantileQ1)) ## print(' minimal evaluation : %.2f' % (minEvaluation))
[docs] def updateQuantiles(self,newData,historySize=None,Debug=False): """ Update the PerformanceQuantiles with a set of new random decision actions. Parameter *historysize* allows to take more or less into account the historical situation. For instance, *historySize=0* does not take into account at all any past observations. Otherwise, if *historySize=None* (the default setting), the new observations become less and less influential compared to the historical data. """ ## if t is not None: ## self.historySizes = t try: newActions = newData['actions'] newEvaluation = newData['evaluation'] except: newActions = newData.actions newEvaluation = newData.evaluation NA = self.NA for g in self.criteria: gNewValues = [] gNewEvaluation = newEvaluation[g] for x in newActions: if gNewEvaluation[x] != NA: gNewValues.append(gNewEvaluation[x]) self._updateCriterionQuantiles(g,gNewValues,historySize=historySize,Debug=Debug)
## self.T += len(newActions) ##################################################################### ####################################################################### #----------test classes and methods ---------------- if __name__ == "__main__": print(""" **************************************************** * Digraph3 performanceQunatiles module * * depends on BipolarOutrankingDigraph and * * $Revision Python 3.9 $ * * Copyright (C) 2010-2021 Raymond Bisdorff * * The module comes with ABSOLUTELY NO WARRANTY * * to the extent permitted by the applicable law. * * This is free software, and you are welcome to * * redistribute it if it remains free software. * **************************************************** """) from performanceQuantiles import * seed = 100 nbrActions = 20 nbrCrit = 13 from randomPerfTabs import Random3ObjectivesPerformanceTableau from randomPerfTabs import RandomPerformanceGenerator as PerfTabGenerator nbrActions=nbrActions nbrCrit = nbrCrit ## tp = Random3ObjectivesPerformanceTableau(numberOfActions=nbrActions, ## NegativeWeights=False, ## negativeWeightProbability=0.1, ## numberOfCriteria=nbrCrit,seed=seed) from randomPerfTabs import RandomAcademicPerformanceTableau tp = RandomAcademicPerformanceTableau(numberOfStudents=100, numberOfCourses=11, WithTypes=True) pq = PerformanceQuantiles(tp,5,LowerClosed=True,Debug=False) #print(pq.actionsTypeStatistics) pq.showHTMLLimitingQuantiles(Transposed=True) #print(pq.limitingQuantiles) pq.showLimitingQuantiles(ByObjectives=False) pq1 = PerformanceQuantiles(tp,5,LowerClosed=False,Debug=False) #print(pq.actionsTypeStatistics) pq1.showHTMLLimitingQuantiles(Transposed=True) #print(pq.limitingQuantiles) pq1.showLimitingQuantiles(ByObjectives=False) print('*------------------*') print('If you see this line all tests were passed successfully :-)') print('Enjoy !')