Total Complexity | 74 |
Total Lines | 651 |
Duplicated Lines | 14.29 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like mutis.astro 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 |
||
2 | """Utils specific to the field of astrophysics""" |
||
3 | |||
4 | import logging |
||
5 | |||
6 | import numpy as np |
||
|
|||
7 | import pandas as pd |
||
8 | |||
9 | import matplotlib as mplt |
||
10 | import matplotlib.pyplot as plt |
||
11 | |||
12 | from astropy.time import Time |
||
13 | |||
14 | |||
15 | __all__ = ["Astro"] |
||
16 | |||
17 | log = logging.getLogger(__name__) |
||
18 | |||
19 | |||
20 | def pol_angle_reshape(s): |
||
21 | """ |
||
22 | Reshape a signal as a polarization angle: shifting by 180 degrees in a way it varies as smoothly as possible. |
||
23 | """ |
||
24 | |||
25 | s = np.array(s) |
||
26 | s = np.mod(s,180) # s % 180 |
||
27 | |||
28 | sn = np.empty(s.shape) |
||
29 | for i in range(1,len(s)): |
||
30 | #if t[i]-t[i-1] < 35: |
||
31 | d = 181 |
||
32 | n = 0 |
||
33 | for m in range(0,100): |
||
34 | m2 = (-1)**(m+1)*np.floor((m+1)/2) |
||
35 | if np.abs(s[i]+180*m2-sn[i-1]) < d: |
||
36 | d = np.abs(s[i]+180*m2-sn[i-1]) |
||
37 | n = m2 |
||
38 | sn[i] = s[i]+180*n |
||
39 | return sn |
||
40 | |||
41 | |||
42 | |||
43 | |||
44 | |||
45 | def KnotsIdAuto(mod_dates, mod_dates_str, mod_data): |
||
46 | """ |
||
47 | Identify the knots appearing in several epochs giving them names, based on their position. |
||
48 | |||
49 | Parameters: |
||
50 | ----------- |
||
51 | mod_dates : a list containing the dates (datetime) of each of the epochs. |
||
52 | mod_data : a list containing a pandas.DataFrame for each epoch, with at least columns |
||
53 | 'X', 'Y' and 'Flux (Jy)'. |
||
54 | |||
55 | Returns: ((Bx, By, B), mod) |
||
56 | -------- |
||
57 | (Bx, By, B) : :tuple: |
||
58 | three lists, the first two containing the positions of |
||
59 | the identified knots, the third their assigned names (for each epoch). |
||
60 | mod : :pd.DataFrame: |
||
61 | pandas.DataFrame containing every knot with their altered labels, with |
||
62 | at least columns 'label', 'date', 'X', 'Y', 'Flux (Jy)'. |
||
63 | |||
64 | |||
65 | Usage: (old) |
||
66 | ------ |
||
67 | >> Bx, By, B = KnotsIdAuto(mod_dates, mod_dates_str, mod_data) |
||
68 | |||
69 | >> for i, (date, data) in enumerate(zip(mod_dates, mod_data)): |
||
70 | data['label'] = B[i] |
||
71 | |||
72 | >> mod = pd.concat(mod_data, ignore_index=True) |
||
73 | >> mod |
||
74 | """ |
||
75 | |||
76 | Bx = [[]]*len(mod_dates) |
||
77 | By = [[]]*len(mod_dates) |
||
78 | B = [[]]*len(mod_dates) |
||
79 | |||
80 | |||
81 | thresh = 0.03 #0.062 |
||
82 | |||
83 | news = 0 |
||
84 | |||
85 | for i, (date, data) in enumerate(zip(mod_dates, mod_data)): |
||
86 | log.debug(f'Analysing epoch i {i:3d} ({mod_dates_str[i]})') |
||
87 | |||
88 | if not len(data.index) > 0: |
||
89 | log.error(' -> Error, no components found') |
||
90 | break |
||
91 | |||
92 | log.debug(f' it has {len(data.index)} components') |
||
93 | |||
94 | Bx[i] = np.ravel(data['X']) |
||
95 | By[i] = np.ravel(data['Y']) |
||
96 | B[i] = np.full(len(data.index), fill_value=None) |
||
97 | |||
98 | # if first epoch, just give them new names...: |
||
99 | if i == 0: |
||
100 | log.debug(' first epoch, giving names...') |
||
101 | |||
102 | for n in range(0,len(mod_data[i].index)): |
||
103 | if data['Flux (Jy)'][n] < 0.001: |
||
104 | log.debug(' skipping, too weak') |
||
105 | break |
||
106 | if n == 0: |
||
107 | B[i][n] = 'A0' |
||
108 | else: |
||
109 | news = news + 1 |
||
110 | B[i][n] = f'B{news}' |
||
111 | log.debug(f' -> FOUND: {B[i]}\n') |
||
112 | continue |
||
113 | |||
114 | # if not first epoch...: |
||
115 | |||
116 | for n in range(0,len(mod_data[i].index)): |
||
117 | if data['Flux (Jy)'][n] < 0.001: |
||
118 | log.debug(' skipping, too weak') |
||
119 | break |
||
120 | if n == 0: |
||
121 | B[i][n] = 'A0' |
||
122 | else: |
||
123 | log.debug(f' -> id component {n}...') |
||
124 | |||
125 | close = None |
||
126 | |||
127 | a = 0 |
||
128 | while ( i - a >= 0 and a < 4 and close is None): |
||
129 | a = a + 1 |
||
130 | |||
131 | if not len(Bx[i-a])>0: |
||
132 | break |
||
133 | |||
134 | dist = ( (Bx[i-a]-Bx[i][n]) ** 2 + (By[i-a]-By[i][n]) ** 2 ) ** 0.5 |
||
135 | |||
136 | for m in range(len(dist)): |
||
137 | if B[i-a][m] in B[i]: |
||
138 | dist[m] = np.inf |
||
139 | |||
140 | if np.amin(dist) < thresh*a**1.5: |
||
141 | close = np.argmin(dist) |
||
142 | |||
143 | if B[i-a][close] in B[i]: |
||
144 | close = None |
||
145 | |||
146 | |||
147 | if close is None: |
||
148 | news = news + 1 |
||
149 | B[i][n] = f'B{news}' |
||
150 | log.debug(f' component {n} is new, naming it {B[i][n]}') |
||
151 | else: |
||
152 | log.debug(f' component {n} is close to {B[i-a][close]} of previous epoch ({a} epochs before)') |
||
153 | B[i][n] = B[i-a][close] |
||
154 | |||
155 | |||
156 | log.debug(f' -> FOUND: {B[i]}\n') |
||
157 | |||
158 | |||
159 | #Bx, By, B = KnotsIdAuto(mod_dates, mod_dates_str, mod_data) |
||
160 | |||
161 | for i, (date, data) in enumerate(zip(mod_dates, mod_data)): |
||
162 | data['label'] = B[i] |
||
163 | |||
164 | mod = pd.concat(mod_data, ignore_index=True) |
||
165 | |||
166 | return (Bx, By, B), mod |
||
167 | |||
168 | |||
169 | |||
170 | def KnotsId2dGUI(mod, use_arrows=False, arrow_pos=1.0): |
||
171 | """ |
||
172 | Prompt a GUI to select identified knots and alter their label, reprenting their 2D |
||
173 | spatial distribution in different times. |
||
174 | |||
175 | It can be used inside jupyter notebooks, using '%matplotlib widget' first. |
||
176 | """ |
||
177 | |||
178 | mod = mod.copy() |
||
179 | |||
180 | knots = dict(tuple(mod.groupby('label'))) |
||
181 | knots_names = list(knots.keys()) |
||
182 | knots_values = list(knots.values()) |
||
183 | knots_jyears = {k:Time(knots[k]['date'].to_numpy()).jyear for k in knots} |
||
184 | knots_X = {k:knots[k]['X'].to_numpy() for k in knots} |
||
185 | knots_Y = {k:knots[k]['Y'].to_numpy() for k in knots} |
||
186 | |||
187 | |||
188 | from matplotlib.widgets import Slider, Button, TextBox, RectangleSelector |
||
189 | |||
190 | fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8,8)) |
||
191 | |||
192 | lineas = list() |
||
193 | flechas = list() |
||
194 | textos = list() |
||
195 | |||
196 | def draw_all(val=2008): |
||
197 | nonlocal lineas, flechas, textos |
||
198 | |||
199 | # instead of clearing the whole axis, remove artists |
||
200 | for linea in lineas: |
||
201 | if linea is not None: |
||
202 | linea.remove() |
||
203 | for texto in textos: |
||
204 | if texto is not None: |
||
205 | texto.remove() |
||
206 | for flecha in flechas: |
||
207 | if flecha is not None: |
||
208 | flecha.remove() |
||
209 | ax.set_prop_cycle(None) |
||
210 | |||
211 | lineas = list() |
||
212 | flechas = list() |
||
213 | textos = list() |
||
214 | |||
215 | xlim, ylim = ax.get_xlim(), ax.get_ylim() |
||
216 | |||
217 | #ax.clear() # either clear the whole axis or remove every artist separetly |
||
218 | |||
219 | for i, label in enumerate(knots_names): |
||
220 | years = knots_jyears[label] |
||
221 | idx = (val-1.5 < years) & (years < val) |
||
222 | x = knots_X[label][idx] |
||
223 | y = knots_Y[label][idx] |
||
224 | |||
225 | lineas.append(ax.plot(x, y, '.-', linewidth=0.6, alpha=0.4, label=label)[0]) |
||
226 | |||
227 | if use_arrows: |
||
228 | if len(x) > 1: |
||
229 | flechas.append(ax.quiver(x[:-1], |
||
230 | y[:-1], |
||
231 | arrow_pos*(x[1:] - x[:-1]), |
||
232 | arrow_pos*(y[1:] - y[:-1]), |
||
233 | scale_units='xy', angles='xy', scale=1, |
||
234 | width=0.0015, headwidth=10, headlength=10, headaxislength=6, |
||
235 | alpha=0.5, color=lineas[i].get_color())) |
||
236 | else: |
||
237 | flechas.append(None) |
||
238 | |||
239 | if len(x) > 0: |
||
240 | #textos.append(ax.annotate(label, (x[0], y[0]), (-28,-10), textcoords='offset points', color=lineas[i].get_color(), fontsize=14)) |
||
241 | textos.append(ax.text(x[-1]+0.015, y[-1]+0.015, label, {'color':lineas[i].get_color(), 'fontsize':14})) |
||
242 | else: |
||
243 | textos.append(None) |
||
244 | |||
245 | ax.set_xlim(xlim) |
||
246 | ax.set_ylim(ylim) |
||
247 | ax.set_aspect('equal') |
||
248 | |||
249 | fig.canvas.draw_idle() # if removed every artist separately instead of ax.clear() |
||
250 | |||
251 | draw_all() |
||
252 | |||
253 | def update(val): |
||
254 | nonlocal lineas, flechas, textos |
||
255 | |||
256 | for i, label in enumerate(knots_names): |
||
257 | years = knots_jyears[label] |
||
258 | idx = (val-1.5 < years) & (years < val) |
||
259 | x = knots_X[label][idx] |
||
260 | y = knots_Y[label][idx] |
||
261 | |||
262 | lineas[i].set_xdata(x) |
||
263 | lineas[i].set_ydata(y) |
||
264 | |||
265 | if textos[i] is not None: |
||
266 | if len(x) > 0: |
||
267 | textos[i].set_position((x[-1]+0.015, y[-1]+0.015)) |
||
268 | textos[i].set_text(label) |
||
269 | else: |
||
270 | textos[i].remove() |
||
271 | textos[i] = None |
||
272 | #textos[i].set_position((10, 10)) |
||
273 | else: |
||
274 | if len(x) > 0: |
||
275 | textos[i] = ax.text(x[-1]+0.02, y[-1]+0.02, label, {'color':lineas[i].get_color(), 'fontsize':14}) |
||
276 | |||
277 | if use_arrows: |
||
278 | if flechas[i] is not None: |
||
279 | flechas[i].remove() |
||
280 | flechas[i] = None |
||
281 | |||
282 | flechas[i] = ax.quiver(x[:-1], |
||
283 | y[:-1], |
||
284 | arrow_pos*(x[1:] - x[:-1]), |
||
285 | arrow_pos*(y[1:] - y[:-1]), |
||
286 | scale_units='xy', angles='xy', scale=1, |
||
287 | width=0.0015, headwidth=10, headlength=10, headaxislength=6, |
||
288 | alpha=0.5, color=lineas[i].get_color()) |
||
289 | |||
290 | fig.canvas.draw_idle() |
||
291 | |||
292 | |||
293 | |||
294 | selected_knot = None |
||
295 | selected_ind = None |
||
296 | selected_x = None |
||
297 | selected_y = None |
||
298 | |||
299 | |||
300 | View Code Duplication | def submit_textbox(text): |
|
301 | nonlocal mod, knots, knots_names, knots_values, knots_jyears, knots_X, knots_Y |
||
302 | |||
303 | log.debug('Submited with:') |
||
304 | log.debug(f' selected_knot {selected_knot}') |
||
305 | log.debug(f' selected_ind {selected_ind}') |
||
306 | log.debug(f' selected_x {selected_x}') |
||
307 | log.debug(f' selected_y {selected_y}') |
||
308 | |||
309 | if selected_knot is not None: |
||
310 | mod.loc[selected_ind, 'label'] = text.upper() |
||
311 | |||
312 | knots = dict(tuple(mod.groupby('label'))) |
||
313 | knots_names = list(knots.keys()) |
||
314 | knots_values = list(knots.values()) |
||
315 | knots_jyears = {k:Time(knots[k]['date'].to_numpy()).jyear for k in knots} |
||
316 | knots_X = {k:knots[k]['X'].to_numpy() for k in knots} |
||
317 | knots_Y = {k:knots[k]['Y'].to_numpy() for k in knots} |
||
318 | |||
319 | print(f"Updated index {selected_ind} to {text.upper()}") |
||
320 | else: |
||
321 | pass |
||
322 | |||
323 | draw_all(slider_date.val) |
||
324 | |||
325 | def line_select_callback(eclick, erelease): |
||
326 | nonlocal selected_knot,selected_x, selected_y, selected_ind |
||
327 | |||
328 | # 1 eclick and erelease are the press and release events |
||
329 | x1, y1 = eclick.xdata, eclick.ydata |
||
330 | x2, y2 = erelease.xdata, erelease.ydata |
||
331 | log.debug("GUI: (%3.2f, %3.2f) --> (%3.2f, %3.2f)" % (x1, y1, x2, y2)) |
||
332 | log.debug("GUI: The button you used were: %s %s" % (eclick.button, erelease.button)) |
||
333 | |||
334 | selected_knot = None |
||
335 | selected_x = None |
||
336 | selected_y = None |
||
337 | selected_ind = None |
||
338 | |||
339 | for i, label in enumerate(knots_names): |
||
340 | years = knots_jyears[label] |
||
341 | idx = (slider_date.val-1.5 < years) & (years < slider_date.val) |
||
342 | |||
343 | if np.sum(idx) == 0: |
||
344 | continue # we did not select any component from this component, next one |
||
345 | |||
346 | x = np.array(knots_X[label]) |
||
347 | y = np.array(knots_Y[label]) |
||
348 | |||
349 | # get points iside current rectangle for current date |
||
350 | rect_idx = (x1 < x) & ( x < x2) & (y1 < y) & ( y < y2) & idx |
||
351 | |||
352 | View Code Duplication | if np.sum(rect_idx) > 0: |
|
353 | textbox.set_val(label) |
||
354 | selected_knot = label |
||
355 | selected_x = x[rect_idx].ravel() |
||
356 | selected_y = y[rect_idx].ravel() |
||
357 | selected_ind = knots[label].index[rect_idx] |
||
358 | log.debug(f'Selected {label} points rect_idx {rect_idx} x {x[rect_idx]}, y {y[rect_idx]} with indices {selected_ind}') |
||
359 | textbox.begin_typing(None) |
||
360 | break # if we find selected components in this epoch, continue with renaming |
||
361 | else: |
||
362 | pass |
||
363 | |||
364 | update(slider_date.val) |
||
365 | |||
366 | |||
367 | View Code Duplication | def toggle_selector(event): |
|
368 | log.debug('GUI: Key pressed.', end=' ') |
||
369 | if event.key in ['Q', 'q'] and toggle_selector.RS.active: |
||
370 | log.debug('Selector deactivated.') |
||
371 | toggle_selector.RS.set_active(False) |
||
372 | if event.key in ['S', 's'] and not toggle_selector.RS.active: |
||
373 | log.debug('Selector activated.') |
||
374 | toggle_selector.RS.set_active(True) |
||
375 | if event.key in ['R', 'r']: |
||
376 | log.debug('Selector deactivated.') |
||
377 | toggle_selector.RS.set_active(False) |
||
378 | textbox.begin_typing(None) |
||
379 | #textbox.set_val('') |
||
380 | |||
381 | |||
382 | toggle_selector.RS = RectangleSelector(ax, line_select_callback, |
||
383 | drawtype='box', useblit=True, |
||
384 | button=[1, 3], # don't use middle button |
||
385 | minspanx=0, minspany=0, |
||
386 | spancoords='data', |
||
387 | interactive=False) |
||
388 | |||
389 | |||
390 | #plt.connect('key_press_event', toggle_selector) |
||
391 | fig.canvas.mpl_connect('key_press_event', toggle_selector) |
||
392 | |||
393 | |||
394 | |||
395 | from mpl_toolkits.axes_grid1 import make_axes_locatable |
||
396 | |||
397 | divider_slider = make_axes_locatable(ax) |
||
398 | slider_ax = divider_slider.append_axes("top", size="3%", pad="4%") |
||
399 | slider_date = Slider(ax=slider_ax, label="Date", valmin=2007, valmax=2020, valinit=2008, valstep=0.2, orientation="horizontal") |
||
400 | slider_date.on_changed(update) |
||
401 | |||
402 | #divider_textbox = make_axes_locatable(ax) |
||
403 | #textbox_ax = divider_textbox.append_axes("bottom", size="3%", pad="4%") |
||
404 | textbox_ax = fig.add_axes([0.3,0.015,0.5,0.05]) |
||
405 | textbox = TextBox(textbox_ax, 'Knot name:', initial='None') |
||
406 | textbox.on_submit(submit_textbox) |
||
407 | |||
408 | |||
409 | |||
410 | ax.set_xlim([-1.0, +1.0]) |
||
411 | ax.set_ylim([-1.0, +1.0]) |
||
412 | ax.set_aspect('equal') |
||
413 | |||
414 | fig.suptitle('S to select, R to rename, Q to deactivate selector') |
||
415 | |||
416 | print('S to select, R to rename, Q to deactivate selector') |
||
417 | print('(you can select points from one component at a time)') |
||
418 | print('(if you use the zoom or movement tools, remember to unselect them)') |
||
419 | |||
420 | plt.show() |
||
421 | |||
422 | return mod |
||
423 | |||
424 | |||
425 | |||
426 | |||
427 | |||
428 | |||
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 |
||