lime-stable 2.0.dev4__py3-none-any.whl → 2.0.dev7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lime/__init__.py +1 -1
- lime/archives/read_fits.py +1 -1
- lime/changelog.txt +2 -1
- lime/fitting/lines.py +5 -5
- lime/inference/intensity_threshold.py +21 -89
- lime/lime.toml +1 -1
- lime/plotting/bokeh_plots.py +23 -15
- lime/plotting/format.py +5 -1
- lime/plotting/plots.py +85 -91
- lime/plotting/utils.py +5 -5
- lime/tools.py +7 -1
- lime/transitions.py +69 -37
- lime/workflow.py +36 -9
- {lime_stable-2.0.dev4.dist-info → lime_stable-2.0.dev7.dist-info}/METADATA +2 -2
- {lime_stable-2.0.dev4.dist-info → lime_stable-2.0.dev7.dist-info}/RECORD +18 -18
- {lime_stable-2.0.dev4.dist-info → lime_stable-2.0.dev7.dist-info}/WHEEL +1 -1
- {lime_stable-2.0.dev4.dist-info → lime_stable-2.0.dev7.dist-info}/licenses/LICENSE.rst +0 -0
- {lime_stable-2.0.dev4.dist-info → lime_stable-2.0.dev7.dist-info}/top_level.txt +0 -0
lime/__init__.py
CHANGED
|
@@ -42,7 +42,7 @@ _logger.debug(f'Launching LiMe {__version__} in Python {__python_version__}')
|
|
|
42
42
|
from lime.observations import Spectrum, Sample, Cube
|
|
43
43
|
from lime.io import *
|
|
44
44
|
from lime.tools import *
|
|
45
|
-
from lime.transitions import Line, label_decomposition,
|
|
45
|
+
from lime.transitions import Line, label_decomposition, bands_from_measurements
|
|
46
46
|
from lime.archives.read_fits import OpenFits, show_instrument_cfg
|
|
47
47
|
from lime.plotting.plots import theme
|
|
48
48
|
from lime.workflow import line_bands
|
lime/archives/read_fits.py
CHANGED
|
@@ -154,7 +154,7 @@ def check_fits_source(fits_source, lime_object=None, load_function=None):
|
|
|
154
154
|
else:
|
|
155
155
|
|
|
156
156
|
if load_function is None:
|
|
157
|
-
|
|
157
|
+
_logger.warning(f'Please introduce fits file instrument or a load function to import the fits file as a '
|
|
158
158
|
f'LiMe observation')
|
|
159
159
|
|
|
160
160
|
return fits_source, spectrum_type
|
lime/changelog.txt
CHANGED
|
@@ -109,4 +109,5 @@ LiMe Mayor update - 2.0.0 - XX/XX/XXXX
|
|
|
109
109
|
- Rename term "_conf" to "_cfg" across function names and arguments for uniformity
|
|
110
110
|
- Fitting functions now have the update_default=True argument. In the default behaviour the default configuration updates the default one. If set to update_default=False only the obj_cfg is used if available else the default one.
|
|
111
111
|
- Creating a spectrum object where all the entries are masked now produces a critical warning instead of raising an error.
|
|
112
|
-
- By default line_bands
|
|
112
|
+
- By default line_bands.
|
|
113
|
+
- Added bands_from_log function
|
lime/fitting/lines.py
CHANGED
|
@@ -180,7 +180,6 @@ def pp_FWHM(line, idx):
|
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
def gaussian_area(line, idx, n_steps):
|
|
183
|
-
|
|
184
183
|
amp = np.random.normal(line.amp[idx], line.amp_err[idx], n_steps)
|
|
185
184
|
sigma = np.random.normal(line.sigma[idx], line.sigma_err[idx], n_steps)
|
|
186
185
|
|
|
@@ -470,8 +469,6 @@ class ProfileModelCompiler:
|
|
|
470
469
|
self.model.prefix = f'line0_'
|
|
471
470
|
|
|
472
471
|
# Fix or not the continuum
|
|
473
|
-
# m_cont_conf = _SLOPE_FIX_PAR if line._cont_from_adjacent else _SLOPE_FREE_PAR
|
|
474
|
-
# n_cont_conf = _INTER_FIX_PAR if line._cont_from_adjacent else _INTER_FREE_PAR
|
|
475
472
|
self.define_param(0, line, 'm_cont', line.m_cont, _SLOPE_FIX_PAR, user_conf)
|
|
476
473
|
self.define_param(0, line, 'n_cont', line.n_cont, _INTER_FIX_PAR, user_conf)
|
|
477
474
|
|
|
@@ -616,8 +613,11 @@ class ProfileModelCompiler:
|
|
|
616
613
|
# Check for negative -0.0 # TODO this needs a better place # FIXME -0.0 error
|
|
617
614
|
if np.signbit(line.sigma_err[i]):
|
|
618
615
|
line.sigma_err[i] = np.nan
|
|
619
|
-
|
|
620
|
-
|
|
616
|
+
_logger.warning(f'Negative scale value for amplitude at {comp_label}')
|
|
617
|
+
|
|
618
|
+
if np.signbit(line.amp_err[i]):
|
|
619
|
+
line.amp_err[i] = np.nan
|
|
620
|
+
_logger.warning(f'Negative scale value for amplitude at {comp_label}')
|
|
621
621
|
|
|
622
622
|
# Compute the profile areas
|
|
623
623
|
profile_flux_dist = AREA_FUNCTIONS[line._p_shape[i]](line, i, 1000)
|
|
@@ -52,8 +52,8 @@ class LineFinder:
|
|
|
52
52
|
|
|
53
53
|
return
|
|
54
54
|
|
|
55
|
-
def peaks_troughs(self, bands, sigma_threshold=3, emission_type=True, width_tol=5,
|
|
56
|
-
continuum_array=None, continuum_std=None, plot_steps=False,
|
|
55
|
+
def peaks_troughs(self, bands, sigma_threshold=3, emission_type=True, width_tol=5,
|
|
56
|
+
continuum_array=None, continuum_std=None, plot_steps=False, **kwargs):
|
|
57
57
|
|
|
58
58
|
"""
|
|
59
59
|
|
|
@@ -82,9 +82,6 @@ class LineFinder:
|
|
|
82
82
|
:param width_tol: Minimum number of pixels between peaks/troughs. The default value is 5.
|
|
83
83
|
:type width_tol: float, optional
|
|
84
84
|
|
|
85
|
-
:param band_modification: Method to adjust the line band with. The default value is None.
|
|
86
|
-
:type band_modification: str, optional
|
|
87
|
-
|
|
88
85
|
:param ml_detection: Machine learning algorithm to detect lines. The default value is None.
|
|
89
86
|
:type ml_detection: str, optional
|
|
90
87
|
|
|
@@ -105,101 +102,36 @@ class LineFinder:
|
|
|
105
102
|
|
|
106
103
|
# Match peaks with theoretical lines
|
|
107
104
|
bands = check_file_dataframe(bands)
|
|
108
|
-
matched_DF = self.label_peaks(idcs_peaks, bands, width_tol=width_tol,
|
|
109
|
-
line_type=emission_type)
|
|
105
|
+
matched_DF = self.label_peaks(idcs_peaks, bands, width_tol=width_tol, line_type=emission_type)
|
|
110
106
|
|
|
111
107
|
# Plot the results
|
|
112
108
|
if plot_steps:
|
|
113
|
-
plot_peaks_troughs(self._spec, idcs_peaks, limit_threshold, continuum_array, matched_DF,
|
|
109
|
+
plot_peaks_troughs(self._spec, idcs_peaks, limit_threshold, continuum_array, matched_DF, **kwargs)
|
|
114
110
|
|
|
115
111
|
return matched_DF
|
|
116
112
|
|
|
117
113
|
|
|
118
|
-
def label_peaks(self,
|
|
119
|
-
|
|
120
|
-
# TODO auto param should be changed to boolean
|
|
121
|
-
# Establish the type of input values for the peak indexes, first numpy array
|
|
122
|
-
if isinstance(peak_table, np.ndarray):
|
|
123
|
-
idcsLinePeak = peak_table
|
|
124
|
-
|
|
125
|
-
# Specutils table
|
|
126
|
-
else:
|
|
127
|
-
# Query the lines from the astropy finder archives #
|
|
128
|
-
if len(peak_table) != 0:
|
|
129
|
-
idcsLineType = peak_table['emission_type'] == line_type
|
|
130
|
-
idcsLinePeak = np.array(peak_table[idcsLineType]['line_center_index'])
|
|
131
|
-
else:
|
|
132
|
-
idcsLinePeak = np.array([])
|
|
114
|
+
def label_peaks(self, idcs_peaks, matched_DF, line_type='emission', width_tol=5):
|
|
133
115
|
|
|
134
116
|
# Security check in case no lines detected
|
|
135
|
-
if len(
|
|
136
|
-
return pd.DataFrame(columns=
|
|
137
|
-
|
|
138
|
-
# Exclude bands not withing the regime:
|
|
139
|
-
w0, wf = self._spec.wave_rest.data[~self._spec.wave_rest.mask][0], self._spec.wave_rest.data[~self._spec.wave_rest.mask][-1]
|
|
140
|
-
idcs_selection = (mask_df.w3 > w0) & (mask_df.w4 < wf)
|
|
141
|
-
|
|
142
|
-
# Prepare dataframe to stored the matched lines
|
|
143
|
-
matched_DF = mask_df.loc[idcs_selection].copy()
|
|
144
|
-
matched_DF['signal_peak'] = np.nan
|
|
145
|
-
|
|
146
|
-
# Theoretical wave values
|
|
147
|
-
waveTheory = label_decomposition(matched_DF.index.values, params_list=['wavelength'])[0]
|
|
148
|
-
matched_DF['wavelength'] = waveTheory
|
|
149
|
-
|
|
150
|
-
# Match the lines with the theoretical emission
|
|
151
|
-
tolerance = np.diff(self._spec.wave_rest).mean() * width_tol
|
|
152
|
-
matched_DF['observation'] = 'not_detected'
|
|
153
|
-
unidentifiedLine = dict.fromkeys(matched_DF.columns.values, np.nan)
|
|
154
|
-
|
|
155
|
-
# Get the wavelength peaks
|
|
156
|
-
wave_peaks = self._spec.wave_rest[idcsLinePeak]
|
|
157
|
-
|
|
158
|
-
for i in np.arange(wave_peaks.size):
|
|
159
|
-
|
|
160
|
-
idx_array = np.where(np.isclose(a=waveTheory.astype(np.float64), b=wave_peaks[i], atol=tolerance))
|
|
161
|
-
|
|
162
|
-
if len(idx_array[0]) == 0:
|
|
163
|
-
unknownLineLabel = 'xy_{:.0f}A'.format(np.round(wave_peaks[i]))
|
|
164
|
-
|
|
165
|
-
# Scheme to avoid repeated lines
|
|
166
|
-
if (unknownLineLabel not in matched_DF.index) and detect_check:
|
|
167
|
-
newRow = unidentifiedLine.copy()
|
|
168
|
-
newRow.update({'wavelength': wave_peaks[i], 'w3': wave_peaks[i] - 5, 'w4': wave_peaks[i] + 5,
|
|
169
|
-
'observation': 'not_identified'})
|
|
170
|
-
matched_DF.loc[unknownLineLabel] = newRow
|
|
171
|
-
|
|
172
|
-
else:
|
|
173
|
-
|
|
174
|
-
row_index = matched_DF.index[matched_DF.wavelength == waveTheory[idx_array[0][0]]]
|
|
175
|
-
matched_DF.loc[row_index, 'observation'] = 'detected'
|
|
176
|
-
matched_DF.loc[row_index, 'signal_peak'] = idcsLinePeak[i]
|
|
177
|
-
theoLineLabel = row_index[0]
|
|
178
|
-
|
|
179
|
-
blended_check = True if '_b' in theoLineLabel else False
|
|
180
|
-
minSeparation = 4 if blended_check else 2
|
|
117
|
+
if len(idcs_peaks) == 0:
|
|
118
|
+
return pd.DataFrame(columns=matched_DF.columns)
|
|
181
119
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if blended_check is False:
|
|
186
|
-
emission_check = True if line_type == 'emission' else False
|
|
187
|
-
idx_min = compute_line_width(idcsLinePeak[i], self._spec.flux, delta_i=-1, min_delta=minSeparation, emission_check=emission_check)
|
|
188
|
-
idx_max = compute_line_width(idcsLinePeak[i], self._spec.flux, delta_i=1, min_delta=minSeparation, emission_check=emission_check)
|
|
189
|
-
matched_DF.loc[row_index, 'w3'] = self._spec.wave_rest[idx_min]
|
|
190
|
-
matched_DF.loc[row_index, 'w4'] = self._spec.wave_rest[idx_max]
|
|
120
|
+
# Add theoretical wavelength values if necessary
|
|
121
|
+
if 'wavelength' not in matched_DF.columns:
|
|
122
|
+
matched_DF['wavelength'] = label_decomposition(matched_DF.index.values, params_list=['wavelength'])[0]
|
|
191
123
|
|
|
192
|
-
#
|
|
193
|
-
|
|
194
|
-
|
|
124
|
+
# Get bands limits indexes
|
|
125
|
+
idcs_w3 = np.searchsorted(self._spec.wave_rest, matched_DF.w3)
|
|
126
|
+
idcs_w4 = np.searchsorted(self._spec.wave_rest, matched_DF.w4)
|
|
195
127
|
|
|
196
|
-
#
|
|
197
|
-
|
|
198
|
-
|
|
128
|
+
# Get the bands matching
|
|
129
|
+
band_contains_peak = (idcs_peaks[None, :] > idcs_w3[:, None]) & (idcs_peaks[None, :] < idcs_w4[:, None])
|
|
130
|
+
idcs_matched_bands = band_contains_peak.any(axis=1)
|
|
131
|
+
idcs_matched_peaks = idcs_peaks[band_contains_peak.argmax(axis=1)[idcs_matched_bands]]
|
|
199
132
|
|
|
200
|
-
#
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
matched_DF = matched_DF.loc[idcs_valid]
|
|
133
|
+
# Crop the bands to the detection
|
|
134
|
+
matched_DF.loc[idcs_matched_bands, 'observation'] = 'detected'
|
|
135
|
+
matched_DF.loc[idcs_matched_bands, 'signal_peak'] = idcs_matched_peaks
|
|
204
136
|
|
|
205
|
-
return matched_DF
|
|
137
|
+
return matched_DF.loc[idcs_matched_bands]
|
lime/lime.toml
CHANGED
lime/plotting/bokeh_plots.py
CHANGED
|
@@ -102,14 +102,7 @@ def bokeh_bands(fig, bands, x, y, z_corr, redshift):
|
|
|
102
102
|
|
|
103
103
|
# Loop through the detections and plot the names
|
|
104
104
|
for i in np.arange(latex.size):
|
|
105
|
-
if idcs_band_limits[0, i] != idcs_band_limits[0, i]: # Y limit for the label check if same pixel
|
|
106
|
-
max_region = np.max(y[idcs_band_limits[0, i]:idcs_band_limits[0, i]])
|
|
107
|
-
else:
|
|
108
|
-
max_region = y[idcs_band_limits[0, i]]
|
|
109
|
-
|
|
110
|
-
label = 'Matched line' if i == 0 else '_'
|
|
111
105
|
fig.add_layout(BoxAnnotation(left=w3_obs[i]/z_corr, right=w4_obs[i]/z_corr, fill_alpha=0.3, fill_color=theme.colors['match_line']))
|
|
112
|
-
# axis.text(wave_array[i] * (1 + redshift) / z_corr, max_region * 0.9 * z_corr, latex[i], rotation=270)
|
|
113
106
|
|
|
114
107
|
return
|
|
115
108
|
|
|
@@ -124,16 +117,25 @@ def bands_filling_bokeh(fig, x, y, z_corr, idcs_mask, label, exclude_continua=Fa
|
|
|
124
117
|
low_lim = 0 if np.isnan(low_lim) else low_lim
|
|
125
118
|
|
|
126
119
|
# Central bands
|
|
120
|
+
print('1',x[idcs_mask[2]:idcs_mask[3]]/z_corr)
|
|
121
|
+
print('1',y[idcs_mask[2]:idcs_mask[3]]*z_corr)
|
|
122
|
+
print('1',low_lim*z_corr)
|
|
127
123
|
fig.varea_step(x=x[idcs_mask[2]:idcs_mask[3]]/z_corr, y1=low_lim*z_corr, y2=y[idcs_mask[2]:idcs_mask[3]]*z_corr,
|
|
128
124
|
step_mode="center", fill_alpha=0.25, color=color_dict['line_band'])
|
|
129
125
|
|
|
130
126
|
# Continua bands exclusion
|
|
131
127
|
if exclude_continua is False:
|
|
128
|
+
print('2', x[idcs_mask[0]:idcs_mask[1]]/z_corr)
|
|
129
|
+
print('2', low_lim*z_corr)
|
|
130
|
+
print('2', y[idcs_mask[0]:idcs_mask[1]]*z_corr)
|
|
132
131
|
fig.varea_step(x=x[idcs_mask[0]:idcs_mask[1]]/z_corr,
|
|
133
132
|
y1=low_lim*z_corr,
|
|
134
133
|
y2=y[idcs_mask[0]:idcs_mask[1]]*z_corr,
|
|
135
134
|
step_mode="center", fill_alpha=0.25, color=color_dict['cont_band'])
|
|
136
135
|
|
|
136
|
+
print('3', x[idcs_mask[4]:idcs_mask[5]]/z_corr)
|
|
137
|
+
print('3', low_lim*z_corr)
|
|
138
|
+
print('3', y[idcs_mask[4]:idcs_mask[5]]*z_corr)
|
|
137
139
|
fig.varea_step(x=x[idcs_mask[4]:idcs_mask[5]]/z_corr,
|
|
138
140
|
y1=low_lim*z_corr,
|
|
139
141
|
y2=y[idcs_mask[4]:idcs_mask[5]]*z_corr,
|
|
@@ -193,8 +195,8 @@ class BokehFigures:
|
|
|
193
195
|
|
|
194
196
|
return
|
|
195
197
|
|
|
196
|
-
def bands(self, label, output_address=None,
|
|
197
|
-
ax_cfg=None, return_fig=False):
|
|
198
|
+
def bands(self, label, output_address=None, bands=None, include_fits=True, rest_frame=False, log_scale=True,
|
|
199
|
+
exclude_continua=True, fig_cfg=None, ax_cfg=None, return_fig=False):
|
|
198
200
|
|
|
199
201
|
|
|
200
202
|
# Unpack variables
|
|
@@ -205,7 +207,7 @@ class BokehFigures:
|
|
|
205
207
|
legend_check = True if label is not None else False
|
|
206
208
|
|
|
207
209
|
# Check which line should be plotted
|
|
208
|
-
line = parse_bands_arguments(label, log,
|
|
210
|
+
line = parse_bands_arguments(label, log, bands, norm_flux)
|
|
209
211
|
|
|
210
212
|
# Proceed to plot
|
|
211
213
|
if line is not None:
|
|
@@ -232,16 +234,13 @@ class BokehFigures:
|
|
|
232
234
|
# Create figure with default utils if not provided
|
|
233
235
|
fig = figure(tools=PLT_CONF.get('tools', "pan,wheel_zoom,box_zoom,reset,save"), y_axis_type=scale_str)
|
|
234
236
|
|
|
235
|
-
# # Create figure with default utils if not provided
|
|
236
|
-
# fig = figure(tools=PLT_CONF.get('tools', "pan,wheel_zoom,box_zoom,reset,save"))
|
|
237
|
-
|
|
238
237
|
# Spectrum data source
|
|
239
238
|
source = ColumnDataSource(data={"x": wave_plot[idcs_bands[0]:idcs_bands[5]] / z_corr,
|
|
240
239
|
"y": flux_plot[idcs_bands[0]:idcs_bands[5]] * z_corr})
|
|
241
240
|
fig.step("x", "y", source=source, color=theme.colors['fg'], line_width=1, mode='center')
|
|
242
241
|
|
|
243
242
|
# Fille the bands
|
|
244
|
-
bands_filling_bokeh(fig, wave_plot, flux_plot, z_corr, idcs_bands, line)
|
|
243
|
+
bands_filling_bokeh(fig, wave_plot, flux_plot, z_corr, idcs_bands, line, exclude_continua=exclude_continua)
|
|
245
244
|
|
|
246
245
|
# Plot labels
|
|
247
246
|
fig.xaxis.axis_label = AXES_CONF['xlabel']
|
|
@@ -250,6 +249,9 @@ class BokehFigures:
|
|
|
250
249
|
# Adjust the format of the plot
|
|
251
250
|
update_bokeh_figure(fig, PLT_CONF)
|
|
252
251
|
|
|
252
|
+
# Hide the legend if there are line profiles
|
|
253
|
+
fig.legend.visible = legend_check
|
|
254
|
+
|
|
253
255
|
# Save or display the plot
|
|
254
256
|
if return_fig:
|
|
255
257
|
return fig
|
|
@@ -258,11 +260,14 @@ class BokehFigures:
|
|
|
258
260
|
save(fig, filename=output_address)
|
|
259
261
|
|
|
260
262
|
else:
|
|
261
|
-
# output_notebook()
|
|
262
263
|
show(fig)
|
|
263
264
|
|
|
265
|
+
else:
|
|
266
|
+
_logger.info(f'The input line {label} could not be found')
|
|
267
|
+
|
|
264
268
|
return
|
|
265
269
|
|
|
270
|
+
|
|
266
271
|
def grid(self, output_address=None, rest_frame=True, log_scale=False, n_cols=6, n_rows=None, col_row_scale=(2, 1.5),
|
|
267
272
|
include_fits=True, in_fig=None, fig_cfg=None, ax_cfg=None, maximize=False):
|
|
268
273
|
|
|
@@ -410,6 +415,9 @@ class BokehFigures:
|
|
|
410
415
|
# Adjust the format of the plot
|
|
411
416
|
update_bokeh_figure(fig, PLT_CONF)
|
|
412
417
|
|
|
418
|
+
# Hide the legend if there are line profiles
|
|
419
|
+
fig.legend.visible = legend_check
|
|
420
|
+
|
|
413
421
|
# Save or display the plot
|
|
414
422
|
if return_fig:
|
|
415
423
|
return fig
|
lime/plotting/format.py
CHANGED
|
@@ -54,6 +54,7 @@ class Themer:
|
|
|
54
54
|
self.conf = None # All the formating data
|
|
55
55
|
self.style = None # Label of the active style
|
|
56
56
|
self.active_conf = None # Dictionary with the active figure configuration and library
|
|
57
|
+
self.default_lib = 'matplotlib'
|
|
57
58
|
|
|
58
59
|
# LiMe plots personalization
|
|
59
60
|
self.colors = None # Features individual colors
|
|
@@ -67,6 +68,8 @@ class Themer:
|
|
|
67
68
|
self.conf = conf.copy()
|
|
68
69
|
self.set_style(style)
|
|
69
70
|
|
|
71
|
+
|
|
72
|
+
|
|
70
73
|
return
|
|
71
74
|
|
|
72
75
|
|
|
@@ -163,7 +166,7 @@ class Themer:
|
|
|
163
166
|
return ax_cfg
|
|
164
167
|
|
|
165
168
|
|
|
166
|
-
def set_style(self, style=None, scale=None, colors_conf=None, library=
|
|
169
|
+
def set_style(self, style=None, scale=None, colors_conf=None, library='matplotlib'):
|
|
167
170
|
|
|
168
171
|
# Set the default style
|
|
169
172
|
# self.style = ['default']
|
|
@@ -173,6 +176,7 @@ class Themer:
|
|
|
173
176
|
# self.style += [style] if isinstance(style, str) else style
|
|
174
177
|
self.style = 'default' if style is None else style
|
|
175
178
|
self.scale = ['default'] if style is None else [scale]
|
|
179
|
+
self.default_lib = library
|
|
176
180
|
|
|
177
181
|
# Set the library defaults
|
|
178
182
|
self.active_conf = {'matplotlib': self.conf['matplotlib']['default'].copy(),
|
lime/plotting/plots.py
CHANGED
|
@@ -159,7 +159,10 @@ def _auto_flux_scale(axis, y, y_scale, scale_dict=theme.plt):
|
|
|
159
159
|
ratio = np.abs(y_max/y_min)
|
|
160
160
|
if (ratio > 25) or (ratio < 0.06):
|
|
161
161
|
if neg_check:
|
|
162
|
-
|
|
162
|
+
if np.sum(y>0) > 1:
|
|
163
|
+
y_scale = {'value': 'symlog', 'linthresh': min(np.ceil(np.abs(y_min)), np.min(y[y>0]))}
|
|
164
|
+
else:
|
|
165
|
+
y_scale = {'value': 'symlog', 'linthresh': np.ceil(np.abs(y_min))}
|
|
163
166
|
else:
|
|
164
167
|
y_scale = {'value': 'log'}
|
|
165
168
|
else:
|
|
@@ -657,7 +660,7 @@ def redshift_permu_evaluation(spectrum, z_infered, obs_wave_arr, theo_wave_arr,
|
|
|
657
660
|
def bands_filling_plot(axis, x, y, z_corr, idcs_mask, label, exclude_continua=False, color_dict=theme.colors, show_central=True):
|
|
658
661
|
|
|
659
662
|
# Security check for low selection
|
|
660
|
-
if
|
|
663
|
+
if y[idcs_mask[2]:idcs_mask[3]].size > 1:
|
|
661
664
|
|
|
662
665
|
# Lower limit for the filled region
|
|
663
666
|
if exclude_continua is False:
|
|
@@ -666,10 +669,10 @@ def bands_filling_plot(axis, x, y, z_corr, idcs_mask, label, exclude_continua=Fa
|
|
|
666
669
|
x_interval = x[idcs_mask[2]:idcs_mask[3]]
|
|
667
670
|
y_interval = y[idcs_mask[2]:idcs_mask[3]]
|
|
668
671
|
else:
|
|
669
|
-
m = (y[idcs_mask[3]] - y[idcs_mask[2]])/(x[idcs_mask[3]] - x[idcs_mask[2]])
|
|
670
|
-
n = y[idcs_mask[2]] - m * x[idcs_mask[2]]
|
|
671
672
|
x_interval = x[idcs_mask[2]:idcs_mask[3]]
|
|
672
673
|
y_interval = y[idcs_mask[2]:idcs_mask[3]]
|
|
674
|
+
m = (y_interval[-1] - y_interval[0])/(x_interval[-1] - x_interval[0])
|
|
675
|
+
n = y_interval[0] - m * x_interval[0]
|
|
673
676
|
low_lim = m * x_interval + n
|
|
674
677
|
|
|
675
678
|
# Central bands
|
|
@@ -690,7 +693,7 @@ def bands_filling_plot(axis, x, y, z_corr, idcs_mask, label, exclude_continua=Fa
|
|
|
690
693
|
return
|
|
691
694
|
|
|
692
695
|
|
|
693
|
-
def plot_peaks_troughs(spec, peak_idcs, detect_limit, continuum, match_bands,
|
|
696
|
+
def plot_peaks_troughs(spec, peak_idcs, detect_limit, continuum, match_bands, **kwargs):
|
|
694
697
|
|
|
695
698
|
norm_flux = spec.norm_flux
|
|
696
699
|
wave = spec.wave
|
|
@@ -724,9 +727,6 @@ def plot_peaks_troughs(spec, peak_idcs, detect_limit, continuum, match_bands, lo
|
|
|
724
727
|
ax.scatter(wave_plot[idcs_mask], flux_plot[idcs_mask], label='Masked pixels', marker='x',
|
|
725
728
|
color=theme.colors['mask_marker'])
|
|
726
729
|
|
|
727
|
-
if log_scale:
|
|
728
|
-
ax.set_yscale('log')
|
|
729
|
-
|
|
730
730
|
ax.legend()
|
|
731
731
|
ax.update(AXES_CONF)
|
|
732
732
|
plt.tight_layout()
|
|
@@ -783,7 +783,6 @@ class Plotter:
|
|
|
783
783
|
|
|
784
784
|
# Loop through the detections and plot the names
|
|
785
785
|
for i, line_label in enumerate(match_log.index):
|
|
786
|
-
print(line_label)
|
|
787
786
|
line = Line(line_label, match_log)
|
|
788
787
|
|
|
789
788
|
# Get the max flux on the region making the exception for 1 pixel bands
|
|
@@ -792,7 +791,7 @@ class Plotter:
|
|
|
792
791
|
|
|
793
792
|
x_text = line.wavelength * (1 + redshift)/z_corr if not line.blended_check else line.wavelength[0] * (1 + redshift)/z_corr
|
|
794
793
|
y_text = max_region * 0.9 * z_corr
|
|
795
|
-
text = line.
|
|
794
|
+
text = line.label
|
|
796
795
|
|
|
797
796
|
axis.text(x_text, y_text, text, rotation=270)
|
|
798
797
|
axis.axvspan(x[idx_w3]/z_corr, x[idx_w4]/z_corr, label='Matched line' if i == 0 else '_', alpha=0.30,
|
|
@@ -1009,84 +1008,79 @@ class SpectrumFigures(Plotter):
|
|
|
1009
1008
|
in_ax.fill_between(wave_plot/z_corr, low_limit*z_corr, high_limit*z_corr, alpha=0.2,
|
|
1010
1009
|
color=theme.colors['fade_fg'])
|
|
1011
1010
|
|
|
1012
|
-
# Include the detection bands
|
|
1013
|
-
if detection_band is not None:
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
bins = [40, 60, 80, 100]
|
|
1011
|
+
# # Include the detection bands
|
|
1012
|
+
# if detection_band is not None:
|
|
1013
|
+
#
|
|
1014
|
+
# detec_obj = getattr(self._spec.infer, detection_band)
|
|
1015
|
+
#
|
|
1016
|
+
# if detec_obj.confidence is not None:
|
|
1017
|
+
#
|
|
1018
|
+
# # Boundaries array for confidence intervals
|
|
1019
|
+
# bounds = np.array([0.0, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])
|
|
1020
|
+
#
|
|
1021
|
+
# # Adjust color map to match lower detection limit to fg color
|
|
1022
|
+
# cmap = plt.get_cmap(theme.colors['mask_map'])
|
|
1023
|
+
# cmaplist = [cmap(i) for i in range(cmap.N)]
|
|
1024
|
+
# cmaplist[0] = theme.colors['fg']
|
|
1025
|
+
# cmap = colors.LinearSegmentedColormap.from_list('mcm', cmaplist, bounds.size-1)
|
|
1026
|
+
# norm = colors.BoundaryNorm(bounds * 100, cmap.N)
|
|
1027
|
+
#
|
|
1028
|
+
# # Iterate through the confidence intervals and plot the step spectrum
|
|
1029
|
+
# for i in range(1, len(bounds)):
|
|
1030
|
+
# if i > 1:
|
|
1031
|
+
# idcs = detec_obj(bounds[i-1]*100, confidence_max=bounds[i]*100)
|
|
1032
|
+
# wave_nan, flux_nan = np.full(wave_plot.size, np.nan), np.full(flux_plot.size, np.nan)
|
|
1033
|
+
# wave_nan[idcs], flux_nan[idcs] = wave_plot[idcs] / z_corr, flux_plot[idcs] * z_corr
|
|
1034
|
+
#
|
|
1035
|
+
# in_ax.step(wave_nan, flux_nan, label=label, where='mid', color=cmap(i-1))
|
|
1036
|
+
#
|
|
1037
|
+
# # Color bar
|
|
1038
|
+
# sm = cm.ScalarMappable(cmap=cmap, norm=norm)
|
|
1039
|
+
# sm.set_array([])
|
|
1040
|
+
# cbar = plt.colorbar(sm, ax=in_ax)
|
|
1041
|
+
# cbar.set_label('Detection confidence %', rotation=270, labelpad=35)
|
|
1042
|
+
#
|
|
1043
|
+
#
|
|
1044
|
+
# else:
|
|
1045
|
+
# _logger.warning(f'The line detection bands confidence has not been calculated. They are not included'
|
|
1046
|
+
# f' on plot.')
|
|
1047
|
+
|
|
1048
|
+
# Show components
|
|
1049
|
+
if show_categories and self._spec.infer.pred_arr is not None:
|
|
1052
1050
|
|
|
1053
1051
|
# Use np.histogram to get the counts in each bin
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
linestyle=category_conf_styles[idx_conf])
|
|
1087
|
-
|
|
1088
|
-
else:
|
|
1089
|
-
_logger.warning('Aspect needs to be installed to display the spectrum components')
|
|
1052
|
+
categories = np.sort(np.unique(self._spec.infer.pred_arr))
|
|
1053
|
+
legend_scatter = []
|
|
1054
|
+
|
|
1055
|
+
for category in categories:
|
|
1056
|
+
if category != 0:
|
|
1057
|
+
|
|
1058
|
+
# Get category properties
|
|
1059
|
+
feature_name = self._spec.infer.model_mgr.medium.number_feature_dict[category]
|
|
1060
|
+
feature_color = aspect.cfg['colors'][feature_name]
|
|
1061
|
+
idcs_feature = self._spec.infer.pred_arr == category
|
|
1062
|
+
legend_scatter.append(mlines.Line2D([], [], marker='o', color='w',
|
|
1063
|
+
markerfacecolor=feature_color, markersize=8, label=feature_name))
|
|
1064
|
+
|
|
1065
|
+
# Count the pixels for each category
|
|
1066
|
+
bins = [40, 60, 80, 100]
|
|
1067
|
+
counts, _ = np.histogram(self._spec.infer.conf_arr[idcs_feature], bins=bins)
|
|
1068
|
+
for idx_conf, count_conf in enumerate(counts):
|
|
1069
|
+
if count_conf > 0:
|
|
1070
|
+
|
|
1071
|
+
# Get indeces matching the detections
|
|
1072
|
+
idcs_count = np.where((bins[idx_conf] < self._spec.infer.conf_arr[idcs_feature]) &
|
|
1073
|
+
(self._spec.infer.conf_arr[idcs_feature] <= bins[idx_conf + 1]))[0]
|
|
1074
|
+
idcs_nonnan = np.where(idcs_feature)[0][idcs_count] # Returns indices where mask is True
|
|
1075
|
+
|
|
1076
|
+
# Generate nan arrays with the data to avoid filling non detections
|
|
1077
|
+
wave_nan, flux_nan = np.full(wave_plot.size, np.nan), np.full(flux_plot.size, np.nan)
|
|
1078
|
+
wave_nan[idcs_nonnan] = wave_plot[idcs_nonnan] / z_corr
|
|
1079
|
+
flux_nan[idcs_nonnan] = flux_plot[idcs_nonnan] * z_corr
|
|
1080
|
+
|
|
1081
|
+
# Plot with the corresponding colors and linestyle
|
|
1082
|
+
in_ax.step(wave_nan, flux_nan, label=feature_name, where='mid', color=feature_color,
|
|
1083
|
+
linestyle=category_conf_styles[idx_conf])
|
|
1090
1084
|
|
|
1091
1085
|
# Legend category
|
|
1092
1086
|
legend_category = in_ax.legend(handles=legend_scatter, edgecolor=theme.colors['fg'])
|
|
@@ -1275,8 +1269,8 @@ class SpectrumFigures(Plotter):
|
|
|
1275
1269
|
|
|
1276
1270
|
return in_fig
|
|
1277
1271
|
|
|
1278
|
-
def bands(self, label=None,
|
|
1279
|
-
ax_cfg=None, in_fig=None, maximize=False, show_err=
|
|
1272
|
+
def bands(self, label=None, bands=None, output_address=None, include_fits=True, rest_frame=False, y_scale='auto', fig_cfg=None,
|
|
1273
|
+
ax_cfg=None, in_fig=None, maximize=False, show_err=False, exclude_continua=False):
|
|
1280
1274
|
|
|
1281
1275
|
"""
|
|
1282
1276
|
|
|
@@ -1333,7 +1327,7 @@ class SpectrumFigures(Plotter):
|
|
|
1333
1327
|
display_check = True if in_fig is None else False
|
|
1334
1328
|
|
|
1335
1329
|
# Check which line should be plotted
|
|
1336
|
-
line = parse_bands_arguments(label,
|
|
1330
|
+
line = parse_bands_arguments(label, bands, log, norm_flux)
|
|
1337
1331
|
|
|
1338
1332
|
# Check the observation has uncertainty to display
|
|
1339
1333
|
if show_err and (self._spec.err_flux is None):
|
|
@@ -1378,13 +1372,13 @@ class SpectrumFigures(Plotter):
|
|
|
1378
1372
|
idcs_bands = line.index_bands(self._spec.wave, self._spec.redshift, just_band_edges=True)
|
|
1379
1373
|
|
|
1380
1374
|
# Plot the spectrum
|
|
1381
|
-
label_leg = line.latex_label if (line.latex_label is not None and include_fits is False) else None
|
|
1375
|
+
label_leg = line.latex_label[0] if (line.latex_label[0] is not None and include_fits is False) else None
|
|
1382
1376
|
in_ax[0].step(wave_plot[idcs_bands[0]:idcs_bands[5]] / z_corr, flux_plot[idcs_bands[0]:idcs_bands[5]] * z_corr,
|
|
1383
1377
|
where='mid', color=theme.colors['fg'], label=label_leg, linewidth=theme.plt['spectrum_width'])
|
|
1384
1378
|
|
|
1385
1379
|
# Continuum bands
|
|
1386
1380
|
bands_filling_plot(in_ax[0], wave_plot, flux_plot, z_corr, idcs_bands, line, color_dict=theme.colors,
|
|
1387
|
-
show_central=not show_err)
|
|
1381
|
+
show_central=not show_err, exclude_continua=exclude_continua)
|
|
1388
1382
|
|
|
1389
1383
|
if show_err:
|
|
1390
1384
|
err_plot = self._spec.err_flux.data
|
lime/plotting/utils.py
CHANGED
|
@@ -7,21 +7,21 @@ from lime.io import _PARENT_BANDS
|
|
|
7
7
|
_logger = logging.getLogger('LiMe')
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def parse_bands_arguments(label,
|
|
10
|
+
def parse_bands_arguments(label, bands, log, norm_flux):
|
|
11
11
|
|
|
12
12
|
line = None
|
|
13
13
|
if label is None and (log.index.size > 0):
|
|
14
14
|
label = log.index[-1]
|
|
15
15
|
line = Line.from_log(label, log, norm_flux)
|
|
16
16
|
|
|
17
|
+
# The user provided a reference band to check the region use it
|
|
18
|
+
elif label is not None and bands is not None:
|
|
19
|
+
line = Line(label, bands)
|
|
20
|
+
|
|
17
21
|
# Line has been measured before
|
|
18
22
|
elif label is not None and (log.index.size > 0):
|
|
19
23
|
line = Line.from_log(label, log, norm_flux)
|
|
20
24
|
|
|
21
|
-
# The user provided a reference band to check the region use it
|
|
22
|
-
elif label is not None and ref_bands is not None:
|
|
23
|
-
line = Line(label, ref_bands)
|
|
24
|
-
|
|
25
25
|
elif label is not None and label in _PARENT_BANDS.index:
|
|
26
26
|
line = Line(label, band=_PARENT_BANDS.loc[label, 'w1':'w6'].to_numpy())
|
|
27
27
|
|
lime/tools.py
CHANGED
|
@@ -54,7 +54,7 @@ def int_to_roman(num):
|
|
|
54
54
|
return roman_num
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
def pd_get(df, row, column, default=None, transform=None):
|
|
57
|
+
def pd_get(df, row, column, default=None, transform=None, nan_to_none=False):
|
|
58
58
|
|
|
59
59
|
# Fast get from dataframe
|
|
60
60
|
try:
|
|
@@ -66,6 +66,12 @@ def pd_get(df, row, column, default=None, transform=None):
|
|
|
66
66
|
if transform is not None:
|
|
67
67
|
cell = default if cell == transform else cell
|
|
68
68
|
|
|
69
|
+
# Transform nan to None
|
|
70
|
+
if nan_to_none and (cell is not None):
|
|
71
|
+
if isinstance(cell, float):
|
|
72
|
+
# print('JODER', cell)
|
|
73
|
+
cell = None if np.isnan(cell) else cell
|
|
74
|
+
|
|
69
75
|
return cell
|
|
70
76
|
|
|
71
77
|
|
lime/transitions.py
CHANGED
|
@@ -4,7 +4,7 @@ import numpy as np
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
from numpy.core.fromnumeric import argmin
|
|
6
6
|
|
|
7
|
-
from .io import _PARENT_BANDS, _LOG_EXPORT, _LOG_COLUMNS, check_file_dataframe, LiMe_Error
|
|
7
|
+
from .io import _PARENT_BANDS, _LOG_EXPORT, _LOG_COLUMNS, check_file_dataframe, LiMe_Error, load_frame
|
|
8
8
|
from pandas import DataFrame
|
|
9
9
|
from .tools import pd_get, au
|
|
10
10
|
|
|
@@ -185,46 +185,53 @@ def air_to_vacuum_function(wave_array, units_wave='AA'):
|
|
|
185
185
|
return wave_array / (1 + 1e-6 * (287.6155 + 1.62887 * sigma2 + 0.01360 * np.square(sigma2)))
|
|
186
186
|
|
|
187
187
|
|
|
188
|
-
def check_units_from_wave(line, str_ion, str_wave,
|
|
188
|
+
def check_units_from_wave(line, str_ion, str_wave, bands, ref_bands=None):
|
|
189
189
|
|
|
190
|
-
#
|
|
191
|
-
if
|
|
190
|
+
# First the input database
|
|
191
|
+
if bands is not None:
|
|
192
192
|
|
|
193
193
|
# Check literal unit
|
|
194
|
-
wave = pd_get(
|
|
195
|
-
units = pd_get(
|
|
194
|
+
wave = pd_get(bands, line, 'wavelength', nan_to_none=True)
|
|
195
|
+
units = pd_get(bands, line, 'units_wave', nan_to_none=True)
|
|
196
196
|
|
|
197
197
|
# Check core element
|
|
198
198
|
if wave is None:
|
|
199
199
|
core_element = f'{str_ion}_{str_wave}'
|
|
200
|
-
wave = pd_get(
|
|
201
|
-
units = pd_get(
|
|
200
|
+
wave = pd_get(bands, core_element, 'wavelength', nan_to_none=True)
|
|
201
|
+
units = pd_get(bands, core_element, 'units_wave', nan_to_none=True)
|
|
202
202
|
|
|
203
203
|
# Convert to units
|
|
204
204
|
units = au.Unit(units) if units is not None else units
|
|
205
|
+
# units = None if (units is None or np.isnan(units)) else au.Unit(units)
|
|
205
206
|
|
|
206
207
|
else:
|
|
207
208
|
wave, units = None, None
|
|
208
209
|
|
|
209
|
-
#
|
|
210
|
+
# Second the reference database
|
|
211
|
+
if (units is None) or (wave is None):
|
|
212
|
+
ref_bands = ref_bands if ref_bands is not None else _PARENT_BANDS
|
|
213
|
+
wave = pd_get(ref_bands, line, 'wavelength', nan_to_none=True)
|
|
214
|
+
units = pd_get(ref_bands, line, 'units_wave', nan_to_none=True)
|
|
215
|
+
|
|
216
|
+
# Convert to units
|
|
217
|
+
units = au.Unit(units) if units is not None else units
|
|
218
|
+
|
|
219
|
+
# Third decipher from label
|
|
210
220
|
if (units is None) or (wave is None):
|
|
211
221
|
|
|
212
222
|
# First check for Angstroms
|
|
213
223
|
if str_wave[-1] == 'A':
|
|
214
|
-
|
|
215
|
-
|
|
224
|
+
units = au.Unit('AA')
|
|
225
|
+
wave = float(str_wave[:-1])
|
|
216
226
|
|
|
217
227
|
else:
|
|
218
228
|
au_unit = au.Unit(str_wave)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
else:
|
|
223
|
-
units_label, wave_label = None, None
|
|
229
|
+
units = au_unit.bases[0]
|
|
230
|
+
wave = au_unit.scale
|
|
224
231
|
|
|
225
|
-
# Give preferences to the tabel values
|
|
226
|
-
wave = wave_label if wave is None else wave
|
|
227
|
-
units = units_label if units is None else units
|
|
232
|
+
# # Give preferences to the tabel values
|
|
233
|
+
# wave = wave_label if wave is None else wave
|
|
234
|
+
# units = units_label if units is None else units
|
|
228
235
|
|
|
229
236
|
return wave, units
|
|
230
237
|
|
|
@@ -400,7 +407,7 @@ def latex_from_label(label, particle=None, wave=None, units_wave=None, kinem=Non
|
|
|
400
407
|
#
|
|
401
408
|
|
|
402
409
|
|
|
403
|
-
def label_composition(line_list,
|
|
410
|
+
def label_composition(line_list, bands=None, default_profile=None, ref_bands=None):
|
|
404
411
|
|
|
405
412
|
# Empty containers for the label componentes
|
|
406
413
|
n_comps = len(line_list)
|
|
@@ -429,7 +436,7 @@ def label_composition(line_list, ref_df=None, default_profile=None):
|
|
|
429
436
|
particle[i] = Particle.from_label(line_items[0])
|
|
430
437
|
|
|
431
438
|
# Wavelength properties
|
|
432
|
-
wavelength[i], units_wave[i] = check_units_from_wave(line, line_items[0], line_items[1],
|
|
439
|
+
wavelength[i], units_wave[i] = check_units_from_wave(line, line_items[0], line_items[1], bands, ref_bands)
|
|
433
440
|
|
|
434
441
|
# Split the optional components: "H1_1216A_t-rec_k-0_p-g" -> {'t': 'rec', 'k': '0', 'p': 'g'} # TODO better do that with optional_comps
|
|
435
442
|
comp_conf = {optC[0]: optC[2:] for optC in line_items[2:]}
|
|
@@ -452,8 +459,8 @@ def label_composition(line_list, ref_df=None, default_profile=None):
|
|
|
452
459
|
trans = comp_conf.get('t', None)
|
|
453
460
|
|
|
454
461
|
# If none is provided check from the table
|
|
455
|
-
if (trans is None) and (
|
|
456
|
-
trans = pd_get(
|
|
462
|
+
if (trans is None) and (bands is not None):
|
|
463
|
+
trans = pd_get(bands, line, 'transition')
|
|
457
464
|
|
|
458
465
|
# Else assume default
|
|
459
466
|
if trans is None:
|
|
@@ -607,27 +614,48 @@ def format_line_mask_option(entry_value, wave_array):
|
|
|
607
614
|
return formatted_value
|
|
608
615
|
|
|
609
616
|
|
|
610
|
-
def
|
|
617
|
+
def bands_from_measurements(frame, sample_levels=['id', 'line'], sort=True, remove_empty_columns=False, index_dict=None,
|
|
618
|
+
bands_hdrs=('wavelength', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6', 'group_label', 'units_wave', 'particle',
|
|
619
|
+
'transition')):
|
|
611
620
|
|
|
612
|
-
#
|
|
613
|
-
|
|
614
|
-
bands = pd.DataFrame(columns=headers)
|
|
621
|
+
# Load the frame if necessary
|
|
622
|
+
frame = check_file_dataframe(frame, sample_levels=sample_levels)
|
|
615
623
|
|
|
616
624
|
# Single frame
|
|
617
625
|
if not isinstance(frame.index, pd.MultiIndex):
|
|
618
|
-
|
|
626
|
+
|
|
627
|
+
# Make dataframe with single lines
|
|
628
|
+
idcs_single = frame.group_label == 'none'
|
|
629
|
+
bands = frame.reindex(index=frame.loc[idcs_single].index, columns=bands_hdrs)
|
|
630
|
+
|
|
631
|
+
# Get the indexes of grouped lines
|
|
632
|
+
group_labels_arr = frame.loc[~idcs_single].group_label
|
|
633
|
+
unique_values, unique_indices = np.unique(group_labels_arr, return_index=True)
|
|
634
|
+
group_labels_arr = group_labels_arr.iloc[np.sort(unique_indices)]
|
|
635
|
+
|
|
636
|
+
# Make dataframe with grouped lines and add blended suffix
|
|
637
|
+
bands_group = frame.reindex(index=group_labels_arr.index, columns=bands_hdrs)
|
|
638
|
+
blended_arr = bands_group.loc[~bands_group.index.to_series().str.endswith('_m')].index.to_numpy()
|
|
639
|
+
bands_group.rename(index={key: f"{key}_b" for key in blended_arr}, inplace=True)
|
|
640
|
+
|
|
641
|
+
# Combine the dataframes
|
|
642
|
+
bands = pd.concat([bands, bands_group], axis=0)
|
|
619
643
|
|
|
620
644
|
# Multi-index
|
|
621
645
|
else:
|
|
622
646
|
|
|
623
|
-
line_list = frame.index.get_level_values(
|
|
647
|
+
line_list = frame.index.get_level_values(sample_levels).unique()
|
|
648
|
+
|
|
649
|
+
idcs_single = frame.group_label == 'none'
|
|
650
|
+
bands = frame.reindex(index=frame.loc[idcs_single].index, columns=bands_hdrs)
|
|
651
|
+
|
|
624
652
|
|
|
625
653
|
# Loop through the lines
|
|
626
654
|
for i, line_label in enumerate(line_list):
|
|
627
655
|
|
|
628
656
|
# Exclude kinematic components:
|
|
629
657
|
if '_k-' not in line_label:
|
|
630
|
-
df_line = frame.xs(line_label, level=
|
|
658
|
+
df_line = frame.xs(line_label, level=sample_levels, drop_level=False)
|
|
631
659
|
group_list = df_line.group_label.unique()
|
|
632
660
|
|
|
633
661
|
for j, group in enumerate(group_list):
|
|
@@ -673,12 +701,16 @@ def bands_from_frame(frame, lines_level='line', sort=True):
|
|
|
673
701
|
bands.loc[line.label, 'latex_label'] = line.latex_label
|
|
674
702
|
bands.loc[line.label, 'units_wave'] = line.units_wave[line._ref_idx]
|
|
675
703
|
bands.loc[line.label, 'particle'] = line.particle[line._ref_idx]
|
|
676
|
-
# bands.loc[line.label, 'transition'] = line.transition
|
|
677
|
-
|
|
678
704
|
|
|
679
705
|
if sort:
|
|
680
706
|
bands.sort_values(by=['wavelength', 'group_label'], inplace=True)
|
|
681
707
|
|
|
708
|
+
if remove_empty_columns:
|
|
709
|
+
bands = bands.dropna(axis=1, how='all')
|
|
710
|
+
|
|
711
|
+
if index_dict is not None:
|
|
712
|
+
bands.rename(index=index_dict, inplace=True)
|
|
713
|
+
|
|
682
714
|
return bands
|
|
683
715
|
|
|
684
716
|
|
|
@@ -734,7 +766,7 @@ class Particle:
|
|
|
734
766
|
class Line:
|
|
735
767
|
|
|
736
768
|
def __init__(self, label, band=None, fit_conf=None, profile=None, cont_from_bands=True, z_line=None,
|
|
737
|
-
update_latex=False, interpret=True):
|
|
769
|
+
update_latex=False, ref_bands=None, interpret=True):
|
|
738
770
|
|
|
739
771
|
# Label attributes
|
|
740
772
|
self.label = label
|
|
@@ -796,7 +828,7 @@ class Line:
|
|
|
796
828
|
|
|
797
829
|
# Interpret the line from the user reference
|
|
798
830
|
if interpret:
|
|
799
|
-
self._from_label(label, band, fit_conf, update_latex)
|
|
831
|
+
self._from_label(label, band, fit_conf, update_latex, ref_bands)
|
|
800
832
|
|
|
801
833
|
return
|
|
802
834
|
|
|
@@ -808,7 +840,7 @@ class Line:
|
|
|
808
840
|
|
|
809
841
|
return self.label
|
|
810
842
|
|
|
811
|
-
def _from_label(self, label, band=None, fit_conf=None, update_latex=False):
|
|
843
|
+
def _from_label(self, label, band=None, fit_conf=None, update_latex=False, ref_bands=None):
|
|
812
844
|
|
|
813
845
|
# If band is not provided use default database
|
|
814
846
|
if band is None:
|
|
@@ -830,8 +862,8 @@ class Line:
|
|
|
830
862
|
self._modularity_component(modularity_comp, fit_conf, band)
|
|
831
863
|
|
|
832
864
|
# Review the components of the line
|
|
833
|
-
|
|
834
|
-
|
|
865
|
+
items = label_composition(self.list_comps, bands=band if isinstance(band, DataFrame) else None,
|
|
866
|
+
default_profile=self.profile_comp)
|
|
835
867
|
self.particle, self.wavelength, self.units_wave, self.kinem, self.profile_comp, self.transition_comp = items
|
|
836
868
|
|
|
837
869
|
# Quick elements for the line profile
|
lime/workflow.py
CHANGED
|
@@ -13,7 +13,9 @@ from lime.transitions import Line, air_to_vacuum_function, label_decomposition
|
|
|
13
13
|
from lime.io import check_file_dataframe, check_file_array_mask, log_to_HDU, results_to_log, load_frame, LiMe_Error, check_fit_conf, _PARENT_BANDS
|
|
14
14
|
from lime.fitting.redshift import RedshiftFitting
|
|
15
15
|
from lime import __version__
|
|
16
|
-
from
|
|
16
|
+
from scipy.sparse import csr_matrix, csgraph
|
|
17
|
+
|
|
18
|
+
|
|
17
19
|
|
|
18
20
|
try:
|
|
19
21
|
import aspect
|
|
@@ -526,7 +528,7 @@ def get_merged_blended_lines(spec, bands, in_cfg, composite_lines):
|
|
|
526
528
|
#
|
|
527
529
|
# return output_dict
|
|
528
530
|
|
|
529
|
-
def pars_bands_conf(spec, bands, ref_bands, fit_conf, composite_lines):
|
|
531
|
+
def pars_bands_conf(spec, bands, ref_bands, fit_conf, composite_lines, automatic_grouping=False):
|
|
530
532
|
|
|
531
533
|
# Get the the grouped lines
|
|
532
534
|
comps_dict = {} if fit_conf is False else {comp: group_label
|
|
@@ -537,6 +539,31 @@ def pars_bands_conf(spec, bands, ref_bands, fit_conf, composite_lines):
|
|
|
537
539
|
if composite_lines is not None:
|
|
538
540
|
comps_dict = {line: comps for line, comps in comps_dict.items() if line in composite_lines}
|
|
539
541
|
|
|
542
|
+
if automatic_grouping:
|
|
543
|
+
|
|
544
|
+
# Get candidate lines for grouping
|
|
545
|
+
line_arr = [item for v in comps_dict.values() for item in v.split('+')]
|
|
546
|
+
line_arr = bands.loc[bands.index.isin(line_arr)].index
|
|
547
|
+
lambda_arr = bands.loc[line_arr, 'wavelength'].to_numpy()
|
|
548
|
+
|
|
549
|
+
# Generate the line - wavelength matrix with the line pixels width
|
|
550
|
+
wave_matrix = np.zeros((lambda_arr.size, spec.wave_rest.data.size))
|
|
551
|
+
w3_arr = np.searchsorted(spec.wave_rest.data, bands.loc[line_arr, 'w3'].to_numpy())
|
|
552
|
+
w4_arr = np.searchsorted(spec.wave_rest.data, bands.loc[line_arr, 'w4'].to_numpy())
|
|
553
|
+
cols = np.arange(wave_matrix.shape[1])
|
|
554
|
+
wave_matrix[(cols >= w3_arr[:, None]) & (cols <= w4_arr[:, None])] = 1
|
|
555
|
+
pixels_width = wave_matrix.sum(axis=1)
|
|
556
|
+
|
|
557
|
+
# Get the decision matrix with the matching intervals
|
|
558
|
+
decision_matrix = wave_matrix @ wave_matrix.T
|
|
559
|
+
blended_matrix = decision_matrix < np.ceil(pixels_width/3)[:, None]
|
|
560
|
+
math_dict = dict(zip(line_arr, np.arange(line_arr.size)))
|
|
561
|
+
|
|
562
|
+
# Get groups of common entries
|
|
563
|
+
_, auto_labels = csgraph.connected_components(csgraph=csr_matrix(decision_matrix > 1), directed=False)
|
|
564
|
+
for labels, group in zip(line_arr, auto_labels):
|
|
565
|
+
print(f"{labels} {group}")
|
|
566
|
+
|
|
540
567
|
# Loop through the lines on the list and review which changes are necessary
|
|
541
568
|
rename_dict, exclude_list= {}, []
|
|
542
569
|
group_dict, w3_dict, w4_dict = {}, {}, {}
|
|
@@ -811,7 +838,8 @@ class SpecTreatment(LineFitting, RedshiftFitting):
|
|
|
811
838
|
self._n_lines = 0
|
|
812
839
|
|
|
813
840
|
def bands(self, label, bands=None, fit_cfg=None, min_method='least_squares', profile='g-emi', cont_from_bands=True,
|
|
814
|
-
err_from_bands=None, temp=10000.0, default_cfg_prefix='default', obj_cfg_prefix=None, update_default=True
|
|
841
|
+
err_from_bands=None, temp=10000.0, default_cfg_prefix='default', obj_cfg_prefix=None, update_default=True,
|
|
842
|
+
ref_bands=None):
|
|
815
843
|
|
|
816
844
|
"""
|
|
817
845
|
|
|
@@ -883,7 +911,7 @@ class SpecTreatment(LineFitting, RedshiftFitting):
|
|
|
883
911
|
cont_from_bands = True if cont_from_bands is None else cont_from_bands
|
|
884
912
|
|
|
885
913
|
# Interpret the input line
|
|
886
|
-
self.line = Line(label, bands, input_conf, profile, cont_from_bands)
|
|
914
|
+
self.line = Line(label, bands, input_conf, profile, cont_from_bands, ref_bands=ref_bands)
|
|
887
915
|
|
|
888
916
|
# Check the line selection is valid
|
|
889
917
|
idcs_selection = review_bands(self._spec, self.line, user_cont_from_bands=cont_from_bands, user_err_from_bands=err_from_bands)
|
|
@@ -925,7 +953,7 @@ class SpecTreatment(LineFitting, RedshiftFitting):
|
|
|
925
953
|
|
|
926
954
|
def frame(self, bands, fit_cfg=None, min_method='least_squares', profile='g-emi', cont_from_bands=None, err_from_bands=None,
|
|
927
955
|
temp=10000.0, line_list=None, default_cfg_prefix='default', obj_cfg_prefix=None, update_default=True, line_detection=False,
|
|
928
|
-
plot_fit=False, progress_output='bar'):
|
|
956
|
+
plot_fit=False, progress_output='bar', ref_bands=None):
|
|
929
957
|
|
|
930
958
|
"""
|
|
931
959
|
|
|
@@ -1053,8 +1081,7 @@ class SpecTreatment(LineFitting, RedshiftFitting):
|
|
|
1053
1081
|
# Fit the lines
|
|
1054
1082
|
self.bands(line, bands, input_conf, min_method, profile,
|
|
1055
1083
|
cont_from_bands=cont_from_bands, err_from_bands=err_from_bands,
|
|
1056
|
-
temp=temp,
|
|
1057
|
-
obj_cfg_prefix=None, default_cfg_prefix=None)
|
|
1084
|
+
temp=temp, obj_cfg_prefix=None, default_cfg_prefix=None, ref_bands=ref_bands)
|
|
1058
1085
|
|
|
1059
1086
|
if plot_fit:
|
|
1060
1087
|
self._spec.plot.bands()
|
|
@@ -1069,7 +1096,7 @@ class SpecTreatment(LineFitting, RedshiftFitting):
|
|
|
1069
1096
|
return
|
|
1070
1097
|
|
|
1071
1098
|
def continuum(self, degree_list, emis_threshold, abs_threshold=None, smooth_length=None, plot_steps=False,
|
|
1072
|
-
|
|
1099
|
+
**kwargs):
|
|
1073
1100
|
|
|
1074
1101
|
"""
|
|
1075
1102
|
|
|
@@ -1134,7 +1161,7 @@ class SpecTreatment(LineFitting, RedshiftFitting):
|
|
|
1134
1161
|
if plot_steps:
|
|
1135
1162
|
ax_cfg = {'title':f'Continuum fitting, iteration ({i+1}/{len(degree_list)})'}
|
|
1136
1163
|
self._spec.plot._continuum_iteration(input_wave, input_flux, cont_fit, input_flux_s, mask_cont, low_lim,
|
|
1137
|
-
high_lim, emis_threshold[i], ax_cfg,
|
|
1164
|
+
high_lim, emis_threshold[i], ax_cfg, **kwargs)
|
|
1138
1165
|
|
|
1139
1166
|
# Include the standard deviation of the spectrum for the unmasked pixels
|
|
1140
1167
|
self._spec.cont = np.ma.masked_array(cont_fit, self._spec.flux.mask)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lime-stable
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.dev7
|
|
4
4
|
Summary: Line measuring algorithm for astronomical spectra
|
|
5
5
|
Author-email: Vital Fernández <vgf@umich.edu>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -19,7 +19,7 @@ Requires-Dist: aspect-stable~=0.3.1
|
|
|
19
19
|
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
|
|
20
20
|
Provides-Extra: full
|
|
21
21
|
Requires-Dist: asdf~=4.1; extra == "full"
|
|
22
|
-
Requires-Dist: bokeh~=3.
|
|
22
|
+
Requires-Dist: bokeh~=3.7; extra == "full"
|
|
23
23
|
Requires-Dist: mplcursors~=0.6; extra == "full"
|
|
24
24
|
Requires-Dist: openpyxl~=3.1; extra == "full"
|
|
25
25
|
Requires-Dist: PyLaTeX~=1.4; extra == "full"
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
lime/__init__.py,sha256=
|
|
2
|
-
lime/changelog.txt,sha256=
|
|
1
|
+
lime/__init__.py,sha256=i7iYJDoAYCsApb35OG33Y0repT6FpgLCsFMTk0Nu0rM,1279
|
|
2
|
+
lime/changelog.txt,sha256=e02y8w_romtBvZLt7wrDxfL0Kry5ccTUkaajX_XwHbs,9657
|
|
3
3
|
lime/io.py,sha256=9bH51m6DAYXJBLDXVQLmaOB0xw6_XoJiMoZlXDQ7Up4,32708
|
|
4
|
-
lime/lime.toml,sha256=
|
|
4
|
+
lime/lime.toml,sha256=YZ4ISkDoWKzFX_o2XTfOzUGpZQvFYzH0ee1dkLaEcV4,52
|
|
5
5
|
lime/observations.py,sha256=2XV1V7FsQYwi1plcGPHATNeLn-XccUhcKtzjds3D3YU,64552
|
|
6
|
-
lime/tools.py,sha256=
|
|
7
|
-
lime/transitions.py,sha256
|
|
8
|
-
lime/workflow.py,sha256=
|
|
6
|
+
lime/tools.py,sha256=5E7_YeXeLrzFo-2h5EAI8J-cPaK4i6g6OJ1uL2jXJ7o,34136
|
|
7
|
+
lime/transitions.py,sha256=-TgVCqL9R0-i4o8jHQGkGllHrMyP-Bo9DqoWfVNxHe4,45634
|
|
8
|
+
lime/workflow.py,sha256=x3es6Sgq9wI785WdTsIuOWnWvKdSYdnJHN_MSkIhcxM,65417
|
|
9
9
|
lime/archives/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
lime/archives/read_fits.py,sha256=
|
|
10
|
+
lime/archives/read_fits.py,sha256=TIJtP8iGokjE9Nj1ALYb7MHE2lUxZGHuz6cx5SZBb5U,37944
|
|
11
11
|
lime/archives/tables.py,sha256=sn3dINNorARuhEA_NsUKnhz8pT5d9u_sNBNKgv3EtuA,14242
|
|
12
12
|
lime/fitting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
lime/fitting/lines.py,sha256=
|
|
13
|
+
lime/fitting/lines.py,sha256=X1QI9ZqpLfxvfVsOMWMDmwLxXhR_gNvIYBdFA7A2GD8,41288
|
|
14
14
|
lime/fitting/redshift.py,sha256=JBYQlESr63PrF3gRYVT-QLQr2yMi9YZKaeNxI8gFvgY,11639
|
|
15
15
|
lime/inference/detection.py,sha256=m_Yz-53RGEvQQa6rgS_-nU2DwiRG45yuZIuneAvs0Dc,425
|
|
16
|
-
lime/inference/intensity_threshold.py,sha256=
|
|
16
|
+
lime/inference/intensity_threshold.py,sha256=9LqSjJQajfrM4SOHSqJWnverKFnrdrjnV472w56T79U,5047
|
|
17
17
|
lime/plotting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
lime/plotting/bokeh_plots.py,sha256=
|
|
19
|
-
lime/plotting/format.py,sha256=
|
|
20
|
-
lime/plotting/plots.py,sha256=
|
|
18
|
+
lime/plotting/bokeh_plots.py,sha256=xJlFS90NW-kQLSBDrfYd3tkjixyHK0DUs1vm5ExmjOQ,23444
|
|
19
|
+
lime/plotting/format.py,sha256=KM2UjhfJ7j2iEOIRTn8JfpLNmSRSlUFTHqB5EKxTq80,7296
|
|
20
|
+
lime/plotting/plots.py,sha256=LruFJvee0NcZ8wkMeb_-bMqonwLsgB6cF92aPqZRAOk,87496
|
|
21
21
|
lime/plotting/plots_interactive.py,sha256=wX_ckpv_lM6YgZZFEfx36dfGt1DpTB5e-R3DyDkWgMY,73357
|
|
22
22
|
lime/plotting/theme_lime.toml,sha256=moyNfRDx0x0Gma3azW_DNfLQoKd1qnBRiX0DAkioBbs,3396
|
|
23
|
-
lime/plotting/utils.py,sha256=
|
|
23
|
+
lime/plotting/utils.py,sha256=tPBXCvMlg7_onLWN9O2I7FoAurzkfF0Qe-JVYSLb1Xs,2069
|
|
24
24
|
lime/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
lime/resources/lines_database_formatting.py,sha256=vhdanmwQ9ih0eERRAOCUeIQoc-VmTdHv7GseWxDavoA,4357
|
|
26
26
|
lime/resources/lines_database_v2.0.0.txt,sha256=grhl32eDMtHHnXOw-LVmSCPbYjqUgccJ8ba3d3vDnHI,30855
|
|
@@ -28,8 +28,8 @@ lime/resources/logo.py,sha256=buuJuwCjw0JFFtqvvIBpjb-LCuFgBwRFqnVOqCmSam8,3296
|
|
|
28
28
|
lime/resources/types_params.txt,sha256=HgK-rr_cHnjNR3oLJomdYt6UvKwBn-FMWt40MkCR0wM,10111
|
|
29
29
|
lime/retrieve/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
30
|
lime/retrieve/peaks.py,sha256=zERD2-GO-AtNrx8xwGqNbml0pB0nTCH4Oro-9fr69GU,8713
|
|
31
|
-
lime_stable-2.0.
|
|
32
|
-
lime_stable-2.0.
|
|
33
|
-
lime_stable-2.0.
|
|
34
|
-
lime_stable-2.0.
|
|
35
|
-
lime_stable-2.0.
|
|
31
|
+
lime_stable-2.0.dev7.dist-info/licenses/LICENSE.rst,sha256=B_kw459IXvKujLR27bhVUwXmTq9AM72DdCA1xGgBMKI,35608
|
|
32
|
+
lime_stable-2.0.dev7.dist-info/METADATA,sha256=pvUxdCfhIskIQOvSpTUiTH4EQdn8ejTi_WiPfedg4ag,3931
|
|
33
|
+
lime_stable-2.0.dev7.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
|
34
|
+
lime_stable-2.0.dev7.dist-info/top_level.txt,sha256=F6pWR5Cgjf9EkXNBZlUSKFKcPG8vPzM08QwYFfwpsZc,5
|
|
35
|
+
lime_stable-2.0.dev7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|