lime-stable 2.0.dev4__tar.gz → 2.0.dev7__tar.gz

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.
Files changed (51) hide show
  1. {lime_stable-2.0.dev4/src/lime_stable.egg-info → lime_stable-2.0.dev7}/PKG-INFO +2 -2
  2. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/pyproject.toml +2 -2
  3. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/__init__.py +1 -1
  4. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/archives/read_fits.py +1 -1
  5. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/changelog.txt +2 -1
  6. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/fitting/lines.py +5 -5
  7. lime_stable-2.0.dev7/src/lime/inference/intensity_threshold.py +137 -0
  8. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/lime.toml +1 -1
  9. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/bokeh_plots.py +23 -15
  10. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/format.py +5 -1
  11. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/plots.py +85 -91
  12. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/utils.py +5 -5
  13. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/tools.py +7 -1
  14. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/transitions.py +69 -37
  15. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/workflow.py +36 -9
  16. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7/src/lime_stable.egg-info}/PKG-INFO +2 -2
  17. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/requires.txt +1 -1
  18. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_line.py +19 -0
  19. lime_stable-2.0.dev4/src/lime/inference/intensity_threshold.py +0 -205
  20. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/LICENSE.rst +0 -0
  21. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/MANIFEST.in +0 -0
  22. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/README.rst +0 -0
  23. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/setup.cfg +0 -0
  24. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/archives/__init__.py +0 -0
  25. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/archives/tables.py +0 -0
  26. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/fitting/__init__.py +0 -0
  27. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/fitting/redshift.py +0 -0
  28. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/inference/detection.py +0 -0
  29. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/io.py +0 -0
  30. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/observations.py +0 -0
  31. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/__init__.py +0 -0
  32. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/plots_interactive.py +0 -0
  33. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/theme_lime.toml +0 -0
  34. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/__init__.py +0 -0
  35. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/lines_database_formatting.py +0 -0
  36. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/lines_database_v2.0.0.txt +0 -0
  37. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/logo.py +0 -0
  38. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/types_params.txt +0 -0
  39. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/retrieve/__init__.py +0 -0
  40. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/retrieve/peaks.py +0 -0
  41. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/SOURCES.txt +0 -0
  42. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/dependency_links.txt +0 -0
  43. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/top_level.txt +0 -0
  44. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_astro.py +0 -0
  45. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_cube.py +0 -0
  46. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_io.py +0 -0
  47. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_model.py +0 -0
  48. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_read_fits.py +0 -0
  49. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_sample.py +0 -0
  50. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_spectrum.py +0 -0
  51. {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lime-stable
3
- Version: 2.0.dev4
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.6; extra == "full"
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"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "lime-stable"
7
- version = "2.0.dev4"
7
+ version = "2.0.dev7"
8
8
  readme = "README.rst"
9
9
  requires-python = ">=3.11"
10
10
  license = "GPL-3.0-or-later"
@@ -25,7 +25,7 @@ classifiers = ["Programming Language :: Python :: 3",
25
25
 
26
26
  [project.optional-dependencies]
27
27
  full = ["asdf~=4.1",
28
- "bokeh~=3.6",
28
+ "bokeh~=3.7",
29
29
  "mplcursors~=0.6",
30
30
  "openpyxl~=3.1",
31
31
  "PyLaTeX~=1.4",
@@ -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, bands_from_frame
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
@@ -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
- raise LiMe_Error(f'Please introduce fits file instrument or a load function to import the fits file as a '
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
@@ -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
@@ -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
- if self.output.errorbars:
620
- _logger.warning(f'Negative value for profile sigma at {line.label}')
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)
@@ -0,0 +1,137 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ from scipy import signal
7
+ from lime.io import check_file_dataframe
8
+ from lime.transitions import label_decomposition
9
+ from lime.plotting.plots import plot_peaks_troughs
10
+
11
+ try:
12
+ import joblib
13
+ joblib_check = True
14
+ except ImportError:
15
+ joblib_check = False
16
+
17
+
18
+ _logger = logging.getLogger('LiMe')
19
+
20
+ MACHINE_PATH = None
21
+ FLUX_PIXEL_CONV = np.linspace(0,1,33)
22
+
23
+
24
+ def compute_line_width(idx_peak, spec_flux, delta_i, min_delta=2, emission_check=True):
25
+
26
+ """
27
+ Algororithm to measure emision line width given its peak location
28
+ :param idx_peak:
29
+ :param spec_flux:
30
+ :param delta_i:
31
+ :param min_delta:
32
+ :return:
33
+ """
34
+
35
+ i = idx_peak
36
+
37
+ if emission_check:
38
+ while (spec_flux[i] > spec_flux[i + delta_i]) or (np.abs(idx_peak - (i + delta_i)) <= min_delta):
39
+ i += delta_i
40
+ else:
41
+ while (spec_flux[i] < spec_flux[i + delta_i]) or (np.abs(idx_peak - (i + delta_i)) <= min_delta):
42
+ i += delta_i
43
+
44
+ return i
45
+
46
+
47
+
48
+
49
+ class LineFinder:
50
+
51
+ def __init__(self):
52
+
53
+ return
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, **kwargs):
57
+
58
+ """
59
+
60
+ This function compares the input lines bands in the observation spectrum to confirm the presence of lines.
61
+
62
+ The input bands can be specified as a pandas dataframe or the path to its file via the ``bands_df`` argument.
63
+
64
+ The continuum needs to be fit a priori with the Spectrum.fit.continuum function or assigning a ``continuum_array``
65
+ and a ``continuum_std``.
66
+
67
+ The ``sigma_threshold`` establishes the standard deviation factor beyond which a positive line detection is assumed.
68
+
69
+ By default the algorithm seeks for emission lines, set ``emission_type`` equal to False for absorption lines.
70
+
71
+ The additional arguments provide additional utils to adjust the line detection and show the steps/results.
72
+
73
+ :param bands: Input bands dataframe or the address to its file.
74
+ :type bands: pandas.Dataframe, str, pathlib.Path
75
+
76
+ :param sigma_threshold: Continuum standard deviation factor for line detection. The default value is 3.
77
+ :type sigma_threshold: float, optional
78
+
79
+ :param emission_type: Line type. The default value is "True" for emission lines.
80
+ :type emission_type: str, optional
81
+
82
+ :param width_tol: Minimum number of pixels between peaks/troughs. The default value is 5.
83
+ :type width_tol: float, optional
84
+
85
+ :param ml_detection: Machine learning algorithm to detect lines. The default value is None.
86
+ :type ml_detection: str, optional
87
+
88
+ :param plot_steps: Plot the detected peaks/troughs. The default value is False
89
+ :type plot_steps: bool, optional
90
+
91
+ """
92
+
93
+ # TODO Lime2.0 replace by warning with new retrieve line bands
94
+ # Check for the peaks of the emission lines
95
+ continuum_array = self._spec.cont if continuum_array is None else continuum_array
96
+ continuum_std = self._spec.cont_std if continuum_std is None else continuum_std
97
+
98
+ # Get indeces of peaks
99
+ limit_threshold = sigma_threshold * continuum_std
100
+ limit_threshold = continuum_array + limit_threshold if emission_type else continuum_array + limit_threshold
101
+ idcs_peaks, _ = signal.find_peaks(self._spec.flux, height=limit_threshold, distance=width_tol)
102
+
103
+ # Match peaks with theoretical lines
104
+ bands = check_file_dataframe(bands)
105
+ matched_DF = self.label_peaks(idcs_peaks, bands, width_tol=width_tol, line_type=emission_type)
106
+
107
+ # Plot the results
108
+ if plot_steps:
109
+ plot_peaks_troughs(self._spec, idcs_peaks, limit_threshold, continuum_array, matched_DF, **kwargs)
110
+
111
+ return matched_DF
112
+
113
+
114
+ def label_peaks(self, idcs_peaks, matched_DF, line_type='emission', width_tol=5):
115
+
116
+ # Security check in case no lines detected
117
+ if len(idcs_peaks) == 0:
118
+ return pd.DataFrame(columns=matched_DF.columns)
119
+
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]
123
+
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)
127
+
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]]
132
+
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
136
+
137
+ return matched_DF.loc[idcs_matched_bands]
@@ -1,3 +1,3 @@
1
1
  [metadata]
2
2
  name = 'lime-stable'
3
- version = "2.0.dev4"
3
+ version = "2.0.dev7"
@@ -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, ref_bands=None, include_fits=True, rest_frame=False, log_scale=True, fig_cfg=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, ref_bands, norm_flux)
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
@@ -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=None):
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(),
@@ -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
- y_scale = {'value': 'symlog', 'linthresh': min(np.ceil(np.abs(y_min)), np.min(y[y>0]))}
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 len(x[idcs_mask[2]:idcs_mask[3]]) > 1:
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, log_scale=False):
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.latex_label[0]
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
- detec_obj = getattr(self._spec.infer, detection_band)
1016
-
1017
- if detec_obj.confidence is not None:
1018
-
1019
- # Boundaries array for confidence intervals
1020
- bounds = np.array([0.0, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])
1021
-
1022
- # Adjust color map to match lower detection limit to fg color
1023
- cmap = plt.get_cmap(theme.colors['mask_map'])
1024
- cmaplist = [cmap(i) for i in range(cmap.N)]
1025
- cmaplist[0] = theme.colors['fg']
1026
- cmap = colors.LinearSegmentedColormap.from_list('mcm', cmaplist, bounds.size-1)
1027
- norm = colors.BoundaryNorm(bounds * 100, cmap.N)
1028
-
1029
- # Iterate through the confidence intervals and plot the step spectrum
1030
- for i in range(1, len(bounds)):
1031
- if i > 1:
1032
- idcs = detec_obj(bounds[i-1]*100, confidence_max=bounds[i]*100)
1033
- wave_nan, flux_nan = np.full(wave_plot.size, np.nan), np.full(flux_plot.size, np.nan)
1034
- wave_nan[idcs], flux_nan[idcs] = wave_plot[idcs] / z_corr, flux_plot[idcs] * z_corr
1035
-
1036
- in_ax.step(wave_nan, flux_nan, label=label, where='mid', color=cmap(i-1))
1037
-
1038
- # Color bar
1039
- sm = cm.ScalarMappable(cmap=cmap, norm=norm)
1040
- sm.set_array([])
1041
- cbar = plt.colorbar(sm, ax=in_ax)
1042
- cbar.set_label('Detection confidence %', rotation=270, labelpad=35)
1043
-
1044
-
1045
- else:
1046
- _logger.warning(f'The line detection bands confidence has not been calculated. They are not included'
1047
- f' on plot.')
1048
-
1049
- if show_categories:
1050
- # Define the bins you want
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
- if self._spec.infer.pred_arr is not None:
1055
- if aspect_check:
1056
- categories = np.sort(np.unique(self._spec.infer.pred_arr))
1057
- legend_scatter = []
1058
-
1059
- for category in categories:
1060
- if category != 0:
1061
-
1062
- # Get category properties
1063
- feature_name = self._spec.infer.model_mgr.medium.number_feature_dict[category]
1064
- feature_color = aspect.cfg['colors'][feature_name]
1065
- idcs_feature = self._spec.infer.pred_arr == category
1066
- legend_scatter.append(mlines.Line2D([], [], marker='o', color='w',
1067
- markerfacecolor=feature_color, markersize=8, label=feature_name))
1068
-
1069
- # Count the pixels for each category
1070
- counts, _ = np.histogram(self._spec.infer.conf_arr[idcs_feature], bins=bins)
1071
- for idx_conf, count_conf in enumerate(counts):
1072
- if count_conf > 0:
1073
-
1074
- # Get indeces matching the detections
1075
- idcs_count = np.where((bins[idx_conf] < self._spec.infer.conf_arr[idcs_feature]) &
1076
- (self._spec.infer.conf_arr[idcs_feature] <= bins[idx_conf + 1]))[0]
1077
- idcs_nonnan = np.where(idcs_feature)[0][idcs_count] # Returns indices where mask is True
1078
-
1079
- # Generate nan arrays with the data to avoid filling non detections
1080
- wave_nan, flux_nan = np.full(wave_plot.size, np.nan), np.full(flux_plot.size, np.nan)
1081
- wave_nan[idcs_nonnan] = wave_plot[idcs_nonnan] / z_corr
1082
- flux_nan[idcs_nonnan] = flux_plot[idcs_nonnan] * z_corr
1083
-
1084
- # Plot with the corresponding colors and linestyle
1085
- in_ax.step(wave_nan, flux_nan, label=feature_name, where='mid', color=feature_color,
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, output_address=None, ref_bands=None, include_fits=True, rest_frame=False, y_scale='auto', fig_cfg=None,
1279
- ax_cfg=None, in_fig=None, maximize=False, show_err=True):
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, log, ref_bands, norm_flux)
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