sacc 1.0__py3-none-any.whl → 2.0__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.
- sacc/__init__.py +4 -1
- sacc/covariance.py +202 -96
- sacc/data_types.py +64 -9
- sacc/io.py +414 -0
- sacc/sacc.py +372 -149
- sacc/tracer_uncertainty/__init__.py +1 -0
- sacc/tracer_uncertainty/base.py +34 -0
- sacc/tracer_uncertainty/nz.py +287 -0
- sacc/tracers/__init__.py +6 -0
- sacc/tracers/base.py +127 -0
- sacc/tracers/clusters.py +332 -0
- sacc/tracers/maps.py +206 -0
- sacc/tracers/misc.py +87 -0
- sacc/tracers/nz.py +285 -0
- sacc/tracers/survey.py +75 -0
- sacc/utils.py +46 -2
- sacc/windows.py +116 -102
- {sacc-1.0.dist-info → sacc-2.0.dist-info}/METADATA +6 -4
- sacc-2.0.dist-info/RECORD +22 -0
- {sacc-1.0.dist-info → sacc-2.0.dist-info}/WHEEL +1 -1
- sacc/tracers.py +0 -1217
- sacc-1.0.dist-info/RECORD +0 -12
- {sacc-1.0.dist-info → sacc-2.0.dist-info}/licenses/LICENSE +0 -0
- {sacc-1.0.dist-info → sacc-2.0.dist-info}/top_level.txt +0 -0
sacc/__init__.py
CHANGED
@@ -3,4 +3,7 @@ from .windows import Window, BandpowerWindow, TopHatWindow, LogTopHatWindow # n
|
|
3
3
|
from .data_types import standard_types, parse_data_type_name, build_data_type_name # noqa
|
4
4
|
from .tracers import BaseTracer # noqa
|
5
5
|
from .covariance import BaseCovariance # noqa
|
6
|
-
|
6
|
+
from .tracer_uncertainty import NZLinearUncertainty, NZShiftUncertainty, NZShiftStretchUncertainty # noqa
|
7
|
+
from .io import BaseIO # noqa
|
8
|
+
from . import io # noqa
|
9
|
+
__version__ = '2.0' #noqa
|
sacc/covariance.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
from astropy.io import fits
|
2
1
|
from astropy.table import Table
|
3
2
|
import scipy.linalg
|
4
3
|
import numpy as np
|
4
|
+
import warnings
|
5
5
|
|
6
6
|
from .utils import invert_spd_matrix
|
7
|
+
from .io import BaseIO, ONE_OBJECT_PER_TABLE, ONE_OBJECT_MULTIPLE_TABLES
|
7
8
|
|
8
|
-
|
9
|
-
class BaseCovariance:
|
9
|
+
class BaseCovariance(BaseIO):
|
10
10
|
"""
|
11
11
|
The abstract base class for covariances in different forms.
|
12
12
|
These are not currently designed to be modified after creation.
|
@@ -26,7 +26,7 @@ class BaseCovariance:
|
|
26
26
|
cov_type: string
|
27
27
|
The type of the covariance (class variable)
|
28
28
|
"""
|
29
|
-
|
29
|
+
_sub_classes = {}
|
30
30
|
|
31
31
|
def __init__(self):
|
32
32
|
"""Abstract superclass constructor.
|
@@ -37,13 +37,34 @@ class BaseCovariance:
|
|
37
37
|
self._dense = None
|
38
38
|
self._dense_inverse = None
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def
|
45
|
-
|
46
|
-
|
40
|
+
# At the moment we only allow one covariance object per table,
|
41
|
+
# so this is only used for consistency when saving objects.
|
42
|
+
self.name = "cov"
|
43
|
+
|
44
|
+
def __eq__(self, other):
|
45
|
+
"""
|
46
|
+
Test for equality
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
other: object
|
51
|
+
The other object to test for equality
|
52
|
+
|
53
|
+
Returns
|
54
|
+
-------
|
55
|
+
equal: bool
|
56
|
+
True if the objects are equal
|
57
|
+
"""
|
58
|
+
if not isinstance(other, self.__class__):
|
59
|
+
return False
|
60
|
+
# We do not test the inverse; we rely on the fact that
|
61
|
+
# if the dense matrices are equal, then the inverses will be equal.
|
62
|
+
# We are also relying on each subclass to have an instance variable
|
63
|
+
# 'size'.
|
64
|
+
return self.name == other.name and \
|
65
|
+
self.size == other.size and \
|
66
|
+
((self._dense is None and other._dense is None) or \
|
67
|
+
np.allclose(self._dense, other._dense))
|
47
68
|
|
48
69
|
@classmethod
|
49
70
|
def from_hdu(cls, hdu):
|
@@ -64,8 +85,11 @@ class BaseCovariance:
|
|
64
85
|
instance: BaseCovariance
|
65
86
|
A covariance instance
|
66
87
|
"""
|
88
|
+
warnings.warn("You are using an older SACC legacy SACC file format with old covariance data."
|
89
|
+
" Consider updating it by loading it and saving it again with the latest SACC version.",
|
90
|
+
DeprecationWarning)
|
67
91
|
subclass_name = hdu.header['saccclss']
|
68
|
-
subclass = cls.
|
92
|
+
subclass = cls._sub_classes[subclass_name]
|
69
93
|
return subclass.from_hdu(hdu)
|
70
94
|
|
71
95
|
@classmethod
|
@@ -80,32 +104,29 @@ class BaseCovariance:
|
|
80
104
|
Parameters
|
81
105
|
----------
|
82
106
|
cov: list[array] or array
|
83
|
-
If a list,
|
84
|
-
|
85
|
-
|
107
|
+
If a list, it should be a list of array-like objects each of which
|
108
|
+
can be coerced into a 2d array. A BlockDiagonalCovariance will be
|
109
|
+
returned.
|
86
110
|
|
87
|
-
|
88
|
-
|
111
|
+
If an array, it should be either 1D or 2d and square. Either a
|
112
|
+
DiagonalCovariance or a FullCovariance will be returned.
|
89
113
|
"""
|
90
114
|
if isinstance(cov, list):
|
91
|
-
s = 0
|
92
115
|
for block in cov:
|
93
116
|
block = np.atleast_2d(block)
|
94
117
|
if (block.ndim != 2) or (block.shape[0] != block.shape[1]):
|
95
118
|
raise ValueError("Covariance block has wrong size "
|
96
119
|
f"or shape {block.shape}")
|
97
|
-
s += block.shape[0]
|
98
120
|
return BlockDiagonalCovariance(cov)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
return FullCovariance(cov)
|
121
|
+
cov = np.array(cov).squeeze()
|
122
|
+
if cov.ndim == 0:
|
123
|
+
return DiagonalCovariance(np.atleast_1d(cov))
|
124
|
+
if cov.ndim == 1:
|
125
|
+
return DiagonalCovariance(cov)
|
126
|
+
if (cov.ndim != 2) or (cov.shape[0] != cov.shape[1]):
|
127
|
+
raise ValueError("Covariance is not a 2D square matrix "
|
128
|
+
f"- shape: {cov.shape}")
|
129
|
+
return FullCovariance(cov)
|
109
130
|
|
110
131
|
@property
|
111
132
|
def dense(self):
|
@@ -140,7 +161,7 @@ class BaseCovariance:
|
|
140
161
|
return self._dense_inverse
|
141
162
|
|
142
163
|
|
143
|
-
class FullCovariance(BaseCovariance,
|
164
|
+
class FullCovariance(BaseCovariance, type_name='full'):
|
144
165
|
"""
|
145
166
|
A covariance subclass representing a full matrix with correlations
|
146
167
|
anywhere. Represented as an n x n matrix.
|
@@ -153,15 +174,40 @@ class FullCovariance(BaseCovariance, cov_type='full'):
|
|
153
174
|
covmat: 2D array
|
154
175
|
The matrix itself, of shape (size x size)
|
155
176
|
"""
|
177
|
+
|
178
|
+
storage_type = ONE_OBJECT_PER_TABLE
|
179
|
+
|
156
180
|
def __init__(self, covmat):
|
157
181
|
self.covmat = np.atleast_2d(covmat)
|
158
182
|
self.size = self.covmat.shape[0]
|
159
183
|
super().__init__()
|
160
184
|
|
161
|
-
def
|
185
|
+
def __eq__(self, other):
|
186
|
+
return super().__eq__(other) and \
|
187
|
+
np.allclose(self.covmat, other.covmat)
|
188
|
+
|
189
|
+
@classmethod
|
190
|
+
def from_hdu(cls, hdu):
|
191
|
+
"""
|
192
|
+
|
193
|
+
Load a covariance object from the data in the HDU
|
194
|
+
LEGACY METHOD: new sacc files will use from_table
|
195
|
+
|
196
|
+
Parameters
|
197
|
+
----------
|
198
|
+
hdu: astropy.fits.ImageHDU instance
|
199
|
+
|
200
|
+
Returns
|
201
|
+
-------
|
202
|
+
cov: FullCovariance
|
203
|
+
Loaded covariance object
|
204
|
+
"""
|
205
|
+
C = hdu.data
|
206
|
+
return cls(C)
|
207
|
+
|
208
|
+
def to_table(self):
|
162
209
|
"""
|
163
|
-
Make an astropy
|
164
|
-
This is represented as an image.
|
210
|
+
Make an astropy table object with this covariance in it.
|
165
211
|
|
166
212
|
Parameters
|
167
213
|
----------
|
@@ -169,32 +215,33 @@ class FullCovariance(BaseCovariance, cov_type='full'):
|
|
169
215
|
|
170
216
|
Returns
|
171
217
|
-------
|
172
|
-
|
173
|
-
|
218
|
+
table: astropy.table.Table instance
|
219
|
+
Table that can be used to reconstruct the object.
|
174
220
|
"""
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
return hdu
|
221
|
+
col_names = [f'col_{i}' for i in range(self.size)]
|
222
|
+
cols = [self.covmat[i] for i in range(self.size)]
|
223
|
+
table = Table(data=cols, names=col_names)
|
224
|
+
table.meta['SIZE'] = self.size
|
225
|
+
return table
|
181
226
|
|
182
227
|
@classmethod
|
183
|
-
def
|
228
|
+
def from_table(cls, table):
|
184
229
|
"""
|
185
|
-
Load a covariance object from the data in the
|
230
|
+
Load a covariance object from the data in the table
|
186
231
|
|
187
232
|
Parameters
|
188
233
|
----------
|
189
|
-
|
234
|
+
table: astropy.table.Table instance
|
190
235
|
|
191
236
|
Returns
|
192
237
|
-------
|
193
238
|
cov: FullCovariance
|
194
239
|
Loaded covariance object
|
195
240
|
"""
|
196
|
-
|
197
|
-
|
241
|
+
size = table.meta['SIZE']
|
242
|
+
covmat = np.array([table[f'col_{i}'] for i in range(size)])
|
243
|
+
return cls(covmat)
|
244
|
+
|
198
245
|
|
199
246
|
def keeping_indices(self, indices):
|
200
247
|
"""
|
@@ -240,7 +287,7 @@ class FullCovariance(BaseCovariance, cov_type='full'):
|
|
240
287
|
return self.covmat.copy()
|
241
288
|
|
242
289
|
|
243
|
-
class BlockDiagonalCovariance(BaseCovariance,
|
290
|
+
class BlockDiagonalCovariance(BaseCovariance, type_name='block'):
|
244
291
|
"""A covariance subclass representing block diagonal covariances
|
245
292
|
|
246
293
|
Block diagonal covariances have sub-blocks that are full dense matrices,
|
@@ -258,6 +305,9 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
258
305
|
size: int
|
259
306
|
overall total size of the matrix
|
260
307
|
"""
|
308
|
+
|
309
|
+
storage_type = ONE_OBJECT_MULTIPLE_TABLES
|
310
|
+
|
261
311
|
def __init__(self, blocks):
|
262
312
|
"""Create a BlockDiagonalCovariance object from a list of blocks
|
263
313
|
|
@@ -271,34 +321,17 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
271
321
|
self.size = sum(self.block_sizes)
|
272
322
|
super().__init__()
|
273
323
|
|
274
|
-
def
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
Parameters
|
281
|
-
----------
|
282
|
-
None
|
283
|
-
|
284
|
-
Returns
|
285
|
-
-------
|
286
|
-
hdu: astropy.fits.ImageHDU object
|
287
|
-
HDU containing data and metadata
|
288
|
-
"""
|
289
|
-
hdu = fits.ImageHDU(np.concatenate([b.flatten() for b in self.blocks]))
|
290
|
-
hdu.name = 'covariance'
|
291
|
-
hdu.header['sacctype'] = 'cov'
|
292
|
-
hdu.header['saccclss'] = self.cov_type
|
293
|
-
hdu.header['size'] = self.size
|
294
|
-
hdu.header['blocks'] = len(self.blocks)
|
295
|
-
for i, s in enumerate(self.block_sizes):
|
296
|
-
hdu.header[f'size_{i}'] = s
|
297
|
-
return hdu
|
324
|
+
def __eq__(self, other):
|
325
|
+
return super().__eq__(other) and \
|
326
|
+
self.block_sizes == other.block_sizes and \
|
327
|
+
all(np.allclose(b1, b2)
|
328
|
+
for b1, b2
|
329
|
+
in zip(self.blocks, other.blocks))
|
298
330
|
|
299
331
|
@classmethod
|
300
332
|
def from_hdu(cls, hdu):
|
301
333
|
"""Read a covariance object from a loaded FITS HDU.
|
334
|
+
LEGACY METHOD: new sacc files will use from_tables
|
302
335
|
|
303
336
|
Parameters
|
304
337
|
----------
|
@@ -320,6 +353,59 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
320
353
|
blocks.append(B)
|
321
354
|
return cls(blocks)
|
322
355
|
|
356
|
+
@classmethod
|
357
|
+
def from_tables(cls, tables):
|
358
|
+
"""
|
359
|
+
Load a covariance object from the data in the tables
|
360
|
+
|
361
|
+
Parameters
|
362
|
+
----------
|
363
|
+
tables: list
|
364
|
+
list of astropy.table.Table instances in block order
|
365
|
+
|
366
|
+
Returns
|
367
|
+
-------
|
368
|
+
cov: BlockDiagonalCovariance
|
369
|
+
Loaded covariance object
|
370
|
+
"""
|
371
|
+
|
372
|
+
blocks = []
|
373
|
+
# Get the block count from the first table
|
374
|
+
nblock = list(tables.values())[0].meta['SACCBCNT']
|
375
|
+
for i in range(nblock):
|
376
|
+
table = tables[f'block_{i}']
|
377
|
+
block_size = table.meta['SACCBSZE']
|
378
|
+
cols = [table[f'block_col_{i}'] for i in range(block_size)]
|
379
|
+
blocks.append(np.array(cols))
|
380
|
+
return cls(blocks)
|
381
|
+
|
382
|
+
def to_tables(self):
|
383
|
+
"""
|
384
|
+
Make an astropy table object with this covariance in it.
|
385
|
+
|
386
|
+
Parameters
|
387
|
+
----------
|
388
|
+
None
|
389
|
+
|
390
|
+
Returns
|
391
|
+
-------
|
392
|
+
table: astropy.table.Table instance
|
393
|
+
Table that can be used to reconstruct the object.
|
394
|
+
"""
|
395
|
+
tables = {}
|
396
|
+
nblock = len(self.blocks)
|
397
|
+
for j, block in enumerate(self.blocks):
|
398
|
+
b = len(block)
|
399
|
+
col_names = [f'block_col_{i}' for i in range(b)]
|
400
|
+
cols = [block[i] for i in range(b)]
|
401
|
+
table = Table(data=cols, names=col_names)
|
402
|
+
table.meta['SIZE'] = self.size
|
403
|
+
table.meta['SACCBIDX'] = j
|
404
|
+
table.meta['SACCBCNT'] = nblock
|
405
|
+
table.meta['SACCBSZE'] = b
|
406
|
+
tables[f'block_{j}'] = table
|
407
|
+
return tables
|
408
|
+
|
323
409
|
def get_block(self, indices):
|
324
410
|
"""Read a (not necessarily contiguous) sublock of the matrix
|
325
411
|
|
@@ -378,7 +464,7 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
378
464
|
blocks = [self.blocks[i][m][:, m] for i, m in
|
379
465
|
enumerate(block_masks)]
|
380
466
|
return self.__class__(blocks)
|
381
|
-
|
467
|
+
if (np.diff(indices) > 0).all():
|
382
468
|
s = 0
|
383
469
|
sub_blocks = []
|
384
470
|
for block, sz in zip(self.blocks, self.block_sizes):
|
@@ -387,10 +473,9 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
387
473
|
sub_blocks.append(block[m][:, m])
|
388
474
|
s += sz
|
389
475
|
return self.__class__(sub_blocks)
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
return FullCovariance(C)
|
476
|
+
C = scipy.linalg.block_diag(*self.blocks)
|
477
|
+
C = C[indices][:, indices]
|
478
|
+
return FullCovariance(C)
|
394
479
|
|
395
480
|
def _get_dense_inverse(self):
|
396
481
|
# Invert all the blocks individually and then
|
@@ -405,7 +490,7 @@ class BlockDiagonalCovariance(BaseCovariance, cov_type='block'):
|
|
405
490
|
return scipy.linalg.block_diag(*self.blocks)
|
406
491
|
|
407
492
|
|
408
|
-
class DiagonalCovariance(BaseCovariance,
|
493
|
+
class DiagonalCovariance(BaseCovariance, type_name='diagonal'):
|
409
494
|
"""A covariance subclass representing covariances that are
|
410
495
|
purely diagonal.
|
411
496
|
|
@@ -417,6 +502,9 @@ class DiagonalCovariance(BaseCovariance, cov_type='diagonal'):
|
|
417
502
|
diag: array
|
418
503
|
The diagonal terms in the covariance (i.e. the variances)
|
419
504
|
"""
|
505
|
+
|
506
|
+
storage_type = ONE_OBJECT_PER_TABLE
|
507
|
+
|
420
508
|
def __init__(self, variances):
|
421
509
|
"""
|
422
510
|
Create a DiagonalCovariance object from the variances
|
@@ -431,26 +519,10 @@ class DiagonalCovariance(BaseCovariance, cov_type='diagonal'):
|
|
431
519
|
self.size = len(self.diag)
|
432
520
|
super().__init__()
|
433
521
|
|
434
|
-
def
|
435
|
-
|
436
|
-
|
437
|
-
In this can a binary table HDU is created.
|
522
|
+
def __eq__(self, other):
|
523
|
+
return super().__eq__(other) and \
|
524
|
+
np.allclose(self.diag, other.diag)
|
438
525
|
|
439
|
-
Parameters
|
440
|
-
----------
|
441
|
-
None
|
442
|
-
|
443
|
-
Returns
|
444
|
-
-------
|
445
|
-
hdu: astropy.fits.BinTableHDU instance
|
446
|
-
HDU that can be used to reconstruct the object.
|
447
|
-
"""
|
448
|
-
table = Table(names=['variance'], data=[self.diag])
|
449
|
-
hdu = fits.table_to_hdu(table)
|
450
|
-
hdu.name = 'covariance'
|
451
|
-
hdu.header['sacctype'] = 'cov'
|
452
|
-
hdu.header['saccclss'] = self.cov_type
|
453
|
-
return hdu
|
454
526
|
|
455
527
|
def keeping_indices(self, indices):
|
456
528
|
"""
|
@@ -472,6 +544,40 @@ class DiagonalCovariance(BaseCovariance, cov_type='diagonal'):
|
|
472
544
|
D = self.diag[indices]
|
473
545
|
return self.__class__(D)
|
474
546
|
|
547
|
+
@classmethod
|
548
|
+
def from_table(cls, table):
|
549
|
+
"""
|
550
|
+
Load a covariance object from the data in the table
|
551
|
+
|
552
|
+
Parameters
|
553
|
+
----------
|
554
|
+
table: astropy.table.Table instance
|
555
|
+
|
556
|
+
Returns
|
557
|
+
-------
|
558
|
+
cov: DiagonalCovariance
|
559
|
+
Loaded covariance object
|
560
|
+
"""
|
561
|
+
D = table['variance']
|
562
|
+
return cls(D)
|
563
|
+
|
564
|
+
def to_table(self):
|
565
|
+
"""
|
566
|
+
Make an astropy table object with this covariance in it.
|
567
|
+
|
568
|
+
Parameters
|
569
|
+
----------
|
570
|
+
None
|
571
|
+
|
572
|
+
Returns
|
573
|
+
-------
|
574
|
+
table: astropy.table.Table instance
|
575
|
+
Table that can be used to reconstruct the object.
|
576
|
+
"""
|
577
|
+
table = Table(data=[self.diag], names=['variance'])
|
578
|
+
table.meta['SIZE'] = self.size
|
579
|
+
return table
|
580
|
+
|
475
581
|
@classmethod
|
476
582
|
def from_hdu(cls, hdu):
|
477
583
|
"""
|
sacc/data_types.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
from collections import namedtuple
|
2
2
|
from astropy.table import Table
|
3
3
|
|
4
|
-
from .utils import (Namespace, hide_null_values,
|
4
|
+
from .utils import (Namespace, hide_null_values, numpy_to_vanilla,
|
5
5
|
null_values, camel_case_split_and_lowercase)
|
6
|
+
from .io import BaseIO, MULTIPLE_OBJECTS_PER_TABLE
|
6
7
|
|
7
8
|
# The format for a data type name looks like this:
|
8
9
|
# {sources}_{properties}_{statistic_type}[_{statistic_subtype}]
|
@@ -220,8 +221,7 @@ def build_data_type_name(sources, properties, statistic, subtype=None):
|
|
220
221
|
for s in properties[1:]])
|
221
222
|
if subtype:
|
222
223
|
return f"{sources}_{properties}_{statistic}_{subtype}"
|
223
|
-
|
224
|
-
return f"{sources}_{properties}_{statistic}"
|
224
|
+
return f"{sources}_{properties}_{statistic}"
|
225
225
|
|
226
226
|
|
227
227
|
# This makes a namespace object, so you can do:
|
@@ -232,7 +232,7 @@ def build_data_type_name(sources, properties, statistic, subtype=None):
|
|
232
232
|
standard_types = Namespace(*required_tags.keys())
|
233
233
|
|
234
234
|
|
235
|
-
class DataPoint:
|
235
|
+
class DataPoint(BaseIO, type_name="DataPoint"):
|
236
236
|
"""A class for a single data point (one scalar value).
|
237
237
|
|
238
238
|
Data points have a type, zero or more tracers, a value,
|
@@ -258,6 +258,8 @@ class DataPoint:
|
|
258
258
|
Dictionary of further data point metadata, such as binning
|
259
259
|
info, angles, etc.
|
260
260
|
"""
|
261
|
+
storage_type = MULTIPLE_OBJECTS_PER_TABLE
|
262
|
+
_sub_classes = {}
|
261
263
|
def __init__(self, data_type, tracers, value,
|
262
264
|
ignore_missing_tags=False, **tags):
|
263
265
|
"""Create a new data point.
|
@@ -297,12 +299,38 @@ class DataPoint:
|
|
297
299
|
f"{data_type} "
|
298
300
|
"(ignore_missing_tags=False)")
|
299
301
|
|
302
|
+
|
300
303
|
def __repr__(self):
|
301
|
-
t = ", ".join(f'{k}={v}' for (k, v) in self.tags.items())
|
304
|
+
t = ", ".join(f'{k}={v}' for (k, v) in self.tags.items() if k != 'sacc_ordering')
|
302
305
|
st = f"DataPoint(data_type='{self.data_type}', "
|
303
306
|
st += f"tracers={self.tracers}, value={self.value}, {t})"
|
304
307
|
return st
|
305
308
|
|
309
|
+
def __eq__(self, other):
|
310
|
+
"""
|
311
|
+
Check equality with another DataPoint.
|
312
|
+
|
313
|
+
This method compares the current DataPoint instance with another
|
314
|
+
to determine if they are equivalent. Two DataPoints are considered
|
315
|
+
equal if they have the same data_type, tracers, value, and tags.
|
316
|
+
|
317
|
+
Parameters
|
318
|
+
----------
|
319
|
+
other: DataPoint
|
320
|
+
The other DataPoint instance to compare against.
|
321
|
+
|
322
|
+
Returns
|
323
|
+
-------
|
324
|
+
bool
|
325
|
+
True if the DataPoints are equal, False otherwise.
|
326
|
+
"""
|
327
|
+
if not isinstance(other, DataPoint):
|
328
|
+
return False
|
329
|
+
return (self.data_type == other.data_type and
|
330
|
+
self.tracers == other.tracers and
|
331
|
+
self.value == other.value and
|
332
|
+
self.tags == other.tags)
|
333
|
+
|
306
334
|
def get_tag(self, tag, default=None):
|
307
335
|
"""
|
308
336
|
Get the value of the the named tag, or None if not found.
|
@@ -356,10 +384,37 @@ class DataPoint:
|
|
356
384
|
tracers = [f'tracer_{i}' for i in range(ntracer)]
|
357
385
|
return tracers, tags
|
358
386
|
|
387
|
+
@classmethod
|
388
|
+
def to_tables(cls, data, lookups=None):
|
389
|
+
data_by_type = {}
|
390
|
+
for i, d in enumerate(data):
|
391
|
+
d.tags['sacc_ordering'] = i
|
392
|
+
# Get the data type name
|
393
|
+
data_type = d.data_type
|
394
|
+
if data_type not in data_by_type:
|
395
|
+
data_by_type[data_type] = []
|
396
|
+
# Add the data point to the table for this type
|
397
|
+
data_by_type[data_type].append(d)
|
398
|
+
|
399
|
+
tables = {}
|
400
|
+
for data_type, data_points in data_by_type.items():
|
401
|
+
# Convert the data points to a table
|
402
|
+
table = cls.to_table(data_points, lookups)
|
403
|
+
# Add metadata to the table
|
404
|
+
table.meta['SACCTYPE'] = 'data'
|
405
|
+
table.meta['SACCNAME'] = data_type
|
406
|
+
tables[data_type] = table
|
407
|
+
|
408
|
+
# Now remove the temporary ordering tag
|
409
|
+
for d in data:
|
410
|
+
del d.tags['sacc_ordering']
|
411
|
+
|
412
|
+
return tables
|
413
|
+
|
359
414
|
@classmethod
|
360
415
|
def to_table(cls, data, lookups=None):
|
361
416
|
"""
|
362
|
-
Convert a list of data points to a single homogenous table
|
417
|
+
Convert a list of data points to a single homogenous table.
|
363
418
|
|
364
419
|
Since data points can have varying tags, this method uses
|
365
420
|
null values to represent non-present tags.
|
@@ -424,11 +479,11 @@ class DataPoint:
|
|
424
479
|
data = []
|
425
480
|
for row in table:
|
426
481
|
# Get basic data elements
|
427
|
-
tracers = tuple([row[f'tracer_{i}'] for i in range(nt)])
|
428
|
-
value = row['value']
|
482
|
+
tracers = tuple([numpy_to_vanilla(row[f'tracer_{i}']) for i in range(nt)])
|
483
|
+
value = numpy_to_vanilla(row['value'])
|
429
484
|
|
430
485
|
# Deal with tags. First just pull out all remaining columns
|
431
|
-
tags = {name: row[name] for name in tag_names}
|
486
|
+
tags = {numpy_to_vanilla(name): row[name] for name in tag_names}
|
432
487
|
for k, v in list(tags.items()):
|
433
488
|
# Deal with any tags that we should replace.
|
434
489
|
# This is mainly used for Window instances.
|