1- from typing import List
2-
31import json
2+ from typing import List , Optional
3+
44import pandas as pd
5- from matplotlib .figure import Figure
5+ from matplotlib import pyplot as plt
6+ from matplotlib import rc , rcParams , style
67from matplotlib .axes import Axes
7- from matplotlib import pyplot as plt , rc , rcParams , style
8+ from matplotlib . figure import Figure
89
910ANTARES_STEP = "antares"
1011PROBLEM_GENERATION_STEP = "problem_generation"
@@ -56,13 +57,15 @@ def stylize_data_for_display(self):
5657 "resolution.stopping_criterion.type" ,
5758 "resolution.stopping_criterion.value" ,
5859 ]
59- stylized_data = self .processed_data .loc [(slice (None ), 1.0 ), columns_to_display ]
60+ stylized_data = self .processed_data .loc [(slice (None ), 1.1 ), columns_to_display ]
6061 stylized_data .index = stylized_data .index .droplevel (1 )
6162 return stylized_data
6263
6364
6465class PerfPlotsGenerator :
65- def __init__ (self , perf_data : pd .DataFrame ) -> None :
66+ def __init__ (
67+ self , perf_data : pd .DataFrame , xpansion_versions : Optional [List [int ]] = None
68+ ) -> None :
6669 self .perf_data = perf_data
6770
6871 self .fig : Figure
@@ -72,69 +75,111 @@ def __init__(self, perf_data: pd.DataFrame) -> None:
7275 rc ("font" , ** {"family" : "serif" })
7376 rcParams .update ({"font.size" : 16 })
7477
75- def _xpansion_versions (self ) -> List [float ]:
76- return self .perf_data .index .unique (level = "version" ).tolist ()
78+ self .xpansion_versions = self ._xpansion_versions (xpansion_versions )
7779
78- def _create_fig (self ) -> None :
80+ def _xpansion_versions (self , xpansion_versions : Optional [List [str ]]) -> List [float ]:
81+ valid_versions = self .perf_data .index .unique (level = "version" ).tolist ()
82+ if xpansion_versions is None :
83+ # If no version is specified by the user, return all versions present in the data
84+ return valid_versions
85+ else :
86+ return_versions = []
87+ for version in xpansion_versions :
88+ if version in valid_versions :
89+ return_versions .append (version )
90+ return return_versions
7991
80- nb_versions = len (self ._xpansion_versions ())
81- fig , ax = plt .subplots (
82- 1 , nb_versions , sharey = True , figsize = (5 * nb_versions , 8 )
83- )
92+ def _create_fig (self ) -> None :
93+ nb_versions = self ._get_nb_versions ()
94+ fig , ax = plt .subplots (1 , 1 , figsize = (8 , 3 * nb_versions ))
8495
8596 self .fig = fig
8697 self .ax = ax
98+ self .ax .invert_yaxis ()
99+
100+ def _get_nb_versions (self ):
101+ return len (self .xpansion_versions )
87102
88103 def _display_names (self ) -> List [str ]:
89104 return self .perf_data .index .unique (level = "display_name" ).tolist ()
90105
91106 def _beautify_fig (self ) -> None :
92- self .fig .subplots_adjust (bottom = 0.2 )
93- self .fig .suptitle ("Xpansion performance evolution" )
94-
95- self .fig .supylabel ("Execution time (s)" )
96- self .fig .supxlabel ("Study id" )
97- self .ax [- 1 ].legend ()
98-
99- for axes in self .ax :
100- axes .set_xticks (self ._display_names ())
101- axes .set_xticklabels (self ._display_names (), rotation = 90 )
107+ self .ax .set_yticks (
108+ list (range (len (self ._display_names ()))),
109+ [study for study in self ._display_names ()],
110+ )
111+ self .ax .set_ylim (self .ax .get_ylim ()[0 ], 1.5 * self .ax .get_ylim ()[1 ])
112+ self .ax .legend (loc = "upper center" , ncol = 3 , fontsize = 12 )
113+ self .ax .set_title ("Xpansion performance evolution" )
102114
115+ self .ax .set_xlabel ("Execution time (s)" )
103116 self .fig .tight_layout ()
104117
105- def _plot_single_version (self , version_count : int , xpansion_version : float ) -> None :
106- antares_step_times = self .perf_data .loc [
107- (slice (None ), xpansion_version ), ANTARES_STEP
108- ].values
109- problem_generation_step_times = self .perf_data .loc [
110- (slice (None ), xpansion_version ), PROBLEM_GENERATION_STEP
111- ].values
112- benders_step_times = self .perf_data .loc [
113- (slice (None ), xpansion_version ), BENDERS_STEP
114- ].values
115-
116- self .ax [version_count ].bar (
117- self ._display_names (), antares_step_times , label = ANTARES_STEP
118- )
119- self .ax [version_count ].bar (
120- self ._display_names (),
121- problem_generation_step_times ,
122- bottom = antares_step_times ,
123- label = PROBLEM_GENERATION_STEP ,
124- )
125- self .ax [version_count ].bar (
126- self ._display_names (),
127- benders_step_times ,
128- bottom = problem_generation_step_times + antares_step_times ,
129- label = BENDERS_STEP ,
130- )
131- self .ax [version_count ].set_title (f"Version { xpansion_version } " )
118+ def _plot_study_data (self ) -> None :
119+
120+ nb_versions = self ._get_nb_versions ()
121+ height = 0.8 / nb_versions # Defines space between different study data
122+ epsilon = 0.03 # Defines space between bars of the data for the different version of the same study
123+ actual_height = (1 - epsilon ) * height
124+ alpha_decrease_rate = 0.2 # Defines transparency difference between bars of the data for the different version of the same study
125+
126+ for count , study in enumerate (self ._display_names ()):
127+ for version_cnt , xpansion_version in enumerate (self .xpansion_versions ):
128+ antares_time = self .perf_data .loc [
129+ (study , xpansion_version ), ANTARES_STEP
130+ ]
131+ pb_gen_time = self .perf_data .loc [
132+ (study , xpansion_version ), PROBLEM_GENERATION_STEP
133+ ]
134+ benders_time = self .perf_data .loc [
135+ (study , xpansion_version ), BENDERS_STEP
136+ ]
137+
138+ y_pos = self ._bar_y_position (nb_versions , height , count , version_cnt )
139+
140+ antares_line = self .ax .barh (
141+ y_pos ,
142+ antares_time ,
143+ height = actual_height ,
144+ color = "C0" ,
145+ align = "edge" ,
146+ alpha = 1 - alpha_decrease_rate * version_cnt ,
147+ )
148+ pb_gen_line = self .ax .barh (
149+ y_pos ,
150+ pb_gen_time ,
151+ height = actual_height ,
152+ left = antares_time ,
153+ color = "C1" ,
154+ align = "edge" ,
155+ alpha = 1 - alpha_decrease_rate * version_cnt ,
156+ )
157+ benders_line = self .ax .barh (
158+ y_pos ,
159+ benders_time ,
160+ left = pb_gen_time + antares_time ,
161+ height = actual_height ,
162+ color = "C2" ,
163+ align = "edge" ,
164+ alpha = 1 - alpha_decrease_rate * version_cnt ,
165+ )
166+ self .ax .bar_label (
167+ benders_line ,
168+ [f"v{ xpansion_version } : { antares_time + pb_gen_time + benders_time } s" ],
169+ label_type = "edge" ,
170+ fontsize = 12 ,
171+ )
172+ if version_cnt == 0 and count == 0 :
173+ antares_line .set_label (ANTARES_STEP )
174+ pb_gen_line .set_label (PROBLEM_GENERATION_STEP )
175+ benders_line .set_label (BENDERS_STEP )
176+
177+ def _bar_y_position (
178+ self , nb_versions : int , height : float , count : int , version_cnt : int
179+ ):
180+ return count + height * (version_cnt - nb_versions / 2 )
132181
133182 def run (self ) -> None :
134-
135183 self ._create_fig ()
136-
137- for version_count , xpansion_version in enumerate (self ._xpansion_versions ()):
138- self ._plot_single_version (version_count , xpansion_version )
139-
184+ self ._plot_study_data ()
140185 self ._beautify_fig ()
0 commit comments