pandas_GUI.plot_Pandas_GUI
1def plot_pandas_GUI(df_info=None, show_text_col = False, **kwargs): 2 """ 3 If passed no parameters this will look for all the dataframes in the user 4 namespace and make them available for plotting. Once a 5 dataframe is chosen only the numerical columns from that dataframe will 6 be available for inclusion in the plotting expression. 7 8 This GUI produces code to use the plotly interactive plotting package. 9 10 If you wish to allow only certain dataframes or have them show up as 11 user friendly names in the menus provide that information in the first 12 paramater df_info. 13 14 To allow inclusion of text columns pass True for show_text_col. 15 16 :param bool show_text_col: (default = False). When True columns 17 containing text will be shown. 18 19 :param list df_info: List of Lists [[object,globalname,userfriendly]],..] 20 * object -- pandas.DataFrame 21 * globalname -- string name of the object in the user global name space. 22 * userfriendly -- string name to display for user selection. 23 24 :keyword string figname: string used to override default python name for 25 figure. 26 27 :keyword bool findframes: default = True. If set to false and dataframes 28 are passed in dfs_info, will not search for dataframes in the user 29 namespace. 30 """ 31 from ipywidgets import Layout, Box, HBox, VBox, GridBox, Tab, \ 32 Accordion, Dropdown, Label, Text, Button, Checkbox, FloatText, \ 33 RadioButtons, BoundedIntText, Output 34 from ipywidgets import HTML as richLabel 35 from IPython.display import display, HTML 36 from IPython.display import Javascript as JS 37 from IPython import get_ipython 38 from JPSLUtils.utils import new_cell_immediately_below,\ 39 select_cell_immediately_below, move_cursor_in_current_cell, \ 40 insert_text_into_next_cell, insert_text_at_beginning_of_current_cell, \ 41 insert_newline_at_end_of_current_cell, select_containing_cell, \ 42 delete_selected_cell, iconselector, notice_group, notebookenv, \ 43 replace_text_of_next_cell 44 45 from .utils import find_pandas_dataframe_names, build_run_snip_widget 46 from IPython import get_ipython 47 global_dict = get_ipython().user_ns 48 JPSLUtils = global_dict["JPSLUtils"] 49 dfs_info = [] 50 if isinstance(df_info,list): 51 for k in df_info: 52 dfs_info.append(k) 53 findframes = kwargs.pop('findframes',True) 54 if findframes: 55 for k in find_pandas_dataframe_names(): 56 dfs_info.append([global_dict[k],k,k]) 57 friendly_to_globalname = {k[2]:k[1] for k in dfs_info} 58 friendly_to_object = {k[2]:k[0] for k in dfs_info} 59 60 figname = kwargs.pop('figname',None) 61 from .utils import find_figure_names 62 figlst = find_figure_names() 63 if figname in figlst: 64 raise UserWarning (str(figname) + ' already exists. Choose a ' 65 'different name for the figure.') 66 if figname == None: 67 figname = 'Figure_'+str(len(figlst)+1) 68 69 #### Define GUI Elements #### 70 # Those followed by a * are required. 71 output = Output() 72 with output: 73 display(HTML( 74 "<h3 id ='pandasplotGUI' style='text-align:center;'>Pandas Plot " 75 "Composer</h3> <div style='text-align:center;'>" 76 "<span style='color:green;'>Steps with a * are required.</span> The " 77 "code that will generate the plot is being " 78 "built in the cell immediately below.</div><div " 79 "style='text-align:center;'>This composer uses a subset of " 80 "<a href ='https://plotly.com/python/line-and-scatter/#'> " 81 "the plotly scatter plot</a> capabilities.</div>")) 82 83 longdesc = {'description_width':'initial'} 84 85 # Notices for the Final Check Tab. 86 makeplot_notices = notice_group(['At least one trace required.', 87 'Axes must have labels.'], 88 'Notices:','','red') 89 makeplot_notices.set_active([0,1]) 90 91 # Strings for each tab, that are combined to make the code. 92 importstr = '# CODE BLOCK generated using plot_pandas_GUI().\n# See ' \ 93 'https://jupyterphysscilab.github.io/jupyter_Pandas_GUI.\n' \ 94 'from plotly import graph_objects as go\n' + str(figname) + \ 95 ' = go.FigureWidget(layout_template=\"simple_white\")\n' 96 step1strdefault = '\n# Trace declaration(s) and trace formatting\n' 97 step1str = step1strdefault 98 step2strdefault = '\n# Axes labels\n' 99 step2str = step2strdefault 100 step3strdefault = '\n# Plot formatting\n' 101 step3str = step3strdefault 102 # 1. Pick Traces* 103 # a. Select Y vs. X pairs* (DataFrame, X and Y, which must be from single 104 # frame. 105 # Notices for the Pick Trace(s) tab. 106 notice_list = [ 107 'Data set (DataFrame) required.', 108 'X- and Y-coordinates required.', 109 'Incomplete or inconsistent error specification(s).', 110 'Non-default trace formatting left from previous trace.', 111 'X-Error settings left from previous trace.', 112 'Y-Error settings left from previous trace.', 113 ] 114 trace_notices = notice_group(notice_list, 'Notices:','','red') 115 step1instr = richLabel(value = 'For each trace you wish to include: ' 116 '<ol><li>Select a DataFrame (Data ' 117 'set);</li>' 118 '<li>Select the column containing the X ' 119 'values;</li>' 120 '<li>Select the column containing the Y ' 121 'values;</li>' 122 '<li>Provide a name for the trace if you do' 123 ' not like the default. This text will be ' 124 'used for the legend;</li>' 125 '<li> OPTIONAL - set additional formatting ' 126 'and error display by expanding the ' 127 'sections at the bottom of this tab;</li>' 128 '<li>Once everything is set use the ' 129 '<b>"Add Trace"</b> button to ' 130 'include it in your plot.</li></ol>') 131 132 step1instracc = Accordion(children = [step1instr]) 133 step1instracc.set_title(0,'Instructions') 134 step1instracc.selected_index = None 135 136 # DataFrame selection 137 tempopts = [] 138 tempopts.append('Choose data set.') 139 for k in dfs_info: 140 tempopts.append(k[2]) 141 whichframe = Dropdown(options=tempopts, 142 description='DataFrame: ',) 143 144 def update_columns(change): 145 df = friendly_to_object[change['new']] 146 tempcols = df.columns.values 147 if change['new'] == 'Choose data set.': 148 Xcoord.disabled = True 149 Ycoord.disabled = True 150 add_trace_but.disabled = True 151 add_trace_but.button_style = '' 152 trace_notices.activate_notice(0) 153 trace_notices.activate_notice(1) 154 add_trace_notices.value = trace_notices.notice_html() 155 return 156 tempopt = ['Choose column for coordinate.'] 157 for k in tempcols: 158 if show_text_col: 159 tempopt.append(k) 160 else: 161 if df[k].dtype != 'O': 162 tempopt.append(k) 163 Xcoord.options = tempopt 164 Xcoord.value = tempopt[0] 165 Ycoord.options = tempopt 166 Ycoord.value = tempopt[0] 167 Xcoord.disabled = False 168 Ycoord.disabled = False 169 trace_notices.activate_notice(1) 170 trace_notices.deactivate_notice(0) 171 add_trace_notices.value =trace_notices.notice_html() 172 pass 173 whichframe.observe(update_columns, names='value') 174 175 # Data selection 176 Xcoord = Dropdown(options=['Choose X-coordinate.'], 177 description='X: ', 178 disabled = True) 179 Ycoord = Dropdown(options=['Choose Y-coordinate.'], 180 description='Y: ', 181 disabled = True) 182 def trace_name_update(change): 183 if change['new'] != 'Choose column for coordinate.': 184 trace_name.value = Ycoord.value 185 if Xcoord.value != 'Choose column for coordinate.' and Ycoord.value \ 186 != 'Choose column for coordinate.': 187 add_trace_but.disabled = False 188 add_trace_but.button_style = 'success' 189 modedrop.disabled = False 190 colordrop.disabled = False 191 yerrtype.disabled = False 192 xerrtype.disabled = False 193 trace_name.disabled = False 194 trace_notices.deactivate_notice(1) 195 add_trace_notices.value = trace_notices.notice_html() 196 else: 197 add_trace_but.disabled = True 198 add_trace_but.button_style = '' 199 modedrop.disabled = True 200 colordrop.disabled = True 201 yerrtype.disabled = True 202 xerrtype.disabled = True 203 trace_name.disabled = True 204 trace_notices.activate_notice(1) 205 add_trace_notices.value = trace_notices.notice_html() 206 pass 207 208 Ycoord.observe(trace_name_update,names='value') 209 210 # Trace name 211 trace_name = Text(placeholder = 'Trace name for legend', 212 description = 'Trace name: ', 213 disabled = True) 214 215 # b. Trace Style (optional) 216 def trace_format_update(change): 217 trace_notices.deactivate_notice(3) 218 add_trace_notices.value = trace_notices.notice_html() 219 pass 220 221 modedrop = Dropdown(options = ['lines','markers','lines+markers'], 222 description = 'Trace Style: ') 223 modedrop.observe(trace_format_update) 224 colordrop = Dropdown(options=['default','blue','orange','green','purple', 225 'red','gold','brown','black'], 226 description = 'Color: ') 227 colordrop.observe(trace_format_update) 228 iconlist = ['circle', 'square', 'caret-up', 'star', 'plus', 'times', 229 'caret-down', 'caret-left', 'caret-right'] 230 icontoplotly = {'circle': 'circle', 'square': 'square', 231 'caret-up': 'triangle-up', 'plus': 'cross', 232 'times': 'x', 'caret-down': 'triangle-down', 233 'caret-left': 'triangle-left', 'caret-right': 234 'triangle-right', 'star': 'star'} 235 markerlabel = Label('Marker Choices: ') 236 marker_selector = iconselector(iconlist, selected = 'circle') 237 filled_open = Checkbox(value = True, 238 description = 'Filled (uncheck for open)', 239 style={'description_width':'initial'}) 240 filled_open.observe(trace_format_update) 241 markersize = BoundedIntText(value = 6, min = 2, max = 25, step = 1, 242 description = 'Marker Size (px): ', 243 style=longdesc) 244 markersize.observe(trace_format_update) 245 markerhbox = HBox(children=[markerlabel,filled_open,markersize]) 246 markervbox = VBox(children=[markerhbox, marker_selector.box]) 247 line_style = Dropdown(options = ['solid','dot','dash','dashdot'], 248 description = 'Line style: ') 249 line_style.observe(trace_format_update) 250 line_width = BoundedIntText(value = 2, min = 1, max = 25, step = 1, 251 description = 'Linewidth (px): ', 252 style=longdesc) 253 line_width.observe(trace_format_update) 254 linehbox = HBox(children=[line_style,line_width]) 255 256 formatHbox1 = HBox(children=[modedrop,colordrop]) 257 formatVbox = VBox(children=[formatHbox1,linehbox,markervbox]) 258 step1formatacc = Accordion(children=[formatVbox]) 259 step1formatacc.set_title(0,'Trace Formatting') 260 step1formatacc.selected_index = 0 261 yerrtype = Dropdown(options = ['none','percent','constant','data'], 262 description = 'Error Type: ', 263 disabled = True) 264 265 def error_settings_OK(): 266 check = True 267 if (yerrtype.value == 'data') and (yerrdata.value == 'Choose error ' 268 'column.'): 269 check = False 270 if (xerrtype.value == 'data') and (xerrdata.value == 'Choose error ' 271 'column.'): 272 check = False 273 if (yerrtype.value == 'none' or yerrtype.value == 'percent' or 274 yerrtype.value == 'constant') and (xerrtype.value == 'none' or 275 xerrtype.value == 'percent' 276 or xerrtype.value =='constant'): 277 check = True 278 return check 279 280 def yerr_change(change): 281 df = friendly_to_object[whichframe.value] 282 if change['new'] == 'percent' or change['new'] == 'constant': 283 yerrvalue.disabled = False 284 yerrdata.disabled = True 285 if change['new'] == 'data': 286 yerrvalue.disabled = True 287 if yerrdata.value == 'Choose error column.': 288 add_trace_but.disabled = True 289 add_trace_but.button_style = '' 290 tempopts = ['Choose error column.'] 291 tempcols = df.columns.values 292 for k in tempcols: 293 if df[k].dtype != 'O': 294 tempopts.append(k) 295 yerrdata.options=tempopts 296 yerrdata.disabled = False 297 if change['new'] == 'none': 298 yerrvalue.disabled = True 299 yerrdata.disabled = True 300 if error_settings_OK(): 301 add_trace_but.disabled = False 302 add_trace_but.button_style = 'success' 303 trace_notices.deactivate_notice(2) 304 else: 305 add_trace_but.disabled = True 306 add_trace_but.button_style = '' 307 trace_notices.activate_notice(2) 308 trace_notices.deactivate_notice(5) 309 add_trace_notices.value = trace_notices.notice_html() 310 pass 311 312 yerrtype.observe(yerr_change, names = 'value') 313 314 yerrvalue = FloatText(description = '% or constant: ', disabled = True, 315 style=longdesc) 316 yerrdata = Dropdown(options = ['Choose error column.'], 317 description = 'Error values: ', 318 disabled = True) 319 320 def errdata_change(change): 321 if error_settings_OK(): 322 add_trace_but.disabled = False 323 add_trace_but.button_style = 'success' 324 trace_notices.deactivate_notice(2) 325 else: 326 add_trace_but.disabled = True 327 add_trace_but.button_style = '' 328 trace_notices.activate_notice(2) 329 add_trace_notices.value = trace_notices.notice_html() 330 pass 331 332 yerrdata.observe(errdata_change, names = 'value') 333 yerrrow1 = HBox(children=[yerrtype,yerrvalue]) 334 yerror = VBox(children=[yerrrow1,yerrdata]) 335 xerrtype = Dropdown(options = ['none','percent','constant','data'], 336 description = 'Error Type: ', 337 disabled = True) 338 def xerr_change(change): 339 df = friendly_to_object[whichframe.value] 340 if change['new'] == 'percent' or change['new'] == 'constant': 341 xerrvalue.disabled = False 342 xerrdata.disabled = True 343 if change['new'] == 'data': 344 xerrvalue.disabled = True 345 if xerrdata.value == 'Choose error column.': 346 add_trace_but.disabled = True 347 add_trace_but.button_style = '' 348 tempopts = ['Choose error column.'] 349 tempcols = df.columns.values 350 for k in tempcols: 351 if df[k].dtype != 'O': 352 tempopts.append(k) 353 xerrdata.options = tempopts 354 xerrdata.disabled = False 355 if change['new'] == 'none': 356 xerrvalue.disabled = True 357 xerrdata.disabled = True 358 if error_settings_OK(): 359 add_trace_but.disabled = False 360 add_trace_but.button_style = 'success' 361 trace_notices.deactivate_notice(2) 362 else: 363 add_trace_but.disabled = True 364 add_trace_but.button_style = '' 365 trace_notices.activate_notice(2) 366 trace_notices.deactivate_notice(4) 367 add_trace_notices.value = trace_notices.notice_html() 368 pass 369 370 xerrtype.observe(xerr_change, names = 'value') 371 xerrvalue = FloatText(description = '% or constant: ', disabled = True, 372 style=longdesc) 373 xerrdata = Dropdown(options = ['Choose error column.'], 374 description = 'Error values: ', 375 disabled = True) 376 377 xerrdata.observe(errdata_change, names = 'value') 378 xerrrow1 = HBox(children=[xerrtype,xerrvalue]) 379 xerror = VBox(children=[xerrrow1,xerrdata]) 380 step1erracc = Accordion(children = [yerror,xerror]) 381 step1erracc.set_title(0, 'Y error bars') 382 step1erracc.set_title(1, 'X error bars') 383 step1erracc.selected_index = None 384 385 # Add Trace button 386 add_trace_but = Button(description = 'Add Trace', 387 disabled = True) 388 def do_add_trace(change): 389 nonlocal importstr, step1str, step2str, step3str 390 dfname = friendly_to_globalname[whichframe.value] 391 text = 'scat = go.Scatter(x = '+dfname+'[\'' \ 392 +Xcoord.value+'\'],' 393 text += ' y = ' +dfname+'[\''+Ycoord.value+ \ 394 '\'],\n' 395 text += ' mode = \''+modedrop.value+'\', name = \'' \ 396 +trace_name.value+'\',' 397 # in here add other formatting items using ifs. 398 if colordrop.value != 'default': 399 text +='\n ' 400 if str(modedrop.value).find('lines') > -1: 401 text += 'line_color = \''+colordrop.value+'\', ' 402 if str(modedrop.value).find('markers') > -1: 403 text += 'marker_color = \'' + colordrop.value + '\', ' 404 if str(modedrop.value).find('lines') > -1: 405 if line_style.value != 'solid': 406 text +='\n ' 407 text +='line_dash=\'' + line_style.value + '\', ' 408 if line_width.value != 2: 409 text +='\n ' 410 text +='line_width=' + str(line_width.value) + ', ' 411 if str(modedrop.value).find('markers') > -1: 412 if markersize.value != 6: 413 text += '\n ' 414 text += 'marker_size=' + str(markersize.value) + ', ' 415 tmpmkr = icontoplotly[marker_selector.value] 416 if not filled_open.value: 417 text += '\n ' 418 tmpmkr +='-open' 419 text += 'marker_symbol=\'' + tmpmkr + '\', ' 420 else: 421 if tmpmkr != 'circle': 422 text += '\n ' 423 text += 'marker_symbol=\'' + tmpmkr + '\', ' 424 if yerrtype.value != 'none': 425 text +='\n ' 426 if yerrtype.value == 'data': 427 text += 'error_y_type=\'data\', ' \ 428 'error_y_array='+dfname 429 text += '[\''+yerrdata.value+'\'],' 430 else: 431 text += 'error_y_type=\''+yerrtype.value+'\', error_y_value=' 432 text += str(yerrvalue.value)+',' 433 if xerrtype.value != 'none': 434 text +='\n ' 435 if xerrtype.value == 'data': 436 text += 'error_x_type=\'data\', ' \ 437 'error_x_array='+dfname 438 text += '[\''+xerrdata.value+'\'],' 439 else: 440 text += 'error_x_type=\''+xerrtype.value+'\', error_x_value=' 441 text += str(xerrvalue.value)+',' 442 text += ')\n' 443 text += figname + '.add_trace(scat)\n' 444 step1str += text 445 if JPSLUtils.notebookenv == 'NBClassic': 446 replace_text_of_next_cell(importstr+step1str+step2str+step3str) 447 else: 448 codearea.sniptext.value = importstr+step1str+step2str+step3str 449 if (modedrop.value != 'lines') or (colordrop.value != 'default') or \ 450 (line_style.value != 'solid') or (line_width.value != 2) or \ 451 (icontoplotly[marker_selector.value] != 'circle') or \ 452 (not filled_open.value) or (markersize.value != 6): 453 trace_notices.activate_notice(3) 454 if (xerrtype.value != 'none'): 455 trace_notices.activate_notice(4) 456 if (yerrtype.value != 'none'): 457 trace_notices.activate_notice(5) 458 add_trace_notices.value = trace_notices.notice_html() 459 makeplot_notices.deactivate_notice(0) 460 step4noticebox.value = makeplot_notices.notice_html() 461 pass 462 add_trace_but.on_click(do_add_trace) 463 464 trace_notices.set_active([0,1]) 465 add_trace_notices = richLabel(value = trace_notices.notice_html()) 466 step1tracebox = VBox(children=[whichframe,Xcoord,Ycoord,trace_name]) 467 step1actionbox = VBox(children=[add_trace_but, add_trace_notices]) 468 step1hbox = HBox(children=[step1tracebox,step1actionbox]) 469 step1optbox = VBox(children=[step1formatacc, step1erracc]) 470 step1opt = Accordion(children = [step1optbox]) 471 step1opt.set_title(0, 'Optional (Trace formatting, error bars...)') 472 step1opt.selected_index = None 473 step1 = VBox(children=[step1instracc, step1hbox, step1opt]) 474 475 # 2. Set Axes Labels (will use column names by default). 476 step2instr = richLabel(value = '<span style = "font-weight:bold;">You must ' 477 'set the axes labels to something ' 478 'appropriate.</span> For example if the X - values ' 479 'represent time in seconds "Time (s)" is a good ' 480 'choice. If the Y - values for the traces all ' 481 'have the same units using the units as the label ' 482 'is a good choice. If the Y - values ' 483 'have different unit quantites the best ' 484 'option is probably "values" and making ' 485 'sure that the trace names used for the ' 486 'legend contain the units for each trace.') 487 step2instracc = Accordion(children = [step2instr]) 488 step2instracc.set_title(0,'Instructions') 489 step2instracc.selected_index = None 490 X_label = Text(placeholder = 'Provide an X-axis label (usually has units)', 491 description = 'X-axis label: ', 492 style = longdesc, 493 layout=Layout(width='45%')) 494 Y_label = Text(placeholder = 'Provide a Y-axis label (usually has units)', 495 description = 'Y-axis label: ', 496 style = longdesc, 497 layout=Layout(width='45%')) 498 step2hbox = HBox(children=[X_label,Y_label]) 499 step2 = VBox(children=[step2instracc,step2hbox]) 500 # 3.Title, Format ... 501 step3instr = richLabel(value='Overall format set here. <ul><li>Experiment ' 502 'with ' 503 'the Plot Styling templates to find your ' 504 'favorite.</li>' 505 '<li>If the Aspect Ratio is set to `auto` the ' 506 'figure will fill the default output region. ' 507 'Other choices will allow you to pick the Plot ' 508 'Size. `Large` will use about 2/3 of an HD ' 509 '(1920X1080) screen.</li>') 510 step3instracc = Accordion(children=[step3instr]) 511 step3instracc.set_title(0, 'Instructions') 512 step3instracc.selected_index = None 513 plot_title = Text(value = figname, 514 description = 'Plot title: ', 515 layout = Layout(width='80%')) 516 def mirror_axes_change(change): 517 if change['new']: 518 mirror_ticks.disabled= False 519 else: 520 mirror_ticks.disabled= True 521 mirror_ticks.value = False 522 pass 523 524 mirror_axes = Checkbox(value = False, 525 description = 'Display Mirror Axes', 526 style = longdesc) 527 mirror_axes.observe(mirror_axes_change, names = 'value') 528 mirror_ticks = Checkbox(value = False, 529 description = 'Mirror Tick Marks', 530 disabled = True) 531 plot_template = Dropdown(options=['none','simple_white', 'ggplot2', 532 'seaborn', 533 'plotly', 'plotly_white', 'plotly_dark', 534 'presentation', 'xgridoff', 'ygridoff', 535 'gridon', 'simple_white+presentation', 536 'simple_white+gridon', 537 'simple_white+presentation+gridon'], 538 value='simple_white', 539 description = 'Plot Styling: ', 540 style = longdesc) 541 def aspect_change(change): 542 if change['new'] != 'auto': 543 plot_size.disabled=False 544 else: 545 plot_size.disabled=True 546 pass 547 548 plot_aspect = Dropdown(options = ['auto', '16:9', '5:3', '7:5', '4:3', 549 '10:8', '1:1'], 550 value = 'auto', 551 description = 'Aspect Ratio: ', 552 style = longdesc) 553 plot_aspect.observe(aspect_change, names = 'value') 554 plot_size = Dropdown(options = ['tiny', 'small', 'medium', 'large', 555 'huge'], 556 value = 'large', 557 description = 'Plot Size: ', 558 style = longdesc, 559 disabled = True) 560 step3hbox2 = HBox(children=[mirror_axes,mirror_ticks, plot_template, 561 plot_aspect, plot_size]) 562 step3 = VBox(children=[step3instracc, plot_title, step3hbox2]) 563 564 # 4. Final Check* 565 step4instr = richLabel(value = 'Things to check before clicking making ' \ 566 'the plot: <ul>' \ 567 '<li>Fix any problems listed in ' \ 568 '"Notices".</li>' \ 569 '<li>Look at the code below to make sure ' \ 570 'you have included all the traces you ' \ 571 'intended to (look for "name").</li>' \ 572 '<li>Check for any unpaired parentheses, ' \ 573 'brackets or braces.</li>' \ 574 '<li>Check that all single and double ' \ 575 'quotes are paired.</li>' \ 576 '<li>If you did any manual editing ' \ 577 'double-check for typos.</li>') 578 step4noticebox = richLabel(value = makeplot_notices.notice_html()) 579 def makeplt_click(change): 580 if JPSLUtils.notebookenv == 'NBClassic': 581 # These commented out lines do nothing because of timing issues. 582 # text = '\n# Force save widget states so that graph will still be\n' 583 # text += '# available when notebook next opened in trusted state.\n' 584 # text += 'import time\ntime.sleep(5)' 585 select_containing_cell('pandasplotGUI') 586 select_cell_immediately_below() 587 # insert_newline_at_end_of_current_cell(text) 588 # jscode = 'Jupyter.actions.call("widgets:save-with-widgets");' 589 # text = 'JPSLUtils.OTJS(\''+jscode+'\')' 590 # insert_newline_at_end_of_current_cell(text) 591 # run the cell to build the plot 592 JPSLUtils.OTJS('Jupyter.notebook.get_selected_cell().execute();') 593 # remove the GUI cell 594 select_containing_cell('pandasplotGUI') 595 delete_selected_cell() 596 pass 597 598 makeplotbut_lay = Layout(visibility="hidden") 599 if JPSLUtils.notebookenv == 'NBClassic': 600 makeplotbut_lay = Layout(visibility="visible") 601 602 makeplotbut = Button(description = 'Make Plot', 603 layout = makeplotbut_lay, 604 disabled = True) 605 makeplotbut.on_click(makeplt_click) 606 step4vbox = VBox(children=[makeplotbut,step4noticebox]) 607 step4 = HBox(children=[step4instr,step4vbox]) 608 609 610 steps = Tab(children=[step1, step2, step3, step4]) 611 steps.set_title(0,'1. Pick Trace(s)*') 612 steps.set_title(1,'2. Label Axes*') 613 steps.set_title(2,'3. Title, Format ...') 614 steps.set_title(3,'4. Final Check*') 615 def tab_changed(change): 616 nonlocal importstr, step1strdefault, step1str, step2strdefault, \ 617 step2str, step3strdefault, step3str 618 if change['new'] ==3: 619 if X_label.value == '' or Y_label.value == '': 620 makeplot_notices.activate_notice(1) 621 makeplotbut.disabled = True 622 makeplotbut.button_style = '' 623 else: 624 makeplot_notices.deactivate_notice(1) 625 step4noticebox.value = makeplot_notices.notice_html() 626 if len(makeplot_notices.get_active()) == 0: 627 makeplotbut.disabled = False 628 makeplotbut.button_style = 'success' 629 if change['new'] > 1: 630 # update step2str 631 step2str = step2strdefault 632 text = figname + '.update_xaxes(title= \'' + X_label.value + '\'' 633 634 def get_mirror_text(): 635 if mirror_axes.value: 636 mirror_text = ', mirror = True)\n' 637 if mirror_ticks.value: 638 mirror_text = ', mirror= \'ticks\')\n' 639 else: 640 mirror_text = ')\n' 641 return mirror_text 642 643 text += get_mirror_text() 644 step2str += text 645 text = figname + '.update_yaxes(title= \'' + Y_label.value + '\'' 646 text += get_mirror_text() 647 step2str += text 648 # update step3str 649 step3str = step3strdefault 650 plot_width = 1200 651 plot_height = 675 652 if plot_title.value != '' or plot_template.value != 'simple_white': 653 text = figname + '.update_layout(title = \'' + plot_title.value + '\', ' 654 text += 'template = \'' + plot_template.value + '\', ' 655 if plot_aspect.value == 'auto': 656 text += 'autosize=True)\n' 657 else: 658 if plot_size.value == 'tiny': 659 plot_width = 300 660 elif plot_size.value == 'small': 661 plot_width = 450 662 elif plot_size.value == 'medium': 663 plot_width = 800 664 elif plot_size.value == 'large': 665 plot_width = 1200 666 elif plot_size.value == 'huge': 667 plot_width = 2400 668 if plot_aspect.value == '16:9': 669 plot_height = int(9 * plot_width / 16) 670 elif plot_aspect.value == '5:3': 671 plot_height = int(3 * plot_width / 5) 672 elif plot_aspect.value == '7:5': 673 plot_height = int(5 * plot_width / 7) 674 elif plot_aspect.value == '4:3': 675 plot_height = int(3 * plot_width / 4) 676 elif plot_aspect.value == '10:8': 677 plot_height = int(8 * plot_width / 10) 678 elif plot_aspect.value == '1:1': 679 plot_height = plot_width 680 text += 'autosize=False,\n width = int(' 681 text += str(plot_width)+'), height=int(' 682 text += str(plot_height)+'))\n' 683 step3str += text 684 text = figname + '.show(config = {' \ 685 '\'toImageButtonOptions\': {\'format\': \'svg\'}})' 686 if JPSLUtils.notebookenv == 'NBClassic': 687 replace_text_of_next_cell( 688 importstr+step1str+step2str+step3str+text) 689 else: 690 codearea.sniptext.value = importstr+step1str+step2str+step3str+text 691 692 pass 693 694 steps.observe(tab_changed, names = 'selected_index') 695 with output: 696 display(steps) 697 if JPSLUtils.notebookenv == 'NBClassic': 698 display(output) 699 select_containing_cell('pandasplotGUI') 700 new_cell_immediately_below() 701 insert_text_into_next_cell(importstr+step1str+step2str+step3str) 702 else: 703 codearea = build_run_snip_widget( 704 importstr+step1str+step2str+step3str, output) 705 with output: 706 display(codearea) 707 display(output) 708 pass
2def plot_pandas_GUI(df_info=None, show_text_col = False, **kwargs): 3 """ 4 If passed no parameters this will look for all the dataframes in the user 5 namespace and make them available for plotting. Once a 6 dataframe is chosen only the numerical columns from that dataframe will 7 be available for inclusion in the plotting expression. 8 9 This GUI produces code to use the plotly interactive plotting package. 10 11 If you wish to allow only certain dataframes or have them show up as 12 user friendly names in the menus provide that information in the first 13 paramater df_info. 14 15 To allow inclusion of text columns pass True for show_text_col. 16 17 :param bool show_text_col: (default = False). When True columns 18 containing text will be shown. 19 20 :param list df_info: List of Lists [[object,globalname,userfriendly]],..] 21 * object -- pandas.DataFrame 22 * globalname -- string name of the object in the user global name space. 23 * userfriendly -- string name to display for user selection. 24 25 :keyword string figname: string used to override default python name for 26 figure. 27 28 :keyword bool findframes: default = True. If set to false and dataframes 29 are passed in dfs_info, will not search for dataframes in the user 30 namespace. 31 """ 32 from ipywidgets import Layout, Box, HBox, VBox, GridBox, Tab, \ 33 Accordion, Dropdown, Label, Text, Button, Checkbox, FloatText, \ 34 RadioButtons, BoundedIntText, Output 35 from ipywidgets import HTML as richLabel 36 from IPython.display import display, HTML 37 from IPython.display import Javascript as JS 38 from IPython import get_ipython 39 from JPSLUtils.utils import new_cell_immediately_below,\ 40 select_cell_immediately_below, move_cursor_in_current_cell, \ 41 insert_text_into_next_cell, insert_text_at_beginning_of_current_cell, \ 42 insert_newline_at_end_of_current_cell, select_containing_cell, \ 43 delete_selected_cell, iconselector, notice_group, notebookenv, \ 44 replace_text_of_next_cell 45 46 from .utils import find_pandas_dataframe_names, build_run_snip_widget 47 from IPython import get_ipython 48 global_dict = get_ipython().user_ns 49 JPSLUtils = global_dict["JPSLUtils"] 50 dfs_info = [] 51 if isinstance(df_info,list): 52 for k in df_info: 53 dfs_info.append(k) 54 findframes = kwargs.pop('findframes',True) 55 if findframes: 56 for k in find_pandas_dataframe_names(): 57 dfs_info.append([global_dict[k],k,k]) 58 friendly_to_globalname = {k[2]:k[1] for k in dfs_info} 59 friendly_to_object = {k[2]:k[0] for k in dfs_info} 60 61 figname = kwargs.pop('figname',None) 62 from .utils import find_figure_names 63 figlst = find_figure_names() 64 if figname in figlst: 65 raise UserWarning (str(figname) + ' already exists. Choose a ' 66 'different name for the figure.') 67 if figname == None: 68 figname = 'Figure_'+str(len(figlst)+1) 69 70 #### Define GUI Elements #### 71 # Those followed by a * are required. 72 output = Output() 73 with output: 74 display(HTML( 75 "<h3 id ='pandasplotGUI' style='text-align:center;'>Pandas Plot " 76 "Composer</h3> <div style='text-align:center;'>" 77 "<span style='color:green;'>Steps with a * are required.</span> The " 78 "code that will generate the plot is being " 79 "built in the cell immediately below.</div><div " 80 "style='text-align:center;'>This composer uses a subset of " 81 "<a href ='https://plotly.com/python/line-and-scatter/#'> " 82 "the plotly scatter plot</a> capabilities.</div>")) 83 84 longdesc = {'description_width':'initial'} 85 86 # Notices for the Final Check Tab. 87 makeplot_notices = notice_group(['At least one trace required.', 88 'Axes must have labels.'], 89 'Notices:','','red') 90 makeplot_notices.set_active([0,1]) 91 92 # Strings for each tab, that are combined to make the code. 93 importstr = '# CODE BLOCK generated using plot_pandas_GUI().\n# See ' \ 94 'https://jupyterphysscilab.github.io/jupyter_Pandas_GUI.\n' \ 95 'from plotly import graph_objects as go\n' + str(figname) + \ 96 ' = go.FigureWidget(layout_template=\"simple_white\")\n' 97 step1strdefault = '\n# Trace declaration(s) and trace formatting\n' 98 step1str = step1strdefault 99 step2strdefault = '\n# Axes labels\n' 100 step2str = step2strdefault 101 step3strdefault = '\n# Plot formatting\n' 102 step3str = step3strdefault 103 # 1. Pick Traces* 104 # a. Select Y vs. X pairs* (DataFrame, X and Y, which must be from single 105 # frame. 106 # Notices for the Pick Trace(s) tab. 107 notice_list = [ 108 'Data set (DataFrame) required.', 109 'X- and Y-coordinates required.', 110 'Incomplete or inconsistent error specification(s).', 111 'Non-default trace formatting left from previous trace.', 112 'X-Error settings left from previous trace.', 113 'Y-Error settings left from previous trace.', 114 ] 115 trace_notices = notice_group(notice_list, 'Notices:','','red') 116 step1instr = richLabel(value = 'For each trace you wish to include: ' 117 '<ol><li>Select a DataFrame (Data ' 118 'set);</li>' 119 '<li>Select the column containing the X ' 120 'values;</li>' 121 '<li>Select the column containing the Y ' 122 'values;</li>' 123 '<li>Provide a name for the trace if you do' 124 ' not like the default. This text will be ' 125 'used for the legend;</li>' 126 '<li> OPTIONAL - set additional formatting ' 127 'and error display by expanding the ' 128 'sections at the bottom of this tab;</li>' 129 '<li>Once everything is set use the ' 130 '<b>"Add Trace"</b> button to ' 131 'include it in your plot.</li></ol>') 132 133 step1instracc = Accordion(children = [step1instr]) 134 step1instracc.set_title(0,'Instructions') 135 step1instracc.selected_index = None 136 137 # DataFrame selection 138 tempopts = [] 139 tempopts.append('Choose data set.') 140 for k in dfs_info: 141 tempopts.append(k[2]) 142 whichframe = Dropdown(options=tempopts, 143 description='DataFrame: ',) 144 145 def update_columns(change): 146 df = friendly_to_object[change['new']] 147 tempcols = df.columns.values 148 if change['new'] == 'Choose data set.': 149 Xcoord.disabled = True 150 Ycoord.disabled = True 151 add_trace_but.disabled = True 152 add_trace_but.button_style = '' 153 trace_notices.activate_notice(0) 154 trace_notices.activate_notice(1) 155 add_trace_notices.value = trace_notices.notice_html() 156 return 157 tempopt = ['Choose column for coordinate.'] 158 for k in tempcols: 159 if show_text_col: 160 tempopt.append(k) 161 else: 162 if df[k].dtype != 'O': 163 tempopt.append(k) 164 Xcoord.options = tempopt 165 Xcoord.value = tempopt[0] 166 Ycoord.options = tempopt 167 Ycoord.value = tempopt[0] 168 Xcoord.disabled = False 169 Ycoord.disabled = False 170 trace_notices.activate_notice(1) 171 trace_notices.deactivate_notice(0) 172 add_trace_notices.value =trace_notices.notice_html() 173 pass 174 whichframe.observe(update_columns, names='value') 175 176 # Data selection 177 Xcoord = Dropdown(options=['Choose X-coordinate.'], 178 description='X: ', 179 disabled = True) 180 Ycoord = Dropdown(options=['Choose Y-coordinate.'], 181 description='Y: ', 182 disabled = True) 183 def trace_name_update(change): 184 if change['new'] != 'Choose column for coordinate.': 185 trace_name.value = Ycoord.value 186 if Xcoord.value != 'Choose column for coordinate.' and Ycoord.value \ 187 != 'Choose column for coordinate.': 188 add_trace_but.disabled = False 189 add_trace_but.button_style = 'success' 190 modedrop.disabled = False 191 colordrop.disabled = False 192 yerrtype.disabled = False 193 xerrtype.disabled = False 194 trace_name.disabled = False 195 trace_notices.deactivate_notice(1) 196 add_trace_notices.value = trace_notices.notice_html() 197 else: 198 add_trace_but.disabled = True 199 add_trace_but.button_style = '' 200 modedrop.disabled = True 201 colordrop.disabled = True 202 yerrtype.disabled = True 203 xerrtype.disabled = True 204 trace_name.disabled = True 205 trace_notices.activate_notice(1) 206 add_trace_notices.value = trace_notices.notice_html() 207 pass 208 209 Ycoord.observe(trace_name_update,names='value') 210 211 # Trace name 212 trace_name = Text(placeholder = 'Trace name for legend', 213 description = 'Trace name: ', 214 disabled = True) 215 216 # b. Trace Style (optional) 217 def trace_format_update(change): 218 trace_notices.deactivate_notice(3) 219 add_trace_notices.value = trace_notices.notice_html() 220 pass 221 222 modedrop = Dropdown(options = ['lines','markers','lines+markers'], 223 description = 'Trace Style: ') 224 modedrop.observe(trace_format_update) 225 colordrop = Dropdown(options=['default','blue','orange','green','purple', 226 'red','gold','brown','black'], 227 description = 'Color: ') 228 colordrop.observe(trace_format_update) 229 iconlist = ['circle', 'square', 'caret-up', 'star', 'plus', 'times', 230 'caret-down', 'caret-left', 'caret-right'] 231 icontoplotly = {'circle': 'circle', 'square': 'square', 232 'caret-up': 'triangle-up', 'plus': 'cross', 233 'times': 'x', 'caret-down': 'triangle-down', 234 'caret-left': 'triangle-left', 'caret-right': 235 'triangle-right', 'star': 'star'} 236 markerlabel = Label('Marker Choices: ') 237 marker_selector = iconselector(iconlist, selected = 'circle') 238 filled_open = Checkbox(value = True, 239 description = 'Filled (uncheck for open)', 240 style={'description_width':'initial'}) 241 filled_open.observe(trace_format_update) 242 markersize = BoundedIntText(value = 6, min = 2, max = 25, step = 1, 243 description = 'Marker Size (px): ', 244 style=longdesc) 245 markersize.observe(trace_format_update) 246 markerhbox = HBox(children=[markerlabel,filled_open,markersize]) 247 markervbox = VBox(children=[markerhbox, marker_selector.box]) 248 line_style = Dropdown(options = ['solid','dot','dash','dashdot'], 249 description = 'Line style: ') 250 line_style.observe(trace_format_update) 251 line_width = BoundedIntText(value = 2, min = 1, max = 25, step = 1, 252 description = 'Linewidth (px): ', 253 style=longdesc) 254 line_width.observe(trace_format_update) 255 linehbox = HBox(children=[line_style,line_width]) 256 257 formatHbox1 = HBox(children=[modedrop,colordrop]) 258 formatVbox = VBox(children=[formatHbox1,linehbox,markervbox]) 259 step1formatacc = Accordion(children=[formatVbox]) 260 step1formatacc.set_title(0,'Trace Formatting') 261 step1formatacc.selected_index = 0 262 yerrtype = Dropdown(options = ['none','percent','constant','data'], 263 description = 'Error Type: ', 264 disabled = True) 265 266 def error_settings_OK(): 267 check = True 268 if (yerrtype.value == 'data') and (yerrdata.value == 'Choose error ' 269 'column.'): 270 check = False 271 if (xerrtype.value == 'data') and (xerrdata.value == 'Choose error ' 272 'column.'): 273 check = False 274 if (yerrtype.value == 'none' or yerrtype.value == 'percent' or 275 yerrtype.value == 'constant') and (xerrtype.value == 'none' or 276 xerrtype.value == 'percent' 277 or xerrtype.value =='constant'): 278 check = True 279 return check 280 281 def yerr_change(change): 282 df = friendly_to_object[whichframe.value] 283 if change['new'] == 'percent' or change['new'] == 'constant': 284 yerrvalue.disabled = False 285 yerrdata.disabled = True 286 if change['new'] == 'data': 287 yerrvalue.disabled = True 288 if yerrdata.value == 'Choose error column.': 289 add_trace_but.disabled = True 290 add_trace_but.button_style = '' 291 tempopts = ['Choose error column.'] 292 tempcols = df.columns.values 293 for k in tempcols: 294 if df[k].dtype != 'O': 295 tempopts.append(k) 296 yerrdata.options=tempopts 297 yerrdata.disabled = False 298 if change['new'] == 'none': 299 yerrvalue.disabled = True 300 yerrdata.disabled = True 301 if error_settings_OK(): 302 add_trace_but.disabled = False 303 add_trace_but.button_style = 'success' 304 trace_notices.deactivate_notice(2) 305 else: 306 add_trace_but.disabled = True 307 add_trace_but.button_style = '' 308 trace_notices.activate_notice(2) 309 trace_notices.deactivate_notice(5) 310 add_trace_notices.value = trace_notices.notice_html() 311 pass 312 313 yerrtype.observe(yerr_change, names = 'value') 314 315 yerrvalue = FloatText(description = '% or constant: ', disabled = True, 316 style=longdesc) 317 yerrdata = Dropdown(options = ['Choose error column.'], 318 description = 'Error values: ', 319 disabled = True) 320 321 def errdata_change(change): 322 if error_settings_OK(): 323 add_trace_but.disabled = False 324 add_trace_but.button_style = 'success' 325 trace_notices.deactivate_notice(2) 326 else: 327 add_trace_but.disabled = True 328 add_trace_but.button_style = '' 329 trace_notices.activate_notice(2) 330 add_trace_notices.value = trace_notices.notice_html() 331 pass 332 333 yerrdata.observe(errdata_change, names = 'value') 334 yerrrow1 = HBox(children=[yerrtype,yerrvalue]) 335 yerror = VBox(children=[yerrrow1,yerrdata]) 336 xerrtype = Dropdown(options = ['none','percent','constant','data'], 337 description = 'Error Type: ', 338 disabled = True) 339 def xerr_change(change): 340 df = friendly_to_object[whichframe.value] 341 if change['new'] == 'percent' or change['new'] == 'constant': 342 xerrvalue.disabled = False 343 xerrdata.disabled = True 344 if change['new'] == 'data': 345 xerrvalue.disabled = True 346 if xerrdata.value == 'Choose error column.': 347 add_trace_but.disabled = True 348 add_trace_but.button_style = '' 349 tempopts = ['Choose error column.'] 350 tempcols = df.columns.values 351 for k in tempcols: 352 if df[k].dtype != 'O': 353 tempopts.append(k) 354 xerrdata.options = tempopts 355 xerrdata.disabled = False 356 if change['new'] == 'none': 357 xerrvalue.disabled = True 358 xerrdata.disabled = True 359 if error_settings_OK(): 360 add_trace_but.disabled = False 361 add_trace_but.button_style = 'success' 362 trace_notices.deactivate_notice(2) 363 else: 364 add_trace_but.disabled = True 365 add_trace_but.button_style = '' 366 trace_notices.activate_notice(2) 367 trace_notices.deactivate_notice(4) 368 add_trace_notices.value = trace_notices.notice_html() 369 pass 370 371 xerrtype.observe(xerr_change, names = 'value') 372 xerrvalue = FloatText(description = '% or constant: ', disabled = True, 373 style=longdesc) 374 xerrdata = Dropdown(options = ['Choose error column.'], 375 description = 'Error values: ', 376 disabled = True) 377 378 xerrdata.observe(errdata_change, names = 'value') 379 xerrrow1 = HBox(children=[xerrtype,xerrvalue]) 380 xerror = VBox(children=[xerrrow1,xerrdata]) 381 step1erracc = Accordion(children = [yerror,xerror]) 382 step1erracc.set_title(0, 'Y error bars') 383 step1erracc.set_title(1, 'X error bars') 384 step1erracc.selected_index = None 385 386 # Add Trace button 387 add_trace_but = Button(description = 'Add Trace', 388 disabled = True) 389 def do_add_trace(change): 390 nonlocal importstr, step1str, step2str, step3str 391 dfname = friendly_to_globalname[whichframe.value] 392 text = 'scat = go.Scatter(x = '+dfname+'[\'' \ 393 +Xcoord.value+'\'],' 394 text += ' y = ' +dfname+'[\''+Ycoord.value+ \ 395 '\'],\n' 396 text += ' mode = \''+modedrop.value+'\', name = \'' \ 397 +trace_name.value+'\',' 398 # in here add other formatting items using ifs. 399 if colordrop.value != 'default': 400 text +='\n ' 401 if str(modedrop.value).find('lines') > -1: 402 text += 'line_color = \''+colordrop.value+'\', ' 403 if str(modedrop.value).find('markers') > -1: 404 text += 'marker_color = \'' + colordrop.value + '\', ' 405 if str(modedrop.value).find('lines') > -1: 406 if line_style.value != 'solid': 407 text +='\n ' 408 text +='line_dash=\'' + line_style.value + '\', ' 409 if line_width.value != 2: 410 text +='\n ' 411 text +='line_width=' + str(line_width.value) + ', ' 412 if str(modedrop.value).find('markers') > -1: 413 if markersize.value != 6: 414 text += '\n ' 415 text += 'marker_size=' + str(markersize.value) + ', ' 416 tmpmkr = icontoplotly[marker_selector.value] 417 if not filled_open.value: 418 text += '\n ' 419 tmpmkr +='-open' 420 text += 'marker_symbol=\'' + tmpmkr + '\', ' 421 else: 422 if tmpmkr != 'circle': 423 text += '\n ' 424 text += 'marker_symbol=\'' + tmpmkr + '\', ' 425 if yerrtype.value != 'none': 426 text +='\n ' 427 if yerrtype.value == 'data': 428 text += 'error_y_type=\'data\', ' \ 429 'error_y_array='+dfname 430 text += '[\''+yerrdata.value+'\'],' 431 else: 432 text += 'error_y_type=\''+yerrtype.value+'\', error_y_value=' 433 text += str(yerrvalue.value)+',' 434 if xerrtype.value != 'none': 435 text +='\n ' 436 if xerrtype.value == 'data': 437 text += 'error_x_type=\'data\', ' \ 438 'error_x_array='+dfname 439 text += '[\''+xerrdata.value+'\'],' 440 else: 441 text += 'error_x_type=\''+xerrtype.value+'\', error_x_value=' 442 text += str(xerrvalue.value)+',' 443 text += ')\n' 444 text += figname + '.add_trace(scat)\n' 445 step1str += text 446 if JPSLUtils.notebookenv == 'NBClassic': 447 replace_text_of_next_cell(importstr+step1str+step2str+step3str) 448 else: 449 codearea.sniptext.value = importstr+step1str+step2str+step3str 450 if (modedrop.value != 'lines') or (colordrop.value != 'default') or \ 451 (line_style.value != 'solid') or (line_width.value != 2) or \ 452 (icontoplotly[marker_selector.value] != 'circle') or \ 453 (not filled_open.value) or (markersize.value != 6): 454 trace_notices.activate_notice(3) 455 if (xerrtype.value != 'none'): 456 trace_notices.activate_notice(4) 457 if (yerrtype.value != 'none'): 458 trace_notices.activate_notice(5) 459 add_trace_notices.value = trace_notices.notice_html() 460 makeplot_notices.deactivate_notice(0) 461 step4noticebox.value = makeplot_notices.notice_html() 462 pass 463 add_trace_but.on_click(do_add_trace) 464 465 trace_notices.set_active([0,1]) 466 add_trace_notices = richLabel(value = trace_notices.notice_html()) 467 step1tracebox = VBox(children=[whichframe,Xcoord,Ycoord,trace_name]) 468 step1actionbox = VBox(children=[add_trace_but, add_trace_notices]) 469 step1hbox = HBox(children=[step1tracebox,step1actionbox]) 470 step1optbox = VBox(children=[step1formatacc, step1erracc]) 471 step1opt = Accordion(children = [step1optbox]) 472 step1opt.set_title(0, 'Optional (Trace formatting, error bars...)') 473 step1opt.selected_index = None 474 step1 = VBox(children=[step1instracc, step1hbox, step1opt]) 475 476 # 2. Set Axes Labels (will use column names by default). 477 step2instr = richLabel(value = '<span style = "font-weight:bold;">You must ' 478 'set the axes labels to something ' 479 'appropriate.</span> For example if the X - values ' 480 'represent time in seconds "Time (s)" is a good ' 481 'choice. If the Y - values for the traces all ' 482 'have the same units using the units as the label ' 483 'is a good choice. If the Y - values ' 484 'have different unit quantites the best ' 485 'option is probably "values" and making ' 486 'sure that the trace names used for the ' 487 'legend contain the units for each trace.') 488 step2instracc = Accordion(children = [step2instr]) 489 step2instracc.set_title(0,'Instructions') 490 step2instracc.selected_index = None 491 X_label = Text(placeholder = 'Provide an X-axis label (usually has units)', 492 description = 'X-axis label: ', 493 style = longdesc, 494 layout=Layout(width='45%')) 495 Y_label = Text(placeholder = 'Provide a Y-axis label (usually has units)', 496 description = 'Y-axis label: ', 497 style = longdesc, 498 layout=Layout(width='45%')) 499 step2hbox = HBox(children=[X_label,Y_label]) 500 step2 = VBox(children=[step2instracc,step2hbox]) 501 # 3.Title, Format ... 502 step3instr = richLabel(value='Overall format set here. <ul><li>Experiment ' 503 'with ' 504 'the Plot Styling templates to find your ' 505 'favorite.</li>' 506 '<li>If the Aspect Ratio is set to `auto` the ' 507 'figure will fill the default output region. ' 508 'Other choices will allow you to pick the Plot ' 509 'Size. `Large` will use about 2/3 of an HD ' 510 '(1920X1080) screen.</li>') 511 step3instracc = Accordion(children=[step3instr]) 512 step3instracc.set_title(0, 'Instructions') 513 step3instracc.selected_index = None 514 plot_title = Text(value = figname, 515 description = 'Plot title: ', 516 layout = Layout(width='80%')) 517 def mirror_axes_change(change): 518 if change['new']: 519 mirror_ticks.disabled= False 520 else: 521 mirror_ticks.disabled= True 522 mirror_ticks.value = False 523 pass 524 525 mirror_axes = Checkbox(value = False, 526 description = 'Display Mirror Axes', 527 style = longdesc) 528 mirror_axes.observe(mirror_axes_change, names = 'value') 529 mirror_ticks = Checkbox(value = False, 530 description = 'Mirror Tick Marks', 531 disabled = True) 532 plot_template = Dropdown(options=['none','simple_white', 'ggplot2', 533 'seaborn', 534 'plotly', 'plotly_white', 'plotly_dark', 535 'presentation', 'xgridoff', 'ygridoff', 536 'gridon', 'simple_white+presentation', 537 'simple_white+gridon', 538 'simple_white+presentation+gridon'], 539 value='simple_white', 540 description = 'Plot Styling: ', 541 style = longdesc) 542 def aspect_change(change): 543 if change['new'] != 'auto': 544 plot_size.disabled=False 545 else: 546 plot_size.disabled=True 547 pass 548 549 plot_aspect = Dropdown(options = ['auto', '16:9', '5:3', '7:5', '4:3', 550 '10:8', '1:1'], 551 value = 'auto', 552 description = 'Aspect Ratio: ', 553 style = longdesc) 554 plot_aspect.observe(aspect_change, names = 'value') 555 plot_size = Dropdown(options = ['tiny', 'small', 'medium', 'large', 556 'huge'], 557 value = 'large', 558 description = 'Plot Size: ', 559 style = longdesc, 560 disabled = True) 561 step3hbox2 = HBox(children=[mirror_axes,mirror_ticks, plot_template, 562 plot_aspect, plot_size]) 563 step3 = VBox(children=[step3instracc, plot_title, step3hbox2]) 564 565 # 4. Final Check* 566 step4instr = richLabel(value = 'Things to check before clicking making ' \ 567 'the plot: <ul>' \ 568 '<li>Fix any problems listed in ' \ 569 '"Notices".</li>' \ 570 '<li>Look at the code below to make sure ' \ 571 'you have included all the traces you ' \ 572 'intended to (look for "name").</li>' \ 573 '<li>Check for any unpaired parentheses, ' \ 574 'brackets or braces.</li>' \ 575 '<li>Check that all single and double ' \ 576 'quotes are paired.</li>' \ 577 '<li>If you did any manual editing ' \ 578 'double-check for typos.</li>') 579 step4noticebox = richLabel(value = makeplot_notices.notice_html()) 580 def makeplt_click(change): 581 if JPSLUtils.notebookenv == 'NBClassic': 582 # These commented out lines do nothing because of timing issues. 583 # text = '\n# Force save widget states so that graph will still be\n' 584 # text += '# available when notebook next opened in trusted state.\n' 585 # text += 'import time\ntime.sleep(5)' 586 select_containing_cell('pandasplotGUI') 587 select_cell_immediately_below() 588 # insert_newline_at_end_of_current_cell(text) 589 # jscode = 'Jupyter.actions.call("widgets:save-with-widgets");' 590 # text = 'JPSLUtils.OTJS(\''+jscode+'\')' 591 # insert_newline_at_end_of_current_cell(text) 592 # run the cell to build the plot 593 JPSLUtils.OTJS('Jupyter.notebook.get_selected_cell().execute();') 594 # remove the GUI cell 595 select_containing_cell('pandasplotGUI') 596 delete_selected_cell() 597 pass 598 599 makeplotbut_lay = Layout(visibility="hidden") 600 if JPSLUtils.notebookenv == 'NBClassic': 601 makeplotbut_lay = Layout(visibility="visible") 602 603 makeplotbut = Button(description = 'Make Plot', 604 layout = makeplotbut_lay, 605 disabled = True) 606 makeplotbut.on_click(makeplt_click) 607 step4vbox = VBox(children=[makeplotbut,step4noticebox]) 608 step4 = HBox(children=[step4instr,step4vbox]) 609 610 611 steps = Tab(children=[step1, step2, step3, step4]) 612 steps.set_title(0,'1. Pick Trace(s)*') 613 steps.set_title(1,'2. Label Axes*') 614 steps.set_title(2,'3. Title, Format ...') 615 steps.set_title(3,'4. Final Check*') 616 def tab_changed(change): 617 nonlocal importstr, step1strdefault, step1str, step2strdefault, \ 618 step2str, step3strdefault, step3str 619 if change['new'] ==3: 620 if X_label.value == '' or Y_label.value == '': 621 makeplot_notices.activate_notice(1) 622 makeplotbut.disabled = True 623 makeplotbut.button_style = '' 624 else: 625 makeplot_notices.deactivate_notice(1) 626 step4noticebox.value = makeplot_notices.notice_html() 627 if len(makeplot_notices.get_active()) == 0: 628 makeplotbut.disabled = False 629 makeplotbut.button_style = 'success' 630 if change['new'] > 1: 631 # update step2str 632 step2str = step2strdefault 633 text = figname + '.update_xaxes(title= \'' + X_label.value + '\'' 634 635 def get_mirror_text(): 636 if mirror_axes.value: 637 mirror_text = ', mirror = True)\n' 638 if mirror_ticks.value: 639 mirror_text = ', mirror= \'ticks\')\n' 640 else: 641 mirror_text = ')\n' 642 return mirror_text 643 644 text += get_mirror_text() 645 step2str += text 646 text = figname + '.update_yaxes(title= \'' + Y_label.value + '\'' 647 text += get_mirror_text() 648 step2str += text 649 # update step3str 650 step3str = step3strdefault 651 plot_width = 1200 652 plot_height = 675 653 if plot_title.value != '' or plot_template.value != 'simple_white': 654 text = figname + '.update_layout(title = \'' + plot_title.value + '\', ' 655 text += 'template = \'' + plot_template.value + '\', ' 656 if plot_aspect.value == 'auto': 657 text += 'autosize=True)\n' 658 else: 659 if plot_size.value == 'tiny': 660 plot_width = 300 661 elif plot_size.value == 'small': 662 plot_width = 450 663 elif plot_size.value == 'medium': 664 plot_width = 800 665 elif plot_size.value == 'large': 666 plot_width = 1200 667 elif plot_size.value == 'huge': 668 plot_width = 2400 669 if plot_aspect.value == '16:9': 670 plot_height = int(9 * plot_width / 16) 671 elif plot_aspect.value == '5:3': 672 plot_height = int(3 * plot_width / 5) 673 elif plot_aspect.value == '7:5': 674 plot_height = int(5 * plot_width / 7) 675 elif plot_aspect.value == '4:3': 676 plot_height = int(3 * plot_width / 4) 677 elif plot_aspect.value == '10:8': 678 plot_height = int(8 * plot_width / 10) 679 elif plot_aspect.value == '1:1': 680 plot_height = plot_width 681 text += 'autosize=False,\n width = int(' 682 text += str(plot_width)+'), height=int(' 683 text += str(plot_height)+'))\n' 684 step3str += text 685 text = figname + '.show(config = {' \ 686 '\'toImageButtonOptions\': {\'format\': \'svg\'}})' 687 if JPSLUtils.notebookenv == 'NBClassic': 688 replace_text_of_next_cell( 689 importstr+step1str+step2str+step3str+text) 690 else: 691 codearea.sniptext.value = importstr+step1str+step2str+step3str+text 692 693 pass 694 695 steps.observe(tab_changed, names = 'selected_index') 696 with output: 697 display(steps) 698 if JPSLUtils.notebookenv == 'NBClassic': 699 display(output) 700 select_containing_cell('pandasplotGUI') 701 new_cell_immediately_below() 702 insert_text_into_next_cell(importstr+step1str+step2str+step3str) 703 else: 704 codearea = build_run_snip_widget( 705 importstr+step1str+step2str+step3str, output) 706 with output: 707 display(codearea) 708 display(output) 709 pass
If passed no parameters this will look for all the dataframes in the user namespace and make them available for plotting. Once a dataframe is chosen only the numerical columns from that dataframe will be available for inclusion in the plotting expression.
This GUI produces code to use the plotly interactive plotting package.
If you wish to allow only certain dataframes or have them show up as user friendly names in the menus provide that information in the first paramater df_info.
To allow inclusion of text columns pass True for show_text_col.
Parameters
bool show_text_col: (default = False). When True columns containing text will be shown.
list df_info: List of Lists [[object,globalname,userfriendly]],..]
- object -- pandas.DataFrame
- globalname -- string name of the object in the user global name space.
- userfriendly -- string name to display for user selection.
:keyword string figname: string used to override default python name for figure.
:keyword bool findframes: default = True. If set to false and dataframes are passed in dfs_info, will not search for dataframes in the user namespace.