2023-02-22 15:45:39 +00:00
#!/bin/python3
2023-02-23 13:37:45 +00: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-04-21 09:45:17 +00:00
from mplfinance . original_flavor import candlestick_ohlc
2023-04-23 14:55:31 +00:00
import os
2023-02-22 15:45:39 +00:00
class Visualizer :
2023-04-20 16:15:02 +00:00
def __init__ ( self , execID , config ) :
2023-02-22 15:45:39 +00:00
self . execID = execID
2023-04-20 16:15:02 +00:00
self . config = config
2023-02-22 15:45:39 +00:00
self . folderPath = " results/ " + self . execID
2023-03-30 11:24:30 +00:00
self . parameters = [ ' run ' , ' blockSize ' , ' failureRate ' , ' numberNodes ' , ' netDegree ' , ' chi ' , ' vpn1 ' , ' vpn2 ' , ' class1ratio ' , ' bwUplinkProd ' , ' bwUplink1 ' , ' bwUplink2 ' ]
2023-02-26 17:36:02 +00:00
self . minimumDataPoints = 2
2023-04-27 11:58:51 +00:00
self . maxTTA = 11000
2023-02-22 15:45:39 +00: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 13:37:45 +00:00
data = { }
2023-03-30 11:24:30 +00:00
bw = [ ]
2023-04-21 15:14:55 +00:00
print ( " Getting data from the folder... " )
2023-03-22 23:17:19 +00:00
""" Loop over the xml files in the folder """
2023-02-23 13:37:45 +00: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 13:37:45 +00: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 )
2023-03-13 14:03:55 +00:00
numberNodes = int ( root . find ( ' numberNodes ' ) . text )
2023-03-30 11:24:30 +00:00
class1ratio = float ( root . find ( ' class1ratio ' ) . text )
2023-02-23 13:37:45 +00:00
netDegree = int ( root . find ( ' netDegree ' ) . text )
chi = int ( root . find ( ' chi ' ) . text )
2023-03-13 14:00:43 +00:00
vpn1 = int ( root . find ( ' vpn1 ' ) . text )
vpn2 = int ( root . find ( ' vpn2 ' ) . text )
2023-03-07 13:36:13 +00:00
bwUplinkProd = int ( root . find ( ' bwUplinkProd ' ) . text )
bwUplink1 = int ( root . find ( ' bwUplink1 ' ) . text )
bwUplink2 = int ( root . find ( ' bwUplink2 ' ) . text )
2023-03-30 11:41:50 +00:00
tta = float ( root . find ( ' tta ' ) . text )
2023-03-30 11:24:30 +00:00
""" Store BW """
bw . append ( bwUplinkProd )
""" Loop over all possible combinations of length of the parameters minus x, y params """
2023-03-21 09:41:52 +00:00
for combination in combinations ( self . parameters , len ( self . parameters ) - 2 ) :
2023-02-23 13:37:45 +00:00
# Get the indices and values of the parameters in the combination
2023-03-26 13:01:07 +00:00
2023-02-23 13:37:45 +00:00
indices = [ self . parameters . index ( element ) for element in combination ]
2023-03-30 11:24:30 +00:00
selectedValues = [ run , blockSize , failureRate , numberNodes , netDegree , chi , vpn1 , vpn2 , class1ratio , bwUplinkProd , bwUplink1 , bwUplink2 ]
2023-02-23 13:37:45 +00:00
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 ) ]
2023-03-21 09:41:52 +00:00
key = tuple ( keyComponents [ : len ( self . parameters ) - 2 ] )
2023-03-30 11:24:30 +00:00
""" Get the names of the other parameters that are not included in the key """
2023-03-07 13:36:13 +00:00
otherParams = [ self . parameters [ i ] for i in range ( len ( self . parameters ) ) if i not in indices ]
2023-03-30 11:24:30 +00:00
""" Append the values of the other parameters and the ttas to the lists for the key """
2023-02-23 13:37:45 +00: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 13:37:45 +00:00
if key not in data :
data [ key ] = { }
2023-03-30 11:24:30 +00:00
""" Initialize lists for the other parameters and the ttas with the key """
2023-02-23 13:37:45 +00: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 15:45:39 +00:00
return data
2023-04-20 16:15:02 +00:00
2023-03-30 11:24:30 +00:00
def averageRuns ( self , data , runs ) :
2023-04-21 09:45:17 +00:00
""" Get the average of all runs for each key """
2023-03-22 23:17:19 +00:00
newData = { }
2023-04-20 19:53:25 +00:00
print ( " Getting the average of the runs... " )
2023-03-22 23:17:19 +00:00
for key , value in data . items ( ) :
runExists = False
""" Check if the key contains ' run_ ' with a numerical value """
for item in key :
2023-03-30 11:24:30 +00:00
if item . startswith ( ' run_ ' ) :
2023-03-22 23:17:19 +00:00
runExists = True
break
if runExists :
2023-04-25 21:32:31 +00:00
ps = list ( data [ key ] . keys ( ) )
2023-03-22 23:17:19 +00:00
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 ] )
2023-03-30 11:24:30 +00:00
""" Average the similar key values """
2023-04-25 21:32:31 +00:00
tta_sums = { }
2023-04-26 16:22:17 +00:00
nbRuns = { }
ttRuns = [ ]
2023-04-25 21:32:31 +00:00
total = [ ]
p0 = [ ]
p1 = [ ]
2023-04-26 16:22:17 +00:00
p2 = [ ]
p3 = [ ]
2023-03-30 11:24:30 +00:00
for i in range ( runs ) :
key0 = ( f ' run_ { i } ' , ) + newKey
2023-04-25 21:32:31 +00:00
#Create a dictionary to store the sums of ttas for each unique pair of values in subkeys
for i in range ( len ( data [ key0 ] [ ps [ 0 ] ] ) ) :
keyPair = ( data [ key0 ] [ ps [ 0 ] ] [ i ] , data [ key0 ] [ ps [ 1 ] ] [ i ] )
2023-04-26 16:22:17 +00:00
if data [ key0 ] [ " ttas " ] [ i ] == - 1 :
data [ key0 ] [ " ttas " ] [ i ] = self . maxTTA
2023-04-25 21:32:31 +00:00
try :
tta_sums [ keyPair ] + = data [ key0 ] [ ' ttas ' ] [ i ]
2023-04-26 16:22:17 +00:00
if data [ key0 ] [ " ttas " ] [ i ] != self . maxTTA :
nbRuns [ keyPair ] + = 1
2023-04-25 21:32:31 +00:00
except KeyError :
tta_sums [ keyPair ] = data [ key0 ] [ ' ttas ' ] [ i ]
2023-04-26 16:22:17 +00:00
if data [ key0 ] [ " ttas " ] [ i ] != self . maxTTA :
nbRuns [ keyPair ] = 1
else :
nbRuns [ keyPair ] = 0
2023-04-25 21:32:31 +00:00
for k , tta in tta_sums . items ( ) :
p0 . append ( k [ 0 ] )
p1 . append ( k [ 1 ] )
total . append ( tta )
2023-04-26 16:22:17 +00:00
for k , run in nbRuns . items ( ) :
p2 . append ( k [ 0 ] )
p3 . append ( k [ 1 ] )
ttRuns . append ( run )
2023-03-30 11:24:30 +00:00
for i in range ( len ( total ) ) :
2023-04-26 16:22:17 +00:00
if ( ttRuns [ i ] == 0 ) : # All tta = -1
2023-04-23 14:55:31 +00:00
total [ i ] = self . maxTTA
2023-04-26 16:22:17 +00:00
elif ttRuns [ i ] < runs : # Some tta = -1
total [ i ] - = ( runs - ttRuns [ i ] ) * self . maxTTA
total [ i ] = total [ i ] / ttRuns [ i ]
else : # No tta = -1
total [ i ] = total [ i ] / ttRuns [ i ]
2023-03-22 23:17:19 +00:00
averages = { }
2023-04-25 21:32:31 +00:00
averages [ ps [ 0 ] ] = p0
averages [ ps [ 1 ] ] = p1
averages [ ' ttas ' ] = total
2023-03-22 23:17:19 +00:00
newData [ newKey ] = averages
return newData
2023-02-22 15:45:39 +00: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 13:37:45 +00: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 15:45:39 +00:00
print ( " Getting filtered keys from data... " )
return filteredKeys
2023-02-26 17:36:02 +00:00
def formatLabel ( self , label ) :
2023-03-22 23:17:19 +00:00
""" Label formatting for the figures """
2023-02-26 17:36:02 +00:00
result = ' ' . join ( [ f " { char } " if char . isupper ( ) else char for char in label ] )
return result . title ( )
2023-03-21 09:41:52 +00:00
2023-02-23 13:37:45 +00:00
def formatTitle ( self , key ) :
2023-03-22 23:17:19 +00:00
""" Title formatting for the figures """
2023-02-23 13:37:45 +00: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 15:45:39 +00:00
def plotHeatmaps ( self ) :
2023-03-22 23:17:19 +00:00
""" Plot and store the 2D heatmaps in subfolders """
2023-03-30 11:24:30 +00:00
data = self . plottingData ( )
""" Average the runs if needed """
2023-04-20 16:15:02 +00:00
if ( len ( self . config . runs ) > 1 ) :
data = self . averageRuns ( data , len ( self . config . runs ) )
2023-02-22 15:45:39 +00:00
filteredKeys = self . similarKeys ( data )
2023-04-27 11:58:51 +00:00
vmin , vmax = 0 , self . maxTTA + 1000
2023-02-22 15:45:39 +00:00
print ( " Plotting heatmaps... " )
2023-04-20 16:15:02 +00:00
2023-03-22 23:17:19 +00:00
""" Create the directory if it doesn ' t exist already """
2023-02-23 13:37:45 +00: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 13:37:45 +00: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 17:36:02 +00:00
if len ( xlabels ) < self . minimumDataPoints or len ( ylabels ) < self . minimumDataPoints :
continue
2023-04-23 14:55:31 +00:00
hist , xedges , yedges = np . histogram2d ( data [ key ] [ labels [ 0 ] ] , data [ key ] [ labels [ 1 ] ] , bins = ( len ( xlabels ) , len ( ylabels ) ) , weights = data [ key ] [ ' ttas ' ] )
2023-02-23 13:37:45 +00:00
hist = hist . T
fig , ax = plt . subplots ( figsize = ( 10 , 6 ) )
2023-04-27 11:58:51 +00:00
sns . heatmap ( hist , xticklabels = xlabels , yticklabels = ylabels , cmap = ' hot_r ' , cbar_kws = { ' label ' : ' Time to block availability (ms) ' } , linecolor = ' black ' , linewidths = 0.3 , annot = True , fmt = " .2f " , ax = ax , vmin = vmin , vmax = vmax )
2023-02-26 17:36:02 +00:00
plt . xlabel ( self . formatLabel ( labels [ 0 ] ) )
plt . ylabel ( self . formatLabel ( labels [ 1 ] ) )
2023-02-23 13:37:45 +00: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 13:37:45 +00:00
filename + = f " { key [ paramValueCnt ] } "
formattedTitle = self . formatTitle ( key [ paramValueCnt ] )
title + = formattedTitle
2023-03-30 11:41:50 +00:00
if ( paramValueCnt + 1 ) % 5 == 0 :
title + = " \n "
2023-02-23 13:37:45 +00:00
paramValueCnt + = 1
2023-04-27 11:58:51 +00:00
title = " Time to Block Availability (ms) "
2023-02-23 13:37:45 +00:00
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 ( )
2023-04-20 16:15:02 +00:00
2023-03-30 11:24:30 +00:00
def plotHist ( self , bandwidth ) :
""" Plot Bandwidth Frequency Histogram """
plt . hist ( bandwidth , bins = 5 )
plt . xlabel ( ' Bandwidth ' )
plt . ylabel ( ' Frequency ' )
plt . title ( ' Bandwidth Histogram ' )
""" Create the directory if it doesn ' t exist already """
histogramFolder = self . folderPath + ' /histogram '
if not os . path . exists ( histogramFolder ) :
os . makedirs ( histogramFolder )
filename = os . path . join ( histogramFolder , ' histogram.png ' )
plt . savefig ( filename )
2023-04-20 16:15:02 +00:00
plt . clf ( )
2023-04-21 09:45:17 +00:00
def plotHist ( self , bandwidth ) :
""" Plot Bandwidth Frequency Histogram """
plt . hist ( bandwidth , bins = 5 )
plt . xlabel ( ' Bandwidth ' )
plt . ylabel ( ' Frequency ' )
plt . title ( ' Bandwidth Histogram ' )
""" Create the directory if it doesn ' t exist already """
histogramFolder = self . folderPath + ' /histogram '
if not os . path . exists ( histogramFolder ) :
os . makedirs ( histogramFolder )
filename = os . path . join ( histogramFolder , ' histogram.png ' )
plt . savefig ( filename )
plt . clf ( )
def plotCandleStick ( self , TX_prod , TX_avg , TX_max ) :
#x-axis corresponding to steps
steps = range ( len ( TX_prod ) )
#Plot the candlestick chart
ohlc = [ ]
for i in range ( len ( TX_prod ) ) :
ohlc . append ( [ steps [ i ] , TX_prod [ i ] , TX_max [ i ] , TX_avg [ i ] ] )
fig , ax = plt . subplots ( )
candlestick_ohlc ( ax , ohlc , width = 0.6 , colorup = ' green ' , colordown = ' red ' )
#Ticks, title and labels
plt . xticks ( steps , [ ' run {} ' . format ( i ) for i in steps ] , rotation = 45 )
plt . title ( ' Candlestick Chart ' )
plt . xlabel ( ' Step ' )
plt . ylabel ( ' Price ' )
#Test
2023-04-21 15:14:55 +00:00
plt . show ( )