sparclclient 1.2.8b4__py2.py3-none-any.whl → 1.3.0b1__py2.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.
sparcl/Results.py CHANGED
@@ -9,6 +9,7 @@ from sparcl.utils import _AttrDict
9
9
 
10
10
  # from sparcl.gather_2d import bin_spectra_records
11
11
  import sparcl.exceptions as ex
12
+ import sparcl.specutils as su
12
13
  from warnings import warn
13
14
  import re
14
15
 
@@ -139,9 +140,7 @@ class Results(UserList):
139
140
 
140
141
  Returns:
141
142
  reordered (:class:`~sparcl.Results.Retrieved`): Contains header and
142
- reordered records.
143
- # none_idx (:obj:`list`): List of indices where record is None.
144
-
143
+ reordered records.
145
144
  """
146
145
  if len(ids_og) <= 0:
147
146
  msg = (
@@ -198,6 +197,14 @@ class Results(UserList):
198
197
  warn(msg, stacklevel=2)
199
198
  return Results(reordered, client=self.client)
200
199
 
200
+ def to_specutils(self):
201
+ """Convert results to a `specutils` object.
202
+
203
+ Returns:
204
+ to_specutils (:class:`~specutils.Spectrum`): a `specutils` object.
205
+ """
206
+ return su.to_specutils(self)
207
+
201
208
 
202
209
  # For results of retrieve()
203
210
  class Retrieved(Results):
@@ -209,7 +216,6 @@ class Retrieved(Results):
209
216
  def __repr__(self):
210
217
  return f"Retrieved Results: {len(self.recs)} records"
211
218
 
212
-
213
219
  #! def bin_spectra(self):
214
220
  #! """Align flux from all records by common wavelength bin.
215
221
  #!
sparcl/__init__.py CHANGED
@@ -37,4 +37,5 @@ __all__ = ["client", "align_records"]
37
37
  #__version__ = "1.2.5"
38
38
  #__version__ = "1.2.6"
39
39
  #__version__ = "1.2.7"
40
- __version__ = "1.2.8b4"
40
+ #__version__ = "1.2.8"
41
+ __version__ = "1.3.0b1"
sparcl/client.py CHANGED
@@ -267,11 +267,12 @@ class SparclClient: # was SparclApi()
267
267
 
268
268
  def token_expired(self, renew=False):
269
269
  """
270
- POST http://localhost:8050/sparc/renew_token/
271
- Content-Type: application/json
272
- {
273
- "refresh_token": "..."
274
- }
270
+ ::
271
+ POST http://localhost:8050/sparc/renew_token/
272
+ Content-Type: application/json
273
+ {
274
+ "refresh_token": "..."
275
+ }
275
276
 
276
277
  Returns an 'access' token
277
278
  """
@@ -308,9 +309,8 @@ class SparclClient: # was SparclApi()
308
309
  None.
309
310
 
310
311
  Example:
311
- >>>
312
- >> client = SparclClient(announcement=False)
313
- >> client.login('test_user@noirlab.edu', 'testpw')
312
+ >>> client = SparclClient(announcement=False)
313
+ >>> client.login('test_user@noirlab.edu', 'pw') # doctest: +SKIP
314
314
  Logged in successfully with email='test_user@noirlab.edu'
315
315
  """
316
316
 
@@ -1021,11 +1021,9 @@ class SparclClient: # was SparclApi()
1021
1021
 
1022
1022
  Example:
1023
1023
  >>> client = SparclClient(announcement=False)
1024
- >>> sids = [5840097619402313728, -8985592895187431424]
1024
+ >>> sids = [4753625089450465280, 1254253099313293312]
1025
1025
  >>> inc = ['specid', 'flux', 'wavelength', 'model']
1026
1026
  >>> ret = client.retrieve_by_specid(specid_list=sids, include=inc)
1027
- >>> len(ret.records[0].wavelength)
1028
- 4617
1029
1027
 
1030
1028
  """
1031
1029
  #!specid_list = list(specid_list)
sparcl/specutils.py ADDED
@@ -0,0 +1,311 @@
1
+ """Functions for converting SPARCL results to specutils objects.
2
+ """
3
+ import warnings
4
+ import numpy as np
5
+ try:
6
+ # specutils >= 2.0
7
+ from specutils import Spectrum
8
+ except ImportError:
9
+ from specutils import Spectrum1D as Spectrum
10
+ from specutils import SpectrumCollection, SpectrumList
11
+ from astropy.nddata import InverseVariance
12
+ import astropy.units as u
13
+
14
+ def _validate_records(records, r0, collection):
15
+ """Validate that records can be converted to Spectrum.
16
+
17
+ Parameters
18
+ ----------
19
+ records : list of dict
20
+ All records to validate.
21
+ r0 : dict
22
+ First record, used as reference for validation.
23
+ collection: bool
24
+ If ``True``, attempt to convert to a
25
+ :class:`~specutils.SpectrumCollection` instead.
26
+
27
+ Raises
28
+ ------
29
+ ValueError
30
+ If records lack 'wavelength' attribute.
31
+ If records have different data releases.
32
+ If wavelength array lengths differ (suggests using SpectrumList).
33
+ If wavelength pixel values differ and collection=False
34
+ (suggests using SpectrumCollection).
35
+
36
+ Warnings
37
+ --------
38
+ UserWarning
39
+ If records come from different data releases.
40
+ """
41
+
42
+ # Check if the first record has wavelength data
43
+ if 'wavelength' not in r0:
44
+ raise ValueError("Results do not have a wavelength attribute. "
45
+ "Conversion is not possible.")
46
+
47
+ # Check if all records come from the same data release
48
+ if not all([r.data_release == r0.data_release for r in records]):
49
+ warnings.warn("Results are not all from the same data release, "
50
+ "conversion may not be possible.", UserWarning)
51
+
52
+ # Check if all records have the same number of wavelength points
53
+ if not all([len(r.wavelength) == len(r0.wavelength) for r in records]):
54
+ raise ValueError("Results do not have the same wavelength solution. "
55
+ "Consider using .to_SpectrumList instead.")
56
+
57
+ # If not creating a SpectrumCollection, check that wavelength values
58
+ # are identical across all records
59
+ if not collection and not all([(r.wavelength == r0.wavelength).all()
60
+ for r in records]):
61
+ raise ValueError("Results do not have the same wavelength pixels. "
62
+ "Consider using SpectrumCollection instead.")
63
+
64
+ def _extract_record_data(records, flux, uncertainty, mask, model, redshift,
65
+ meta, spectral_axis, has_model, has_redshift,
66
+ collection, single_record):
67
+ """Extract all data from records into arrays. This function modifies
68
+ arrays in-place rather than returning values.
69
+
70
+ Parameters
71
+ ----------
72
+ records: list of dict
73
+ Records containing flux, ivar, mask, wavelength, and optional
74
+ model/redshift.
75
+ flux : np.ndarray
76
+ Pre-allocated array for flux values. Shape: (n_pixels,) if
77
+ single_record, else (n_records, n_pixels).
78
+ uncertainty : np.ndarray
79
+ Pre-allocated array for inverse variance.
80
+ mask : np.ndarray
81
+ Pre-allocated array for data quality masks.
82
+ model : np.ndarray or None
83
+ Pre-allocated array for model values if has_model=True.
84
+ redshift : list
85
+ Empty list to populate with redshift values.
86
+ meta : dict
87
+ Empty dict to populate with metadata.
88
+ spectral_axis : np.ndarray
89
+ For collections, pre-allocated 2D array to store wavelength grids.
90
+ For non-collections, this is just r0.wavelength (not modified).
91
+ has_model : bool
92
+ Whether records contain 'model' attribute.
93
+ has_redshift : bool
94
+ Whether records contain 'redshift' attribute.
95
+ collection : bool
96
+ If True, stores wavelength arrays for each record in spectral_axis.
97
+ single_record : bool
98
+ If True, treats arrays as 1D. If False, treats as 2D with row per
99
+ record.
100
+
101
+ Returns
102
+ -------
103
+ None
104
+ All outputs are written to the input arrays/containers in-place.
105
+ """
106
+ for k, record in enumerate(records):
107
+ if single_record:
108
+ # For single record, assign directly (1D)
109
+ flux[:] = record.flux
110
+ uncertainty[:] = record.ivar
111
+ mask[:] = record.mask
112
+ if has_model:
113
+ model[:] = record.model
114
+ else:
115
+ # For multiple records, assign to row (2D)
116
+ flux[k, :] = record.flux
117
+ uncertainty[k, :] = record.ivar
118
+ mask[k, :] = record.mask
119
+ if has_model:
120
+ model[k, :] = record.model
121
+
122
+ # Store redshift values if available
123
+ if has_redshift:
124
+ redshift.append(record.redshift)
125
+
126
+ # For collections, each record can have its own wavelength grid
127
+ if collection:
128
+ spectral_axis[k, :] = record.wavelength
129
+
130
+ # Extract all additional metadata attributes
131
+ for attribute, value in record.items():
132
+ if attribute not in ('flux', 'ivar', 'mask', 'model', 'redshift',
133
+ 'wavelength'):
134
+ if single_record:
135
+ # For single record, store metadata values as scalars
136
+ meta[attribute] = value
137
+ else:
138
+ # For multiple records, accumulate metadata values into
139
+ # lists (creates list on first encounter, then appends)
140
+ meta.setdefault(attribute, []).append(value)
141
+
142
+ def to_Spectrum(results, *, collection=False):
143
+ """Convert `results` to :class:`specutils.Spectrum`.
144
+
145
+ Parameters
146
+ ----------
147
+ results : :class:`sparcl.Results.Retrieved`
148
+ Retrieved results, or a single record from a set of results.
149
+ collection : bool, optional
150
+ If ``True``, attempt to convert to a
151
+ :class:`~specutils.SpectrumCollection` instead.
152
+
153
+ Returns
154
+ -------
155
+ :class:`~specutils.Spectrum` or :class:`~specutils.SpectrumCollection`
156
+ The requested object.
157
+
158
+ Raises
159
+ ------
160
+ ValueError
161
+ If `results` can't be converted to a :class:`~specutils.Spectrum`
162
+ object in a valid way. For example, if some of the spectra have a
163
+ different wavelength solution.
164
+ """
165
+ # Prepare records
166
+ if isinstance(results, dict):
167
+ records = [results]
168
+ r0 = results
169
+ else:
170
+ try:
171
+ records = results.records
172
+ if len(records) == 0:
173
+ raise ValueError("No records found in results. Cannot "
174
+ "convert empty results to Spectrum.")
175
+ r0 = results.records[0]
176
+ except (IndexError, AttributeError) as e:
177
+ raise ValueError("No records found in results. Cannot "
178
+ "convert empty results to Spectrum.") from e
179
+
180
+ # Validate
181
+ _validate_records(records, r0, collection)
182
+
183
+ # Determine which optional data components exist in records
184
+ has_redshift = 'redshift' in r0
185
+ has_model = 'model' in r0
186
+ n_pixels = r0.flux.shape[0]
187
+ single_record = len(records) == 1
188
+
189
+ # Set flux shape based on number of records
190
+ if single_record:
191
+ flux_shape = (n_pixels,)
192
+ else:
193
+ flux_shape = (len(records), n_pixels)
194
+
195
+ # Build spectral axis
196
+ if collection:
197
+ spectral_axis = np.empty((len(records), r0.wavelength.shape[0]),
198
+ dtype=r0.wavelength.dtype)
199
+ else:
200
+ spectral_axis = r0.wavelength
201
+
202
+ # Initialize arrays
203
+ flux = np.empty(flux_shape, dtype=r0.flux.dtype)
204
+ uncertainty = np.empty(flux_shape, dtype=r0.ivar.dtype)
205
+ mask = np.empty(flux_shape, dtype=r0.mask.dtype)
206
+ model = np.empty(flux_shape, dtype=r0.model.dtype) if has_model else None
207
+ redshift = []
208
+ meta = {}
209
+
210
+ # Populate arrays by iterating through records
211
+ _extract_record_data(records, flux, uncertainty, mask, model, redshift,
212
+ meta, spectral_axis, has_model, has_redshift,
213
+ collection, single_record)
214
+
215
+ # Convert redshift list to numpy array if exists
216
+ if has_redshift:
217
+ redshift = np.array(redshift)
218
+ if single_record and len(redshift) == 1:
219
+ # Convert to scalar if single record
220
+ redshift = redshift[0]
221
+ else:
222
+ redshift = None
223
+
224
+ # Add model to metadata if exists
225
+ if has_model:
226
+ meta['model'] = model
227
+
228
+ # Prepare arguments common to both Spectrum and SpectrumCollection
229
+ common_args = {
230
+ 'flux': flux * 10**-17 * u.Unit('erg cm-2 s-1 AA-1'),
231
+ 'spectral_axis': spectral_axis * u.AA,
232
+ 'uncertainty': InverseVariance(uncertainty),
233
+ 'mask': mask,
234
+ 'meta': meta}
235
+
236
+ if collection:
237
+ return SpectrumCollection(**common_args)
238
+
239
+ return Spectrum(**common_args, redshift=redshift)
240
+
241
+ def to_SpectrumList(results):
242
+ """Convert `results` to :class:`specutils.SpectrumList`.
243
+
244
+ Parameters
245
+ ----------
246
+ results : :class:`sparcl.Results.Retrieved`
247
+ Retrieved results.
248
+
249
+ Returns
250
+ -------
251
+ :class:`~specutils.SpectrumList`
252
+ The requested object.
253
+ """
254
+ s = SpectrumList()
255
+ if isinstance(results, dict):
256
+ records = [results]
257
+ else:
258
+ records = results.records
259
+ for r in records:
260
+ if 'redshift' in r:
261
+ redshift = r.redshift
262
+ else:
263
+ redshift = None
264
+ meta = dict()
265
+ for attribute in r:
266
+ if attribute not in ('flux', 'wavelength', 'ivar',
267
+ 'redshift', 'mask'):
268
+ meta[attribute] = r[attribute]
269
+ s1 = Spectrum(flux=r.flux*10**-17*u.Unit('erg cm-2 s-1 AA-1'),
270
+ spectral_axis=r.wavelength*u.AA,
271
+ uncertainty=InverseVariance(r.ivar),
272
+ redshift=redshift,
273
+ mask=r.mask,
274
+ meta=meta)
275
+ s.append(s1)
276
+ return s
277
+
278
+ def to_specutils(results):
279
+ """Convert `results` to a specutils object.
280
+
281
+ Parameters
282
+ ----------
283
+ results : :class:`sparcl.Results.Retrieved`
284
+ Retrieved results.
285
+
286
+ Returns
287
+ -------
288
+ :class:`~specutils.Spectrum` or :class:`~specutils.SpectrumCollection`
289
+ or :class:`~specutils.SpectrumList`
290
+ The most natural conversion to a specutils object.
291
+
292
+ Raises
293
+ ------
294
+ ValueError
295
+ If no valid conversion can be performed.
296
+ """
297
+ try:
298
+ # Try standard Spectrum conversion first
299
+ s = to_Spectrum(results)
300
+ except ValueError as ve:
301
+ # Check the error message to determine appropriate alternative
302
+ if 'SpectrumList' in str(ve):
303
+ # Different wavelength array lengths use SpectrumList
304
+ s = to_SpectrumList(results)
305
+ elif 'SpectrumCollection' in str(ve):
306
+ # Same wavelength length but diff pixels use SpectrumCollection
307
+ s = to_Spectrum(results, collection=True)
308
+ else:
309
+ raise ValueError("Could not find a valid conversion to "
310
+ "specutils objects!")
311
+ return s
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sparclclient
3
- Version: 1.2.8b4
3
+ Version: 1.3.0b1
4
4
  Summary: A client for getting spectra and meta-data from NOIRLab.
5
5
  Author-email: "S. Pothier" <datalab-spectro@noirlab.edu>
6
6
  Description-Content-Type: text/markdown
@@ -1,12 +1,13 @@
1
- sparcl/Results.py,sha256=vnACjm8sp7MrsJ_5LTuKMuyRWqeBJxOxQM302cHmAlk,9159
2
- sparcl/__init__.py,sha256=OAZ72-b6xfmKNbYnM_CY6m8Zc7B9hz_PThiTsQyGla8,1170
3
- sparcl/client.py,sha256=C9vYeHqZvl0lzVw1BRJeGCv5zk2W2XH1aLsFtBYeYk4,40013
1
+ sparcl/Results.py,sha256=IlnYZrDddARv3PJ3HPcyHJwTdMroqGdw-eaSrbmAA64,9290
2
+ sparcl/__init__.py,sha256=5iJtQlT6iymXB7qJZazu4MQjt9jjci3NEArMicoTizY,1193
3
+ sparcl/client.py,sha256=OAY9F1KbyVc5kos7twqOFnQcGtoqraNG6-CJWqmVE3o,39976
4
4
  sparcl/conf.py,sha256=GFNDelaiVIAkjNjvFlG7HAlPpU39nqZmTPohQGmOcgI,928
5
5
  sparcl/exceptions.py,sha256=sznmOMGENHvxutSXlRVWqi87bR2Qiebka7LyR3mAII0,4244
6
6
  sparcl/fields.py,sha256=NZUBqDidpbXfeX5F4b306F323xZY2CRIx8eVv-HWTVU,5127
7
7
  sparcl/gather_2d.py,sha256=YTFVQl38dRZjUu0AEUFVZtQm7zC8mU3LVvUTDGxp6u8,8722
8
8
  sparcl/resample_spectra.py,sha256=nk5HiyaGF-b9SiTmIC9yJBbYX4VYsiLFHyW186bPs2s,1310
9
9
  sparcl/sparc.ini,sha256=q_wjo9DLnCYRxWFMl0CtMYp4DD1AXfEcK6BP6cmncwo,329
10
+ sparcl/specutils.py,sha256=9rp0P2LRsp5UC5ODNwtO2vy0cigCiVtdMtogDb5gDb0,10970
10
11
  sparcl/type_conversion.py,sha256=QmXNX9j_7QHnBu83f2ZBfREoql9wuo98ZbhQtSjRRWc,12965
11
12
  sparcl/unsupported.py,sha256=bfkkZa-PuqwN-Bqo3vCIrLupbWMDTCiTHPMNfXnqmMc,1848
12
13
  sparcl/utils.py,sha256=pDAk9roe7ezfVohnKcLMxFjjXHkp7EQLbgVNe5sSTrk,6259
@@ -15,7 +16,7 @@ sparcl/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
15
16
  sparcl/benchmarks/benchmarks.py,sha256=OmlSdnAPLmcvGXsr-HzGyfAAcnoqlO0JQ4EIA7JGhZc,9424
16
17
  sparcl/benchmarks/sparcl_benchmarking.ipynb,sha256=gwof2hqM9Qb49qzRX-mky7WNqXZCMSB7ry8bX8dImxc,17559
17
18
  sparcl/notebooks/sparcl-examples.ipynb,sha256=gEwMKI1x7A1YsVeCsQn1QoMO0ZuIhMUAu3qedTiQ7hM,169268
18
- sparclclient-1.2.8b4.dist-info/LICENSE,sha256=y10EluGMCzGs9X4oYCYyix3l6u-lawB_vlGR8qe442Q,1576
19
- sparclclient-1.2.8b4.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
20
- sparclclient-1.2.8b4.dist-info/METADATA,sha256=Oyk3k7Xy_q_pOiHy4kNfgf5uK8nJDRyVM1R5txgzaVs,677
21
- sparclclient-1.2.8b4.dist-info/RECORD,,
19
+ sparclclient-1.3.0b1.dist-info/LICENSE,sha256=y10EluGMCzGs9X4oYCYyix3l6u-lawB_vlGR8qe442Q,1576
20
+ sparclclient-1.3.0b1.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
21
+ sparclclient-1.3.0b1.dist-info/METADATA,sha256=vANPyhFvOUZzx-yMf4NHsdLOmg_qvxqTZtr_BLeqapU,677
22
+ sparclclient-1.3.0b1.dist-info/RECORD,,