| Conditions | 24 |
| Total Lines | 222 |
| Code Lines | 135 |
| Lines | 46 |
| Ratio | 20.72 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like mutis.astro.KnotsIdGUI() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # Licensed under a 3-clause BSD style license - see LICENSE |
||
| 429 | def KnotsIdGUI(mod): |
||
| 430 | """ |
||
| 431 | Prompt a GUI to select identified knots and alter their label, reprenting their |
||
| 432 | time evolution. |
||
| 433 | |||
| 434 | It can be used inside jupyter notebooks, using '%matplotlib widget' first. |
||
| 435 | |||
| 436 | mod_data : a list containing a pandas.DataFrame for each epoch, with at least columns |
||
| 437 | 'X', 'Y' and 'Flux (Jy)'. |
||
| 438 | |||
| 439 | Parameters: |
||
| 440 | ----------- |
||
| 441 | mod : :pd.DataFrame: |
||
| 442 | pandas.DataFrame containing every knot, with at least columns |
||
| 443 | 'label', 'date', 'X', 'Y', 'Flux (Jy)'. |
||
| 444 | |||
| 445 | Returns: |
||
| 446 | -------- |
||
| 447 | mod : :pd.DataFrame: |
||
| 448 | pandas.DataFrame containing every knot with their altered labels, with |
||
| 449 | at least columns 'label', 'date', 'X', 'Y', 'Flux (Jy)'. |
||
| 450 | """ |
||
| 451 | |||
| 452 | mod = mod.copy() |
||
| 453 | |||
| 454 | knots = dict(tuple(mod.groupby('label'))) |
||
| 455 | knots_names = list(knots.keys()) |
||
| 456 | knots_values = list(knots.values()) |
||
| 457 | knots_jyears = {k:Time(knots[k]['date'].to_numpy()).jyear for k in knots} |
||
| 458 | knots_dates = {k:knots[k]['date'].to_numpy() for k in knots} |
||
| 459 | knots_fluxes = {k:knots[k]['Flux (Jy)'].to_numpy() for k in knots} |
||
| 460 | |||
| 461 | from matplotlib.widgets import TextBox, RectangleSelector |
||
| 462 | |||
| 463 | fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(10,6)) |
||
| 464 | |||
| 465 | lineas = list() |
||
| 466 | textos = list() |
||
| 467 | |||
| 468 | def draw_all(): |
||
| 469 | nonlocal lineas, textos |
||
| 470 | |||
| 471 | for linea in lineas: |
||
| 472 | if linea is not None: |
||
| 473 | linea.remove() |
||
| 474 | for texto in textos: |
||
| 475 | if texto is not None: |
||
| 476 | texto.remove() |
||
| 477 | |||
| 478 | ax.set_prop_cycle(None) |
||
| 479 | |||
| 480 | lineas = list() |
||
| 481 | textos = list() |
||
| 482 | |||
| 483 | #ax.clear() # either clear the whole axis or remove every artist separetly |
||
| 484 | |||
| 485 | for i, label in enumerate(knots_names): |
||
| 486 | x, y = knots_jyears[label], knots_fluxes[label] |
||
| 487 | |||
| 488 | if len(x) > 0: |
||
| 489 | lineas.append(ax.plot(x, y, '.-', linewidth=0.8, alpha=0.5, label=label)[0]) |
||
| 490 | else: |
||
| 491 | lineas.append(None) |
||
| 492 | |||
| 493 | if len(x) > 0: |
||
| 494 | #textos.append(ax.annotate(label, (x[0], y[0]), (-28,-10), textcoords='offset points', color=lineas[i].get_color(), fontsize=14)) |
||
| 495 | textos.append(ax.text(x[0], y[0], label, {'color':lineas[i].get_color(), 'fontsize':14})) |
||
| 496 | else: |
||
| 497 | textos.append(None) |
||
| 498 | |||
| 499 | fig.canvas.draw_idle() # if removed every artist separately instead of ax.clear() |
||
| 500 | |||
| 501 | |||
| 502 | def update(): |
||
| 503 | nonlocal lineas, textos |
||
| 504 | |||
| 505 | for i, label in enumerate(knots_names): |
||
| 506 | x, y = knots_jyears[label], knots_fluxes[label] |
||
| 507 | |||
| 508 | if lineas[i] is not None: |
||
| 509 | if len(x) > 0: |
||
| 510 | lineas[i].set_xdata(x) |
||
| 511 | lineas[i].set_ydata(y) |
||
| 512 | else: |
||
| 513 | lineas[i].remove() |
||
| 514 | lineas[i] = None |
||
| 515 | else: |
||
| 516 | if len(x) > 0: |
||
| 517 | lineas[i] = ax.plot(x, y, '.-', linewidth=0.8, alpha=0.5, label=label)[0] |
||
| 518 | |||
| 519 | if textos[i] is not None: |
||
| 520 | if len(x) > 0: |
||
| 521 | textos[i].set_position((x[0], y[0])) |
||
| 522 | textos[i].set_text(label) |
||
| 523 | else: |
||
| 524 | textos[i].remove() |
||
| 525 | textos[i] = None |
||
| 526 | #textos[i].set_position((10, 10)) |
||
| 527 | else: |
||
| 528 | if len(x) > 0: |
||
| 529 | #textos[i] = ax.annotate(label, (x[0], y[0]), (-24,-10), textcoords='offset points', color=lineas[i].get_color(), fontsize=15) |
||
| 530 | textos[i] = ax.text(x[0], y[0], label, {'color':lineas[i].get_color(), 'fontsize':14}) |
||
| 531 | |||
| 532 | fig.canvas.draw_idle() |
||
| 533 | |||
| 534 | |||
| 535 | selected_knot = None |
||
| 536 | selected_ind = None |
||
| 537 | selected_date = None |
||
| 538 | selected_flux = None |
||
| 539 | |||
| 540 | View Code Duplication | def submit_textbox(text): |
|
| 541 | nonlocal mod, knots, knots_names, knots_values, knots_jyears, knots_dates, knots_fluxes |
||
| 542 | |||
| 543 | log.debug('Submited with:') |
||
| 544 | log.debug(f' selected_knot {selected_knot}') |
||
| 545 | log.debug(f' selected_ind {selected_ind}') |
||
| 546 | log.debug(f' selected_flux {selected_flux}') |
||
| 547 | log.debug(f' selected_date {selected_date}') |
||
| 548 | |||
| 549 | if selected_knot is not None: |
||
| 550 | mod.loc[selected_ind, 'label'] = text.upper() |
||
| 551 | |||
| 552 | knots = dict(tuple(mod.groupby('label'))) |
||
| 553 | knots_names = list(knots.keys()) |
||
| 554 | knots_values = list(knots.values()) |
||
| 555 | knots_jyears = {k:Time(knots[k]['date'].to_numpy()).jyear for k in knots} |
||
| 556 | knots_dates = {k:knots[k]['date'].to_numpy() for k in knots} |
||
| 557 | knots_fluxes = {k:knots[k]['Flux (Jy)'].to_numpy() for k in knots} |
||
| 558 | |||
| 559 | print(f"Updated index {selected_ind} to {text.upper()}") |
||
| 560 | else: |
||
| 561 | pass |
||
| 562 | |||
| 563 | draw_all() |
||
| 564 | |||
| 565 | def line_select_callback(eclick, erelease): |
||
| 566 | nonlocal selected_knot,selected_date, selected_flux, selected_ind |
||
| 567 | |||
| 568 | # 1 eclick and erelease are the press and release events |
||
| 569 | x1, y1 = eclick.xdata, eclick.ydata |
||
| 570 | x2, y2 = erelease.xdata, erelease.ydata |
||
| 571 | log.debug("GUI: (%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)) |
||
| 572 | log.debug("GUI: The button you used were: %s %s" % (eclick.button, erelease.button)) |
||
| 573 | |||
| 574 | selected_knot = None |
||
| 575 | selected_date = None |
||
| 576 | selected_flux = None |
||
| 577 | selected_ind = None |
||
| 578 | |||
| 579 | for i, label in enumerate(knots_names): |
||
| 580 | years = knots_jyears[label] |
||
| 581 | fluxes = knots_fluxes[label] |
||
| 582 | |||
| 583 | # get points iside current rectangle for current date |
||
| 584 | rect_idx = (x1 < years) & ( years < x2) & (y1 < fluxes) & ( fluxes < y2) |
||
| 585 | |||
| 586 | if np.sum(rect_idx) == 0: |
||
| 587 | continue # we did not select any component from this component, next one |
||
| 588 | |||
| 589 | x = np.array(years) |
||
| 590 | y = np.array(fluxes) |
||
| 591 | |||
| 592 | View Code Duplication | if np.sum(rect_idx) > 0: |
|
| 593 | textbox.set_val(label) |
||
| 594 | selected_knot = label |
||
| 595 | selected_x = x[rect_idx].ravel() |
||
| 596 | selected_y = y[rect_idx].ravel() |
||
| 597 | selected_ind = knots[label].index[rect_idx] |
||
| 598 | log.debug(f'Selected {label} points rect_idx {rect_idx} date {x[rect_idx]}, flux {y[rect_idx]} with indices {selected_ind}') |
||
| 599 | break # if we find selected components in this epoch, continue with renaming |
||
| 600 | else: |
||
| 601 | pass |
||
| 602 | |||
| 603 | update() |
||
| 604 | |||
| 605 | |||
| 606 | View Code Duplication | def toggle_selector(event): |
|
| 607 | log.debug('GUI: Key pressed.') |
||
| 608 | if event.key in ['Q', 'q'] and toggle_selector.RS.active: |
||
| 609 | log.debug('Selector deactivated.') |
||
| 610 | toggle_selector.RS.set_active(False) |
||
| 611 | if event.key in ['S', 's'] and not toggle_selector.RS.active: |
||
| 612 | log.debug('Selector activated.') |
||
| 613 | toggle_selector.RS.set_active(True) |
||
| 614 | if event.key in ['R', 'r']: |
||
| 615 | log.debug('Selector deactivated.') |
||
| 616 | toggle_selector.RS.set_active(False) |
||
| 617 | textbox.begin_typing(None) |
||
| 618 | #textbox.set_val('') |
||
| 619 | |||
| 620 | |||
| 621 | toggle_selector.RS = RectangleSelector(ax, line_select_callback, |
||
| 622 | drawtype='box', useblit=True, |
||
| 623 | button=[1, 3], # don't use middle button |
||
| 624 | minspanx=0, minspany=0, |
||
| 625 | spancoords='data', |
||
| 626 | interactive=False) |
||
| 627 | |||
| 628 | |||
| 629 | fig.canvas.mpl_connect('key_press_event', toggle_selector) |
||
| 630 | |||
| 631 | from mpl_toolkits.axes_grid1 import make_axes_locatable |
||
| 632 | |||
| 633 | divider_textbox = make_axes_locatable(ax) |
||
| 634 | textbox_ax = divider_textbox.append_axes("bottom", size="10%", pad="15%") |
||
| 635 | #textbox_ax = fig.add_axes([0.3,0,0.5,0.05]) |
||
| 636 | textbox = TextBox(textbox_ax, 'Knot name:', initial='None') |
||
| 637 | textbox.on_submit(submit_textbox) |
||
| 638 | |||
| 639 | draw_all() |
||
| 640 | |||
| 641 | #ax.autoscale() |
||
| 642 | ax.set_xlabel('date (year)') |
||
| 643 | ax.set_ylabel('Flux (Jy)') |
||
| 644 | ax.set_title('Flux from each component') |
||
| 645 | |||
| 646 | xlims = Time(np.amin(mod['date'])).jyear, Time(np.amax(mod['date'])).jyear |
||
| 647 | ax.set_xlim((xlims[0]-0.03*np.abs(xlims[1]-xlims[0]), xlims[1]+0.03*np.abs(xlims[1]-xlims[0]))) |
||
| 648 | plt.show() |
||
| 649 | |||
| 650 | return mod |
||