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.
- {lime_stable-2.0.dev4/src/lime_stable.egg-info → lime_stable-2.0.dev7}/PKG-INFO +2 -2
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/pyproject.toml +2 -2
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/__init__.py +1 -1
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/archives/read_fits.py +1 -1
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/changelog.txt +2 -1
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/fitting/lines.py +5 -5
- lime_stable-2.0.dev7/src/lime/inference/intensity_threshold.py +137 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/lime.toml +1 -1
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/bokeh_plots.py +23 -15
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/format.py +5 -1
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/plots.py +85 -91
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/utils.py +5 -5
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/tools.py +7 -1
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/transitions.py +69 -37
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/workflow.py +36 -9
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7/src/lime_stable.egg-info}/PKG-INFO +2 -2
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/requires.txt +1 -1
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_line.py +19 -0
- lime_stable-2.0.dev4/src/lime/inference/intensity_threshold.py +0 -205
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/LICENSE.rst +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/MANIFEST.in +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/README.rst +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/setup.cfg +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/archives/__init__.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/archives/tables.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/fitting/__init__.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/fitting/redshift.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/inference/detection.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/io.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/observations.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/__init__.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/plots_interactive.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/plotting/theme_lime.toml +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/__init__.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/lines_database_formatting.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/lines_database_v2.0.0.txt +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/logo.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/resources/types_params.txt +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/retrieve/__init__.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime/retrieve/peaks.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/SOURCES.txt +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/dependency_links.txt +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/src/lime_stable.egg-info/top_level.txt +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_astro.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_cube.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_io.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_model.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_read_fits.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_sample.py +0 -0
- {lime_stable-2.0.dev4 → lime_stable-2.0.dev7}/tests/test_spectrum.py +0 -0
- {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.
|
|
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"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lime-stable"
|
|
7
|
-
version = "2.0.
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
@@ -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]
|
|
@@ -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
|
|
@@ -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(),
|
|
@@ -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
|