2023-02-22 16:45:39 +01:00
#!/bin/python3
2023-02-23 14:37:45 +01:00
import os , sys
import time
import xml . etree . ElementTree as ET
import matplotlib . pyplot as plt
import numpy as np
import seaborn as sns
from itertools import combinations
2023-02-22 16:45:39 +01:00
class Visualizer :
def __init__ ( self , execID ) :
self . execID = execID
self . folderPath = " results/ " + self . execID
2023-02-23 14:37:45 +01:00
self . parameters = [ ' run ' , ' blockSize ' , ' failureRate ' , ' numberValidators ' , ' netDegree ' , ' chi ' ]
2023-02-26 18:36:02 +01:00
self . minimumDataPoints = 2
2023-02-22 16:45:39 +01:00
def plottingData ( self ) :
2023-03-22 23:17:19 +00:00
""" Store data with a unique key for each params combination """
2023-02-23 14:37:45 +01:00
data = { }
2023-03-22 23:17:19 +00:00
""" Loop over the xml files in the folder """
2023-02-23 14:37:45 +01:00
for filename in os . listdir ( self . folderPath ) :
2023-03-22 23:17:19 +00:00
""" Loop over the xmls and store the data in variables """
2023-02-23 14:37:45 +01:00
if filename . endswith ( ' .xml ' ) :
tree = ET . parse ( os . path . join ( self . folderPath , filename ) )
root = tree . getroot ( )
run = int ( root . find ( ' run ' ) . text )
blockSize = int ( root . find ( ' blockSize ' ) . text )
failureRate = int ( root . find ( ' failureRate ' ) . text )
numberValidators = int ( root . find ( ' numberValidators ' ) . text )
netDegree = int ( root . find ( ' netDegree ' ) . text )
chi = int ( root . find ( ' chi ' ) . text )
tta = int ( root . find ( ' tta ' ) . text )
2023-03-22 23:17:19 +00:00
""" Loop over all possible combinations of length 4 of the parameters """
2023-02-23 14:37:45 +01:00
for combination in combinations ( self . parameters , 4 ) :
2023-03-22 23:17:19 +00:00
""" Get the indices and values of the parameters in the combination """
2023-02-23 14:37:45 +01:00
indices = [ self . parameters . index ( element ) for element in combination ]
selectedValues = [ run , blockSize , failureRate , numberValidators , netDegree , chi ]
values = [ selectedValues [ index ] for index in indices ]
names = [ self . parameters [ i ] for i in indices ]
keyComponents = [ f " { name } _ { value } " for name , value in zip ( names , values ) ]
key = tuple ( keyComponents [ : 4 ] )
2023-03-22 23:17:19 +00:00
""" Get the names of the other 2 parameters that are not included in the key """
2023-02-23 14:37:45 +01:00
otherParams = [ self . parameters [ i ] for i in range ( 6 ) if i not in indices ]
2023-03-22 23:17:19 +00:00
""" Append the values of the other 2 parameters and the ttas to the lists for the key """
2023-02-23 14:37:45 +01:00
otherIndices = [ i for i in range ( len ( self . parameters ) ) if i not in indices ]
2023-03-22 23:17:19 +00:00
""" Initialize the dictionary for the key if it doesn ' t exist yet """
2023-02-23 14:37:45 +01:00
if key not in data :
data [ key ] = { }
2023-03-22 23:17:19 +00:00
""" Initialize lists for the other 2 parameters and the ttas with the key """
2023-02-23 14:37:45 +01:00
data [ key ] [ otherParams [ 0 ] ] = [ ]
data [ key ] [ otherParams [ 1 ] ] = [ ]
data [ key ] [ ' ttas ' ] = [ ]
if otherParams [ 0 ] in data [ key ] :
data [ key ] [ otherParams [ 0 ] ] . append ( selectedValues [ otherIndices [ 0 ] ] )
else :
data [ key ] [ otherParams [ 0 ] ] = [ selectedValues [ otherIndices [ 0 ] ] ]
if otherParams [ 1 ] in data [ key ] :
data [ key ] [ otherParams [ 1 ] ] . append ( selectedValues [ otherIndices [ 1 ] ] )
else :
data [ key ] [ otherParams [ 1 ] ] = [ selectedValues [ otherIndices [ 1 ] ] ]
data [ key ] [ ' ttas ' ] . append ( tta )
2023-02-22 16:45:39 +01:00
print ( " Getting data from the folder... " )
return data
2023-03-22 23:17:19 +00:00
def averageRuns ( self , data ) :
""" Get the average of run 0 and run 1 for each key """
newData = { }
for key , value in data . items ( ) :
runExists = False
""" Check if the key contains ' run_ ' with a numerical value """
for item in key :
if item . startswith ( ' run_0 ' ) :
runExists = True
break
if runExists :
for item in key :
""" Create a new key with the other items in the tuple """
if item . startswith ( ' run_ ' ) :
newKey = tuple ( [ x for x in key if x != item ] )
key0 = ( ' run_0 ' , ) + newKey
data0 = data [ key0 ]
key1 = ( ' run_1 ' , ) + newKey
data1 = data [ key1 ]
averages = { }
for key in data0 . keys ( ) :
if key in data1 :
values = [ ]
for i in range ( len ( data0 [ key ] ) ) :
value = ( data0 [ key ] [ i ] + data1 [ key ] [ i ] ) / 2.0
if isinstance ( data0 [ key ] [ i ] , int ) and isinstance ( data1 [ key ] [ i ] , int ) and key != ' ttas ' :
value = int ( value )
values . append ( value )
averages [ key ] = values
newData [ newKey ] = averages
print ( " Getting the average of the runs... " )
return newData
2023-02-22 16:45:39 +01:00
def similarKeys ( self , data ) :
2023-03-22 23:17:19 +00:00
""" Get the keys for all data with the same x and y labels """
2023-02-23 14:37:45 +01:00
filteredKeys = { }
for key1 , value1 in data . items ( ) :
subKeys1 = list ( value1 . keys ( ) )
filteredKeys [ ( subKeys1 [ 0 ] , subKeys1 [ 1 ] ) ] = [ key1 ]
for key2 , value2 in data . items ( ) :
subKeys2 = list ( value2 . keys ( ) )
if key1 != key2 and subKeys1 [ 0 ] == subKeys2 [ 0 ] and subKeys1 [ 1 ] == subKeys2 [ 1 ] :
try :
filteredKeys [ ( subKeys1 [ 0 ] , subKeys1 [ 1 ] ) ] . append ( key2 )
except KeyError :
filteredKeys [ ( subKeys1 [ 0 ] , subKeys1 [ 1 ] ) ] = [ key2 ]
2023-02-22 16:45:39 +01:00
print ( " Getting filtered keys from data... " )
return filteredKeys
2023-02-26 18:36:02 +01:00
def formatLabel ( self , label ) :
2023-03-22 23:17:19 +00:00
""" Label formatting for the figures """
2023-02-26 18:36:02 +01:00
result = ' ' . join ( [ f " { char } " if char . isupper ( ) else char for char in label ] )
return result . title ( )
2023-02-23 14:37:45 +01:00
def formatTitle ( self , key ) :
2023-03-22 23:17:19 +00:00
""" Title formatting for the figures """
2023-02-23 14:37:45 +01:00
name = ' ' . join ( [ f " { char } " if char . isupper ( ) else char for char in key . split ( ' _ ' ) [ 0 ] ] )
number = key . split ( ' _ ' ) [ 1 ]
return f " { name . title ( ) } : { number } "
2023-02-22 16:45:39 +01:00
def plotHeatmaps ( self ) :
2023-03-22 23:17:19 +00:00
""" Plot and store the 2D heatmaps in subfolders """
2023-02-22 16:45:39 +01:00
data = self . plottingData ( )
2023-03-22 23:17:19 +00:00
data = self . averageRuns ( data )
2023-02-22 16:45:39 +01:00
filteredKeys = self . similarKeys ( data )
print ( " Plotting heatmaps... " )
2023-02-23 14:37:45 +01:00
2023-03-22 23:17:19 +00:00
""" Create the directory if it doesn ' t exist already """
2023-02-23 14:37:45 +01:00
heatmapsFolder = self . folderPath + ' /heatmaps '
if not os . path . exists ( heatmapsFolder ) :
os . makedirs ( heatmapsFolder )
2023-03-22 23:17:19 +00:00
""" Plot """
2023-02-23 14:37:45 +01:00
for labels , keys in filteredKeys . items ( ) :
for key in keys :
xlabels = np . sort ( np . unique ( data [ key ] [ labels [ 0 ] ] ) )
ylabels = np . sort ( np . unique ( data [ key ] [ labels [ 1 ] ] ) )
2023-02-26 18:36:02 +01:00
if len ( xlabels ) < self . minimumDataPoints or len ( ylabels ) < self . minimumDataPoints :
continue
2023-02-23 14:37:45 +01:00
hist , xedges , yedges = np . histogram2d ( data [ key ] [ labels [ 0 ] ] , data [ key ] [ labels [ 1 ] ] , bins = ( len ( xlabels ) , len ( ylabels ) ) , weights = data [ key ] [ ' ttas ' ] )
hist = hist . T
fig , ax = plt . subplots ( figsize = ( 10 , 6 ) )
sns . heatmap ( hist , xticklabels = xlabels , yticklabels = ylabels , cmap = ' Purples ' , cbar_kws = { ' label ' : ' Time to block availability ' } , linecolor = ' black ' , linewidths = 0.3 , annot = True , fmt = " .2f " , ax = ax )
2023-02-26 18:36:02 +01:00
plt . xlabel ( self . formatLabel ( labels [ 0 ] ) )
plt . ylabel ( self . formatLabel ( labels [ 1 ] ) )
2023-02-23 14:37:45 +01:00
filename = " "
title = " "
paramValueCnt = 0
for param in self . parameters :
2023-03-22 23:17:19 +00:00
if param != labels [ 0 ] and param != labels [ 1 ] and param != ' run ' :
2023-02-23 14:37:45 +01:00
filename + = f " { key [ paramValueCnt ] } "
formattedTitle = self . formatTitle ( key [ paramValueCnt ] )
title + = formattedTitle
paramValueCnt + = 1
title_obj = plt . title ( title )
font_size = 16 * fig . get_size_inches ( ) [ 0 ] / 10
title_obj . set_fontsize ( font_size )
filename = filename + " .png "
targetFolder = os . path . join ( heatmapsFolder , f " { labels [ 0 ] } Vs { labels [ 1 ] } " )
if not os . path . exists ( targetFolder ) :
os . makedirs ( targetFolder )
plt . savefig ( os . path . join ( targetFolder , filename ) )
plt . close ( )
plt . clf ( )