11import copy
22import functools
33import itertools
4- from typing import Literal , Optional
4+ from typing import Literal
55
66import colorsys
77import matplotlib as mpl
1111import pandas as pd
1212from pandas .io .formats .style import Styler
1313
14- __all__ = ["visualization" , "tableau" , "palette" ]
14+ __all__ = ["visualization" , "two_output_visualization" , " tableau" , "palette" ]
1515
1616
1717SEQUENTIAL_PALETTES = [
@@ -135,25 +135,17 @@ def palette(
135135def visualization (
136136 * ,
137137 bins : pd .DataFrame ,
138- bins2 : Optional [pd .DataFrame ] = None ,
139138 palette : list [list [float ]],
140139 n_bins : str | int = "auto" ,
141140 kind : Literal ["histogram" , "boxplot" ] = "histogram" ,
142141 ax = None ,
143- output_name : str = "Output 1" ,
144- output_name2 : str = "Output 2" ,
145- xlim : Optional [tuple [float , float ]] = None ,
146- ylim : Optional [tuple [float , float ]] = None ,
147- r_scatter : float = 1.0 ,
148142) -> plt .Axes :
149143 """Histogram plot of scenarios.
150144
151145 Parameters
152146 ----------
153147 bins : DataFrame
154148 Multidimensional bins.
155- bins2 : DataFrame
156- Multidimensional bins for output 2
157149 palette : list of int of size (n, 4)
158150 List of colours corresponding to scenarios.
159151 n_bins : str or int
@@ -162,85 +154,16 @@ def visualization(
162154 Histogram or Box Plot.
163155 ax : Axes, optional
164156 Matplotlib axis.
165- output_name : str, default "Output 1"
166- Name of the primary output variable.
167- output_name2 : str, default "Output 2"
168- Name of the second output variable.
169- xlim : tuple of float, optional
170- Minimum and maximum values for the x-axis (Output 1).
171- ylim : tuple of float, optional
172- Minimum and maximum values for the y-axis (Output 2).
173- r_scatter : float, default 1.0
174- The portion of data points displayed on the scatter plot (0 to 1).
175157
176158 Returns
177159 -------
178- axs : Axes
179- Matplotlib axis for two-output graph.
180160 ax : Axes
181161 Matplotlib axis.
182162
183163 """
184164 # needed to get the correct stacking order
185165 bins .columns = pd .RangeIndex (start = len (bins .columns ), stop = 0 , step = - 1 )
186166
187- if bins2 is not None :
188- fig , axs = plt .subplots (2 , 2 , sharex = "col" , sharey = "row" , figsize = (8 , 8 ))
189- axs [0 , 1 ].axis ("off" )
190-
191- sns .histplot (
192- bins ,
193- multiple = "stack" ,
194- stat = "probability" ,
195- palette = palette ,
196- common_bins = True ,
197- common_norm = True ,
198- bins = n_bins ,
199- legend = False ,
200- ax = axs [0 , 0 ],
201- )
202- axs [0 , 0 ].set_xlim (xlim )
203- axs [0 , 0 ].set_box_aspect (1 )
204- axs [0 , 0 ].axis ("off" )
205-
206- data = pd .concat ([pd .melt (bins ), pd .melt (bins2 )["value" ]], axis = 1 )
207- data .columns = ["c" , "x" , "y" ]
208-
209- if r_scatter < 1.0 :
210- data = data .sample (frac = r_scatter )
211-
212- sns .scatterplot (
213- data = data ,
214- x = "x" ,
215- y = "y" ,
216- hue = "c" ,
217- palette = palette ,
218- ax = axs [1 , 0 ],
219- legend = False ,
220- )
221- axs [1 , 0 ].set (xlabel = output_name , ylabel = output_name2 )
222- axs [1 , 0 ].set_box_aspect (1 )
223-
224- sns .histplot (
225- data ,
226- y = "y" ,
227- hue = "c" ,
228- multiple = "stack" ,
229- stat = "probability" ,
230- palette = palette ,
231- common_bins = True ,
232- common_norm = True ,
233- bins = 40 ,
234- legend = False ,
235- ax = axs [1 , 1 ],
236- )
237- axs [1 , 1 ].set_ylim (ylim )
238- axs [1 , 1 ].set_box_aspect (1 )
239- axs [1 , 1 ].axis ("off" )
240-
241- fig .subplots_adjust (wspace = - 0.015 , hspace = 0 )
242- return axs [1 , 0 ]
243-
244167 if kind == "histogram" :
245168 ax = sns .histplot (
246169 bins ,
@@ -266,6 +189,105 @@ def visualization(
266189 return ax
267190
268191
192+ def two_output_visualization (
193+ * ,
194+ bins : pd .DataFrame ,
195+ bins2 : pd .DataFrame ,
196+ palette : list [list [float ]],
197+ n_bins : str | int = "auto" ,
198+ output_name : str = "Output 1" ,
199+ output_name2 : str = "Output 2" ,
200+ xlim : tuple [float , float ] | None = None ,
201+ ylim : tuple [float , float ] | None = None ,
202+ r_scatter : float = 1.0 ,
203+ ) -> tuple [plt .Figure , np .ndarray ]:
204+ """Two-output visualization.
205+ Produces a 2x2 figure
206+ * top-left : stacked histogram for *output 1* (axes hidden)
207+ * bottom-left : scatter of output 1 vs output 2, coloured by scenario
208+ * bottom-right: rotated stacked histogram for *output 2* (axes hidden)
209+ * top-right : empty
210+
211+ Parameters
212+ ----------
213+ bins : DataFrame
214+ Multidimensional bins for the primary output.
215+ bins2 : DataFrame
216+ Multidimensional bins for the secondary output.
217+ palette : list of int of size (n, 4)
218+ List of colours corresponding to scenarios.
219+ n_bins : str or int
220+ Number of bins for the histograms.
221+ output_name : str, default "Output 1"
222+ Axis label for the primary output.
223+ output_name2 : str, default "Output 2"
224+ Axis label for the secondary output.
225+ xlim : tuple of float, optional
226+ Limits for the primary output axis (scatter x / top histogram).
227+ ylim : tuple of float, optional
228+ Limits for the secondary output axis (scatter y / right histogram).
229+ r_scatter : float, default 1.0
230+ Fraction of data points shown in the scatter plot.
231+
232+ Returns
233+ -------
234+ fig : Figure
235+ axs : ndarray of shape (2, 2)
236+
237+ """
238+ fig , axs = plt .subplots (2 , 2 , sharex = "col" , sharey = "row" , figsize = (8 , 8 ))
239+
240+ axs [0 , 1 ].axis ("off" )
241+
242+ visualization (bins = bins .copy (), palette = palette , n_bins = n_bins , ax = axs [0 , 0 ])
243+ if xlim is not None :
244+ axs [0 , 0 ].set_xlim (xlim )
245+ axs [0 , 0 ].set_box_aspect (1 )
246+ axs [0 , 0 ].axis ("off" )
247+
248+ data = pd .concat ([pd .melt (bins ), pd .melt (bins2 )["value" ]], axis = 1 )
249+ data .columns = ["c" , "x" , "y" ]
250+ if r_scatter < 1.0 :
251+ data = data .sample (frac = r_scatter )
252+
253+ sns .scatterplot (
254+ data = data ,
255+ x = "x" ,
256+ y = "y" ,
257+ hue = "c" ,
258+ palette = palette ,
259+ ax = axs [1 , 0 ],
260+ legend = False ,
261+ )
262+ axs [1 , 0 ].set (xlabel = output_name , ylabel = output_name2 )
263+ if xlim is not None :
264+ axs [1 , 0 ].set_xlim (xlim )
265+ if ylim is not None :
266+ axs [1 , 0 ].set_ylim (ylim )
267+ axs [1 , 0 ].set_box_aspect (1 )
268+
269+ sns .histplot (
270+ data ,
271+ y = "y" ,
272+ hue = "c" ,
273+ multiple = "stack" ,
274+ stat = "probability" ,
275+ palette = palette ,
276+ common_bins = True ,
277+ common_norm = True ,
278+ bins = 40 ,
279+ legend = False ,
280+ ax = axs [1 , 1 ],
281+ )
282+ if ylim is not None :
283+ axs [1 , 1 ].set_ylim (ylim )
284+ axs [1 , 1 ].set_box_aspect (1 )
285+ axs [1 , 1 ].axis ("off" )
286+
287+ fig .subplots_adjust (wspace = - 0.015 , hspace = 0 )
288+ return fig , axs
289+
290+
269291def tableau (
270292 * ,
271293 var_names : list [str ],
0 commit comments