| Conditions | 28 | 
| Total Lines | 284 | 
| Code Lines | 179 | 
| Lines | 47 | 
| Ratio | 16.55 % | 
| 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.KnotsId2dGUI() 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  | 
            ||
| 215 | def KnotsId2dGUI(mod, use_arrows=False, arrow_pos=1.0):  | 
            ||
| 216 | """  | 
            ||
| 217 | Prompt a GUI to select identified knots and alter their label, reprenting their 2D  | 
            ||
| 218 | spatial distribution in different times.  | 
            ||
| 219 | |||
| 220 | It can be used inside jupyter notebooks, using '%matplotlib widget' first.  | 
            ||
| 221 | |||
| 222 | Parameters:  | 
            ||
| 223 | -----------  | 
            ||
| 224 | mod : :pd.DataFrame:  | 
            ||
| 225 | pandas.DataFrame containing every knot, with at least columns  | 
            ||
| 226 | 'label', 'date', 'X', 'Y', 'Flux (Jy)'.  | 
            ||
| 227 | |||
| 228 | Returns:  | 
            ||
| 229 | --------  | 
            ||
| 230 | mod : :pd.DataFrame:  | 
            ||
| 231 | pandas.DataFrame containing every knot with their altered labels, with  | 
            ||
| 232 | at least columns 'label', 'date', 'X', 'Y', 'Flux (Jy)'.  | 
            ||
| 233 | """  | 
            ||
| 234 | |||
| 235 | mod = mod.copy()  | 
            ||
| 236 | |||
| 237 |     knots = dict(tuple(mod.groupby('label'))) | 
            ||
| 238 | knots_names = list(knots.keys())  | 
            ||
| 239 | knots_values = list(knots.values())  | 
            ||
| 240 |     knots_jyears = {k:Time(knots[k]['date'].to_numpy()).jyear for k in knots} | 
            ||
| 241 |     knots_X = {k:knots[k]['X'].to_numpy() for k in knots} | 
            ||
| 242 |     knots_Y = {k:knots[k]['Y'].to_numpy() for k in knots} | 
            ||
| 243 | |||
| 244 | |||
| 245 | from matplotlib.widgets import Slider, Button, TextBox, RectangleSelector  | 
            ||
| 246 | |||
| 247 | out = get_output()  | 
            ||
| 248 | |||
| 249 | fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8,8))  | 
            ||
| 250 | |||
| 251 | lineas = list()  | 
            ||
| 252 | flechas = list()  | 
            ||
| 253 | textos = list()  | 
            ||
| 254 | |||
| 255 | def draw_all(val=2008):  | 
            ||
| 256 | nonlocal lineas, flechas, textos  | 
            ||
| 257 | |||
| 258 | # instead of clearing the whole axis, remove artists  | 
            ||
| 259 | for linea in lineas:  | 
            ||
| 260 | if linea is not None:  | 
            ||
| 261 | linea.remove()  | 
            ||
| 262 | for texto in textos:  | 
            ||
| 263 | if texto is not None:  | 
            ||
| 264 | texto.remove()  | 
            ||
| 265 | for flecha in flechas:  | 
            ||
| 266 | if flecha is not None:  | 
            ||
| 267 | flecha.remove()  | 
            ||
| 268 | ax.set_prop_cycle(None)  | 
            ||
| 269 | |||
| 270 | lineas = list()  | 
            ||
| 271 | flechas = list()  | 
            ||
| 272 | textos = list()  | 
            ||
| 273 | |||
| 274 | xlim, ylim = ax.get_xlim(), ax.get_ylim()  | 
            ||
| 275 | |||
| 276 | #ax.clear() # either clear the whole axis or remove every artist separetly  | 
            ||
| 277 | |||
| 278 | for i, label in enumerate(knots_names):  | 
            ||
| 279 | years = knots_jyears[label]  | 
            ||
| 280 | idx = (val-1.5 < years) & (years < val)  | 
            ||
| 281 | x = knots_X[label][idx]  | 
            ||
| 282 | y = knots_Y[label][idx]  | 
            ||
| 283 | |||
| 284 | lineas.append(ax.plot(x, y, '.-', linewidth=0.6, alpha=0.4, label=label)[0])  | 
            ||
| 285 | |||
| 286 | if use_arrows:  | 
            ||
| 287 | if len(x) > 1:  | 
            ||
| 288 | flechas.append(ax.quiver(x[:-1],  | 
            ||
| 289 | y[:-1],  | 
            ||
| 290 | arrow_pos*(x[1:] - x[:-1]),  | 
            ||
| 291 | arrow_pos*(y[1:] - y[:-1]),  | 
            ||
| 292 | scale_units='xy', angles='xy', scale=1,  | 
            ||
| 293 | width=0.0015, headwidth=10, headlength=10, headaxislength=6,  | 
            ||
| 294 | alpha=0.5, color=lineas[i].get_color()))  | 
            ||
| 295 | else:  | 
            ||
| 296 | flechas.append(None)  | 
            ||
| 297 | |||
| 298 | if len(x) > 0:  | 
            ||
| 299 | #textos.append(ax.annotate(label, (x[0], y[0]), (-28,-10), textcoords='offset points', color=lineas[i].get_color(), fontsize=14))  | 
            ||
| 300 |                 textos.append(ax.text(x[-1]+0.015, y[-1]+0.015, label, {'color':lineas[i].get_color(), 'fontsize':14})) | 
            ||
| 301 | else:  | 
            ||
| 302 | textos.append(None)  | 
            ||
| 303 | |||
| 304 | ax.set_xlim(xlim)  | 
            ||
| 305 | ax.set_ylim(ylim)  | 
            ||
| 306 |         ax.set_aspect('equal') | 
            ||
| 307 | |||
| 308 | fig.canvas.draw_idle() # if removed every artist separately instead of ax.clear()  | 
            ||
| 309 | |||
| 310 | draw_all()  | 
            ||
| 311 | |||
| 312 | def update(val):  | 
            ||
| 313 | nonlocal lineas, flechas, textos  | 
            ||
| 314 | |||
| 315 | for i, label in enumerate(knots_names):  | 
            ||
| 316 | years = knots_jyears[label]  | 
            ||
| 317 | idx = (val-1.5 < years) & (years < val)  | 
            ||
| 318 | x = knots_X[label][idx]  | 
            ||
| 319 | y = knots_Y[label][idx]  | 
            ||
| 320 | |||
| 321 | lineas[i].set_xdata(x)  | 
            ||
| 322 | lineas[i].set_ydata(y)  | 
            ||
| 323 | |||
| 324 | if textos[i] is not None:  | 
            ||
| 325 | if len(x) > 0:  | 
            ||
| 326 | textos[i].set_position((x[-1]+0.015, y[-1]+0.015))  | 
            ||
| 327 | textos[i].set_text(label)  | 
            ||
| 328 | else:  | 
            ||
| 329 | textos[i].remove()  | 
            ||
| 330 | textos[i] = None  | 
            ||
| 331 | #textos[i].set_position((10, 10))  | 
            ||
| 332 | else:  | 
            ||
| 333 | if len(x) > 0:  | 
            ||
| 334 |                     textos[i] = ax.text(x[-1]+0.02, y[-1]+0.02, label, {'color':lineas[i].get_color(), 'fontsize':14}) | 
            ||
| 335 | |||
| 336 | if use_arrows:  | 
            ||
| 337 | if flechas[i] is not None:  | 
            ||
| 338 | flechas[i].remove()  | 
            ||
| 339 | flechas[i] = None  | 
            ||
| 340 | |||
| 341 | flechas[i] = ax.quiver(x[:-1],  | 
            ||
| 342 | y[:-1],  | 
            ||
| 343 | arrow_pos*(x[1:] - x[:-1]),  | 
            ||
| 344 | arrow_pos*(y[1:] - y[:-1]),  | 
            ||
| 345 | scale_units='xy', angles='xy', scale=1,  | 
            ||
| 346 | width=0.0015, headwidth=10, headlength=10, headaxislength=6,  | 
            ||
| 347 | alpha=0.5, color=lineas[i].get_color())  | 
            ||
| 348 | |||
| 349 | fig.canvas.draw_idle()  | 
            ||
| 350 | |||
| 351 | |||
| 352 | |||
| 353 | selected_knot = None  | 
            ||
| 354 | selected_ind = None  | 
            ||
| 355 | selected_x = None  | 
            ||
| 356 | selected_y = None  | 
            ||
| 357 | |||
| 358 | |||
| 359 | View Code Duplication | def submit_textbox(text):  | 
            |
| 360 | nonlocal mod, knots, knots_names, knots_values, knots_jyears, knots_X, knots_Y  | 
            ||
| 361 | |||
| 362 |         log.debug('Submited with:') | 
            ||
| 363 |         log.debug(f'   selected_knot {selected_knot}') | 
            ||
| 364 |         log.debug(f'   selected_ind {selected_ind}') | 
            ||
| 365 |         log.debug(f'   selected_x {selected_x}') | 
            ||
| 366 |         log.debug(f'   selected_y {selected_y}') | 
            ||
| 367 | |||
| 368 | if selected_knot is not None:  | 
            ||
| 369 | mod.loc[selected_ind, 'label'] = text.upper()  | 
            ||
| 370 | |||
| 371 |             knots = dict(tuple(mod.groupby('label'))) | 
            ||
| 372 | knots_names = list(knots.keys())  | 
            ||
| 373 | knots_values = list(knots.values())  | 
            ||
| 374 |             knots_jyears = {k:Time(knots[k]['date'].to_numpy()).jyear for k in knots} | 
            ||
| 375 |             knots_X = {k:knots[k]['X'].to_numpy() for k in knots} | 
            ||
| 376 |             knots_Y = {k:knots[k]['Y'].to_numpy() for k in knots} | 
            ||
| 377 | |||
| 378 |             log.debug(f"Updated index {selected_ind} to {text.upper()}") | 
            ||
| 379 | else:  | 
            ||
| 380 | pass  | 
            ||
| 381 | |||
| 382 | draw_all(slider_date.val)  | 
            ||
| 383 | |||
| 384 | |||
| 385 | def line_select_callback(eclick, erelease):  | 
            ||
| 386 | nonlocal selected_knot,selected_x, selected_y, selected_ind  | 
            ||
| 387 | |||
| 388 | # 1 eclick and erelease are the press and release events  | 
            ||
| 389 | x1, y1 = eclick.xdata, eclick.ydata  | 
            ||
| 390 | x2, y2 = erelease.xdata, erelease.ydata  | 
            ||
| 391 |         log.debug("GUI: (%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)) | 
            ||
| 392 |         log.debug("GUI:  The button you used were: %s %s" % (eclick.button, erelease.button)) | 
            ||
| 393 | |||
| 394 | selected_knot = None  | 
            ||
| 395 | selected_x = None  | 
            ||
| 396 | selected_y = None  | 
            ||
| 397 | selected_ind = None  | 
            ||
| 398 | |||
| 399 | for i, label in enumerate(knots_names):  | 
            ||
| 400 | years = knots_jyears[label]  | 
            ||
| 401 | idx = (slider_date.val-1.5 < years) & (years < slider_date.val)  | 
            ||
| 402 | |||
| 403 | if np.sum(idx) == 0:  | 
            ||
| 404 | continue # we did not select any component from this component, next one  | 
            ||
| 405 | |||
| 406 | x = np.array(knots_X[label])  | 
            ||
| 407 | y = np.array(knots_Y[label])  | 
            ||
| 408 | |||
| 409 | # get points iside current rectangle for current date  | 
            ||
| 410 | rect_idx = (x1 < x) & ( x < x2) & (y1 < y) & ( y < y2) & idx  | 
            ||
| 411 | |||
| 412 | View Code Duplication | if np.sum(rect_idx) > 0:  | 
            |
| 413 | textbox.set_val(label)  | 
            ||
| 414 | |||
| 415 | selected_knot = label  | 
            ||
| 416 | selected_x = x[rect_idx].ravel()  | 
            ||
| 417 | selected_y = y[rect_idx].ravel()  | 
            ||
| 418 | selected_ind = knots[label].index[rect_idx]  | 
            ||
| 419 | |||
| 420 |                 log.debug(f'Selected {label} points  rect_idx {rect_idx} x {x[rect_idx]}, y {y[rect_idx]} with indices {selected_ind}') | 
            ||
| 421 | |||
| 422 | textbox.begin_typing(None)  | 
            ||
| 423 | break # if we find selected components in this epoch, continue with renaming  | 
            ||
| 424 | else:  | 
            ||
| 425 | pass  | 
            ||
| 426 | |||
| 427 | # print epoch of selected points:  | 
            ||
| 428 | if selected_knot is not None:  | 
            ||
| 429 | out.clear_output()  | 
            ||
| 430 |             out.append_stdout('Selected:\n') | 
            ||
| 431 | for idx in selected_ind:  | 
            ||
| 432 |                 #print(f"Selected {mod.loc[idx, 'label']} {mod.loc[idx, 'date']}") | 
            ||
| 433 |                 out.append_stdout(f" -> {mod.loc[idx, 'label']} {mod.loc[idx, 'date'].strftime('%Y-%m-%d')}\n") | 
            ||
| 434 | |||
| 435 | update(slider_date.val)  | 
            ||
| 436 | |||
| 437 | |||
| 438 | View Code Duplication | def toggle_selector(event):  | 
            |
| 439 |         log.debug('GUI: Key pressed.') | 
            ||
| 440 | if event.key in ['Q', 'q'] and toggle_selector.RS.active:  | 
            ||
| 441 |             log.debug('Selector deactivated.') | 
            ||
| 442 | toggle_selector.RS.set_active(False)  | 
            ||
| 443 | if event.key in ['S', 's'] and not toggle_selector.RS.active:  | 
            ||
| 444 |             log.debug('Selector activated.') | 
            ||
| 445 | toggle_selector.RS.set_active(True)  | 
            ||
| 446 | if event.key in ['R', 'r']:  | 
            ||
| 447 |             log.debug('Selector deactivated.') | 
            ||
| 448 | toggle_selector.RS.set_active(False)  | 
            ||
| 449 | textbox.begin_typing(None)  | 
            ||
| 450 |             #textbox.set_val('') | 
            ||
| 451 | |||
| 452 | |||
| 453 | toggle_selector.RS = RectangleSelector(ax, line_select_callback,  | 
            ||
| 454 | drawtype='box', useblit=True,  | 
            ||
| 455 | button=[1, 3], # don't use middle button  | 
            ||
| 456 | minspanx=0, minspany=0,  | 
            ||
| 457 | spancoords='data',  | 
            ||
| 458 | interactive=False)  | 
            ||
| 459 | |||
| 460 | |||
| 461 |     #plt.connect('key_press_event', toggle_selector) | 
            ||
| 462 |     fig.canvas.mpl_connect('key_press_event', toggle_selector) | 
            ||
| 463 | |||
| 464 | |||
| 465 | |||
| 466 | from mpl_toolkits.axes_grid1 import make_axes_locatable  | 
            ||
| 467 | |||
| 468 | divider_slider = make_axes_locatable(ax)  | 
            ||
| 469 |     slider_ax = divider_slider.append_axes("top", size="3%", pad="4%")   | 
            ||
| 470 | slider_date = Slider(ax=slider_ax, label="Date", valmin=2007, valmax=2020, valinit=2008, valstep=0.2, orientation="horizontal")  | 
            ||
| 471 | slider_date.on_changed(update)  | 
            ||
| 472 | |||
| 473 | #divider_textbox = make_axes_locatable(ax)  | 
            ||
| 474 |     #textbox_ax = divider_textbox.append_axes("bottom", size="3%", pad="4%")  | 
            ||
| 475 | textbox_ax = fig.add_axes([0.3,0.015,0.5,0.05])  | 
            ||
| 476 | textbox = TextBox(textbox_ax, 'Knot name:', initial='None')  | 
            ||
| 477 | textbox.on_submit(submit_textbox)  | 
            ||
| 478 | |||
| 479 | |||
| 480 | |||
| 481 | ax.set_xlim([-1.0, +1.0])  | 
            ||
| 482 | ax.set_ylim([-1.0, +1.0])  | 
            ||
| 483 |     ax.set_aspect('equal') | 
            ||
| 484 | |||
| 485 |     fig.suptitle('S to select, R to rename, Q to deactivate selector') | 
            ||
| 486 | |||
| 487 |     print('Usage:', | 
            ||
| 488 | '-> S to select, R to rename, Q to deactivate selector',  | 
            ||
| 489 | '-> To delete the knot, name it with a whitespace',  | 
            ||
| 490 | '-> You can select points from one component at a time',  | 
            ||
| 491 | '-> If you use the zoom or movement tools, remember to unselect them',  | 
            ||
| 492 | sep='\n')  | 
            ||
| 493 | |||
| 494 | plt.show()  | 
            ||
| 495 | |||
| 496 | out.display_output()  | 
            ||
| 497 | |||
| 498 | return mod  | 
            ||
| 499 | |||
| 902 |         data.to_csv(f'{path}/{label}.csv', index=False)  | 
            ||