@@ -139,84 +139,137 @@ def markdown_display(df: pd.DataFrame) -> None:
139
139
140
140
141
141
class DataFramePlot :
142
- """Class to plot a dataframe ."""
142
+ """Class to plot a data frame ."""
143
143
144
144
def plot_2dataframes (
145
145
self ,
146
146
args_plot : PlotAPI ,
147
147
df_1 : pd .DataFrame ,
148
148
df_2 : Optional [pd .DataFrame ] = None ,
149
149
) -> None :
150
- """Plot of two dataframes .
150
+ """Plot two data frames .
151
151
152
152
!!! info "About the plot"
153
153
154
- The plot is a combination of two plots. The first plot is the
155
- can be the residual plot of a fit or the _modified_ data. The second
156
- plot can be the fit or the original data.
154
+ The plot is a combination of two plots. The first plot can be the
155
+ residual plot of a fit or the _modified_ data. The second plot can be the
156
+ fit or the original data.
157
157
158
158
!!! missing "`line_dash_map`"
159
159
160
160
Currently, the `line_dash_map` is not working, and the dash is not
161
- plotted. Most likely, this is related to the fact that the columns
162
- are not labeled in the dataframe .
161
+ plotted. This is likely due to the columns not being labeled in the
162
+ data frame .
163
163
164
164
Args:
165
165
args_plot (PlotAPI): PlotAPI object for the settings of the plot.
166
- df_1 (pd.DataFrame): First dataframe to plot, which will generate
167
- automatically a fit plot with residual plot. The ratio is 70% to 20%
168
- with 10% space in between.
169
- df_2 (Optional[pd.DataFrame], optional): Second optional dataframe to
170
- plot for comparsion . In this case, the ratio will between first
171
- and second plot will be same. Defaults to None.
166
+ df_1 (pd.DataFrame): First data frame to plot, which will generate
167
+ a fit plot with residual plot. The ratio is 70% to 20% with
168
+ 10% space in between.
169
+ df_2 (Optional[pd.DataFrame], optional): Second optional data frame to
170
+ plot for comparison . In this case, the ratio between the first
171
+ and second plot will be the same. Defaults to None.
172
172
"""
173
173
if df_2 is None :
174
- _fig1 = px .line (
175
- df_1 ,
176
- x = ColumnNamesAPI ().energy ,
177
- y = ColumnNamesAPI ().residual ,
178
- color_discrete_sequence = [args_plot .color .residual ],
179
- )
180
- _y = df_1 .columns .drop ([ColumnNamesAPI ().energy , ColumnNamesAPI ().residual ])
181
- _fig2 = px .line (
182
- df_1 ,
183
- x = ColumnNamesAPI ().energy ,
184
- y = _y ,
185
- color_discrete_map = {
186
- ColumnNamesAPI ().intensity : args_plot .color .intensity ,
187
- ColumnNamesAPI ().fit : args_plot .color .fit ,
188
- ** {
189
- key : args_plot .color .components
190
- for key in _y .drop (
191
- [ColumnNamesAPI ().intensity , ColumnNamesAPI ().fit ]
192
- )
193
- },
194
- },
195
- line_dash_map = {
196
- ColumnNamesAPI ().intensity : "solid" ,
197
- ColumnNamesAPI ().fit : "longdash" ,
198
- ** {
199
- key : "dash"
200
- for key in _y .drop (
201
- [ColumnNamesAPI ().intensity , ColumnNamesAPI ().fit ]
202
- )
203
- },
204
- },
205
- )
174
+ fig = self ._plot_single_dataframe (args_plot , df_1 )
206
175
else :
207
- _fig1 = px .line (df_1 , x = args_plot .x , y = args_plot .y )
208
- _fig2 = px .line (df_2 , x = args_plot .x , y = args_plot .y )
176
+ fig = self ._plot_two_dataframes (args_plot , df_1 , df_2 )
177
+
178
+ fig .show (
179
+ config = {
180
+ "toImageButtonOptions" : {
181
+ "format" : "png" ,
182
+ "filename" : "plot_of_2_dataframes" ,
183
+ "scale" : 4 ,
184
+ }
185
+ }
186
+ )
187
+
188
+ def _plot_single_dataframe (self , args_plot : PlotAPI , df : pd .DataFrame ) -> Figure :
189
+ """Plot a single data frame with residuals."""
190
+ fig = make_subplots (
191
+ rows = 2 , cols = 1 , shared_xaxes = True , shared_yaxes = True , vertical_spacing = 0.05
192
+ )
209
193
194
+ residual_fig = self ._create_residual_plot (df , args_plot )
195
+ fit_fig = self ._create_fit_plot (df , args_plot )
196
+
197
+ for trace in residual_fig ["data" ]:
198
+ fig .add_trace (trace , row = 1 , col = 1 )
199
+ for trace in fit_fig ["data" ]:
200
+ fig .add_trace (trace , row = 2 , col = 1 )
201
+
202
+ self ._update_plot_layout (fig , args_plot , df_2_provided = False )
203
+ return fig
204
+
205
+ def _plot_two_dataframes (
206
+ self , args_plot : PlotAPI , df_1 : pd .DataFrame , df_2 : pd .DataFrame
207
+ ) -> Figure :
208
+ """Plot two data frames for comparison."""
210
209
fig = make_subplots (
211
210
rows = 2 , cols = 1 , shared_xaxes = True , shared_yaxes = True , vertical_spacing = 0.05
212
211
)
213
212
214
- for _spec_1 in _fig1 ["data" ]:
215
- fig .append_trace (_spec_1 , row = 1 , col = 1 )
216
- for _spec_2 in _fig2 ["data" ]:
217
- fig .append_trace (_spec_2 , row = 2 , col = 1 )
213
+ fig1 = px .line (df_1 , x = args_plot .x , y = args_plot .y )
214
+ fig2 = px .line (df_2 , x = args_plot .x , y = args_plot .y )
215
+
216
+ for trace in fig1 ["data" ]:
217
+ fig .add_trace (trace , row = 1 , col = 1 )
218
+ for trace in fig2 ["data" ]:
219
+ fig .add_trace (trace , row = 2 , col = 1 )
220
+
221
+ self ._update_plot_layout (fig , args_plot , df_2_provided = True )
222
+ return fig
223
+
224
+ def _create_residual_plot (self , df : pd .DataFrame , args_plot : PlotAPI ) -> Figure :
225
+ """Create the residual plot."""
226
+ return px .line (
227
+ df ,
228
+ x = ColumnNamesAPI ().energy ,
229
+ y = ColumnNamesAPI ().residual ,
230
+ color_discrete_sequence = [args_plot .color .residual ],
231
+ )
232
+
233
+ def _create_fit_plot (self , df : pd .DataFrame , args_plot : PlotAPI ) -> Figure :
234
+ """Create the fit plot."""
235
+ y_columns = df .columns .drop (
236
+ [ColumnNamesAPI ().energy , ColumnNamesAPI ().residual ]
237
+ )
238
+ color_map = {
239
+ ColumnNamesAPI ().intensity : args_plot .color .intensity ,
240
+ ColumnNamesAPI ().fit : args_plot .color .fit ,
241
+ ** {
242
+ key : args_plot .color .components
243
+ for key in y_columns .drop (
244
+ [ColumnNamesAPI ().intensity , ColumnNamesAPI ().fit ]
245
+ )
246
+ },
247
+ }
248
+ line_dash_map = {
249
+ ColumnNamesAPI ().intensity : "solid" ,
250
+ ColumnNamesAPI ().fit : "longdash" ,
251
+ ** {
252
+ key : "dash"
253
+ for key in y_columns .drop (
254
+ [ColumnNamesAPI ().intensity , ColumnNamesAPI ().fit ]
255
+ )
256
+ },
257
+ }
258
+ return px .line (
259
+ df ,
260
+ x = ColumnNamesAPI ().energy ,
261
+ y = y_columns ,
262
+ color_discrete_map = color_map ,
263
+ line_dash_map = line_dash_map ,
264
+ )
265
+
266
+ def _update_plot_layout (
267
+ self , fig : Figure , args_plot : PlotAPI , df_2_provided : bool
268
+ ) -> None :
269
+ """Update the plot layout."""
218
270
height = args_plot .size [1 ][0 ]
219
271
self .update_layout_axes (fig , args_plot , height )
272
+
220
273
xaxis_title = self .title_text (
221
274
name = args_plot .xaxis_title .name , unit = args_plot .xaxis_title .unit
222
275
)
@@ -226,7 +279,8 @@ def plot_2dataframes(
226
279
227
280
fig .update_xaxes (title_text = xaxis_title , row = 1 , col = 1 )
228
281
fig .update_xaxes (title_text = xaxis_title , row = 2 , col = 1 )
229
- if df_2 is None :
282
+
283
+ if not df_2_provided :
230
284
residual_title = self .title_text (
231
285
name = args_plot .residual_title .name , unit = args_plot .residual_title .unit
232
286
)
@@ -235,25 +289,20 @@ def plot_2dataframes(
235
289
fig .update_yaxes (title_text = residual_title , row = 1 , col = 1 )
236
290
else :
237
291
fig .update_yaxes (title_text = yaxis_title , row = 1 , col = 1 )
292
+
238
293
fig .update_yaxes (title_text = yaxis_title , row = 2 , col = 1 )
239
- fig .show (
240
- config = {
241
- "toImageButtonOptions" : dict (
242
- format = "png" , filename = "plot_of_2_dataframes" , scale = 4
243
- )
244
- }
245
- )
246
294
247
295
def plot_dataframe (self , args_plot : PlotAPI , df : pd .DataFrame ) -> None :
248
- """Plot the dataframe according to the PlotAPI arguments.
296
+ """Plot the data frame according to the PlotAPI arguments.
249
297
250
298
Args:
251
299
args_plot (PlotAPI): PlotAPI object for the settings of the plot.
252
- df (pd.DataFrame): Dataframe to plot.
300
+ df (pd.DataFrame): Data frame to plot.
253
301
"""
254
302
fig = px .line (df , x = args_plot .x , y = args_plot .y )
255
303
height = args_plot .size [1 ][0 ]
256
304
self .update_layout_axes (fig , args_plot , height )
305
+
257
306
fig .update_xaxes (
258
307
title_text = self .title_text (
259
308
name = args_plot .xaxis_title .name , unit = args_plot .xaxis_title .unit
@@ -266,34 +315,33 @@ def plot_dataframe(self, args_plot: PlotAPI, df: pd.DataFrame) -> None:
266
315
)
267
316
fig .show (
268
317
config = {
269
- "toImageButtonOptions" : dict (
270
- format = "png" , filename = "plot_dataframe" , scale = 4
271
- )
318
+ "toImageButtonOptions" : {
319
+ "format" : "png" ,
320
+ "filename" : "plot_dataframe" ,
321
+ "scale" : 4 ,
322
+ }
272
323
}
273
324
)
274
325
275
326
def plot_global_fit (self , args_plot : PlotAPI , df : pd .DataFrame ) -> None :
276
- """Plot the global dataframe according to the PlotAPI arguments.
327
+ """Plot the global data frame according to the PlotAPI arguments.
277
328
278
329
Args:
279
330
args_plot (PlotAPI): PlotAPI object for the settings of the plot.
280
- df (pd.DataFrame): Dataframe to plot.
331
+ df (pd.DataFrame): Data frame to plot.
281
332
"""
282
- for i in range (
283
- 1 ,
284
- sum (bool (_col .startswith (ColumnNamesAPI ().fit )) for _col in df .columns ) + 1 ,
285
- ):
286
- _col = [col for col in df .columns if col .endswith (str (i ))]
287
- _col .append (ColumnNamesAPI ().energy )
288
- _df = df [_col ]
289
- _df = _df .rename (
333
+ num_fits = df .columns .str .startswith (ColumnNamesAPI ().fit ).sum ()
334
+ for i in range (1 , num_fits + 1 ):
335
+ cols = [col for col in df .columns if col .endswith (f"_{ i } " )]
336
+ cols .append (ColumnNamesAPI ().energy )
337
+ df_subset = df [cols ].rename (
290
338
columns = {
291
339
f"{ ColumnNamesAPI ().intensity } _{ i } " : ColumnNamesAPI ().intensity ,
292
340
f"{ ColumnNamesAPI ().fit } _{ i } " : ColumnNamesAPI ().fit ,
293
341
f"{ ColumnNamesAPI ().residual } _{ i } " : ColumnNamesAPI ().residual ,
294
342
}
295
343
)
296
- self .plot_2dataframes (args_plot , _df )
344
+ self .plot_2dataframes (args_plot , df_subset )
297
345
298
346
def plot_metric (
299
347
self ,
@@ -306,28 +354,32 @@ def plot_metric(
306
354
307
355
Args:
308
356
args_plot (PlotAPI): PlotAPI object for the settings of the plot.
309
- df_metric (pd.DataFrame): Metric dataframe to plot.
310
- bar_criteria (Union[str, List[str]]): String or list of criteria to plot as
311
- bars.
312
- line_criteria (Union[str, List[str]]): String or l of criteria to plot as
313
- lines.
357
+ df_metric (pd.DataFrame): Metric data frame to plot.
358
+ bar_criteria (Union[str, List[str]]): Criteria to plot as bars.
359
+ line_criteria (Union[str, List[str]]): Criteria to plot as lines.
314
360
"""
315
361
fig = make_subplots (specs = [[{"secondary_y" : True }]])
316
- _fig_bar = px .bar (
362
+ fig_bar = px .bar (
317
363
df_metric ,
318
364
y = bar_criteria ,
319
365
color_discrete_sequence = args_plot .color .bars ,
320
366
)
321
- _fig_line = px .line (
367
+ fig_line = px .line (
322
368
df_metric ,
323
369
y = line_criteria ,
324
370
color_discrete_sequence = args_plot .color .lines ,
325
371
)
326
- _fig_line .update_traces (mode = "lines+markers" , yaxis = "y2" )
327
- fig .add_traces (_fig_bar .data + _fig_line .data )
372
+ fig_line .update_traces (mode = "lines+markers" , yaxis = "y2" )
373
+
374
+ for trace in fig_bar .data :
375
+ fig .add_trace (trace )
376
+ for trace in fig_line .data :
377
+ fig .add_trace (trace )
378
+
328
379
fig .update_layout (xaxis_type = "category" )
329
380
height = args_plot .size [1 ][1 ]
330
381
self .update_layout_axes (fig , args_plot , height )
382
+
331
383
fig .update_xaxes (
332
384
title_text = self .title_text (
333
385
name = args_plot .run_title .name , unit = args_plot .run_title .unit
@@ -347,9 +399,11 @@ def plot_metric(
347
399
)
348
400
fig .show (
349
401
config = {
350
- "toImageButtonOptions" : dict (
351
- format = "png" , filename = "plot_metric" , scale = 4
352
- )
402
+ "toImageButtonOptions" : {
403
+ "format" : "png" ,
404
+ "filename" : "plot_metric" ,
405
+ "scale" : 4 ,
406
+ }
353
407
}
354
408
)
355
409
@@ -378,16 +432,17 @@ def update_layout_axes(
378
432
plot_bgcolor = args_plot .color .plot ,
379
433
)
380
434
435
+ minor_ticks = self .get_minor (args_plot )
436
+
381
437
fig .update_xaxes (
382
- minor = self . get_minor ( args_plot = args_plot ) ,
438
+ minor = minor_ticks ,
383
439
gridcolor = args_plot .color .grid ,
384
440
linecolor = args_plot .color .line ,
385
441
zerolinecolor = args_plot .color .zero_line ,
386
442
color = args_plot .color .color ,
387
443
)
388
-
389
444
fig .update_yaxes (
390
- minor = self . get_minor ( args_plot = args_plot ) ,
445
+ minor = minor_ticks ,
391
446
gridcolor = args_plot .color .grid ,
392
447
linecolor = args_plot .color .line ,
393
448
zerolinecolor = args_plot .color .zero_line ,
@@ -406,7 +461,7 @@ def title_text(name: str, unit: Optional[str] = None) -> str:
406
461
Returns:
407
462
str: Title text.
408
463
"""
409
- return name if unit is None else f"{ name } [{ unit } ]"
464
+ return f"{ name } [{ unit } ]" if unit else name
410
465
411
466
def get_minor (self , args_plot : PlotAPI ) -> Dict [str , Union [str , bool ]]:
412
467
"""Get the minor axis arguments.
@@ -417,12 +472,12 @@ def get_minor(self, args_plot: PlotAPI) -> Dict[str, Union[str, bool]]:
417
472
Returns:
418
473
Dict[str, Union[str, bool]]: Dictionary with the minor axis arguments.
419
474
"""
420
- return dict (
421
- tickcolor = args_plot .color .ticks ,
422
- showgrid = args_plot .grid .show ,
423
- ticks = args_plot .grid .ticks ,
424
- griddash = args_plot .grid .dash ,
425
- )
475
+ return {
476
+ " tickcolor" : args_plot .color .ticks ,
477
+ " showgrid" : args_plot .grid .show ,
478
+ " ticks" : args_plot .grid .ticks ,
479
+ " griddash" : args_plot .grid .dash ,
480
+ }
426
481
427
482
428
483
class ExportResults :
0 commit comments