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