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/tracers/nz.py ADDED
@@ -0,0 +1,285 @@
1
+ from .base import BaseTracer, ONE_OBJECT_PER_TABLE, ONE_OBJECT_MULTIPLE_TABLES
2
+ from astropy.table import Table
3
+ import numpy as np
4
+ from ..utils import remove_dict_null_values
5
+
6
+ class NZTracer(BaseTracer, type_name='NZ'):
7
+ """
8
+ A Tracer type for tomographic n(z) data.
9
+
10
+ Takes two arguments arrays of z and n(z)
11
+
12
+ Parameters
13
+ ----------
14
+ name: str
15
+ The name for this specific tracer, e.g. a
16
+ tomographic bin identifier.
17
+
18
+ z: array
19
+ Redshift sample values
20
+
21
+ nz: array
22
+ Number density n(z) at redshift sample points.
23
+
24
+ extra_columns: dict[str: array] or dict[int: array]
25
+ Additional estimates of the same n(z), by name
26
+ """
27
+
28
+ storage_type = ONE_OBJECT_PER_TABLE
29
+
30
+ def __init__(self, name, z, nz,
31
+ extra_columns=None, **kwargs):
32
+ """
33
+ Create a tracer corresponding to a distribution in redshift n(z),
34
+ for example of galaxies.
35
+
36
+ Parameters
37
+ ----------
38
+ name: str
39
+ The name for this specific tracer, e.g. a
40
+ tomographic bin identifier.
41
+
42
+ z: array
43
+ Redshift sample values
44
+
45
+ nz: array
46
+ Number density n(z) at redshift sample points.
47
+
48
+ extra_columns: dict[str:array]
49
+ Optional, default=None. Additional realizations or
50
+ estimates of the same n(z), by name.
51
+
52
+ Returns
53
+ -------
54
+ instance: NZTracer object
55
+ An instance of this class
56
+ """
57
+ super().__init__(name, **kwargs)
58
+ self.z = np.array(z)
59
+ self.nz = np.array(nz)
60
+ self.extra_columns = {} if extra_columns is None else extra_columns
61
+
62
+ def to_table(self):
63
+ """Convert a list of NZTracers to a list of astropy tables
64
+
65
+ This is used when saving data to a file.
66
+ One table is generated per tracer.
67
+
68
+ Parameters
69
+ ----------
70
+ instance_list: list
71
+ List of tracer instances
72
+
73
+ Returns
74
+ -------
75
+ tables: list
76
+ List of astropy tables
77
+ """
78
+ names = ['z', 'nz']
79
+ cols = [self.z, self.nz]
80
+ for nz_id, col in self.extra_columns.items():
81
+ names.append(str(nz_id))
82
+ cols.append(col)
83
+ table = Table(data=cols, names=names)
84
+ table.meta['SACCQTTY'] = self.quantity
85
+ # This will also get set at the higher level
86
+ # if the tracer is save to a file, but for
87
+ # testing it's useful to have it here too.
88
+ table.meta['SACCNAME'] = self.name
89
+ for key, value in self.metadata.items():
90
+ table.meta['META_'+key] = value
91
+ remove_dict_null_values(table.meta)
92
+ return table
93
+
94
+ @classmethod
95
+ def from_table(cls, table):
96
+ """Convert an astropy table into a an n(z) tracer
97
+
98
+ This is used when loading data from a file.
99
+
100
+ Parameters
101
+ ----------
102
+ table: astropy.table.Table
103
+ Must contain the appropriate data, for example as saved
104
+ by to_table.
105
+
106
+ Returns
107
+ -------
108
+ tracers: dict
109
+ Dict mapping string names to tracer objects.
110
+ Only contains one key/value pair for the one tracer.
111
+ """
112
+ name = table.meta['SACCNAME']
113
+ quantity = table.meta.get('SACCQTTY', 'generic')
114
+ z = table['z']
115
+ nz = table['nz']
116
+ extra_columns = {}
117
+ for col in table.columns.values():
118
+ if col.name not in ['z', 'nz']:
119
+ extra_columns[col.name] = col.data
120
+
121
+ metadata = {}
122
+ for key, value in table.meta.items():
123
+ if key.startswith("META_"):
124
+ metadata[key[5:]] = value
125
+ return cls(name, z, nz,
126
+ quantity=quantity,
127
+ extra_columns=extra_columns,
128
+ metadata=metadata)
129
+
130
+
131
+
132
+ class QPNZTracer(BaseTracer, type_name='QPNZ'):
133
+ """
134
+ A Tracer type for tomographic n(z) data represented as a `qp.Ensemble`
135
+
136
+ Takes a `qp.Ensemble` and optionally a redshift array.
137
+
138
+ Requires the `qp` and `tables_io` packages to be installed.
139
+
140
+ Parameters
141
+ ----------
142
+ name: str
143
+ The name for this specific tracer, e.g. a
144
+ tomographic bin identifier.
145
+
146
+ ensemble: qp.Ensemble
147
+ The qp.ensemble in questions
148
+ """
149
+
150
+ storage_type = ONE_OBJECT_MULTIPLE_TABLES
151
+
152
+ def __init__(self, name, ens, z=None, nz=None, **kwargs):
153
+ """
154
+ Create a tracer corresponding to a distribution in redshift n(z),
155
+ for example of galaxies.
156
+
157
+ Parameters
158
+ ----------
159
+ name: str
160
+ The name for this specific tracer, e.g. a
161
+ tomographic bin identifier.
162
+
163
+ ensemble: qp.Ensemble
164
+ The qp.ensemble in questions
165
+
166
+ z: array
167
+ Optional grid of redshift values at which to evaluate the ensemble.
168
+ If left as None then the ensemble metadata is checked for a grid.
169
+ If that is not present then no redshift grid is saved.
170
+ nz: array
171
+ Optional, default=None. establishes a fiducial n(z) for the ensemble.
172
+ This can later be used to compute systematic biases in the ensemble.
173
+
174
+ Returns
175
+ -------
176
+ instance: NZTracer object
177
+ An instance of this class
178
+ """
179
+ super().__init__(name, **kwargs)
180
+ self.ensemble = ens
181
+ if z is None:
182
+ ens_meta = ens.metadata
183
+ if 'bins' in list(ens_meta.keys()):
184
+ z = ens_meta['bins'][0]
185
+ self.z = z
186
+ if z is None:
187
+ self.nz = None
188
+ else:
189
+ if nz is not None:
190
+ if len(nz) != len(z):
191
+ raise ValueError("nz must have the same length as z")
192
+ self.nz = nz
193
+ else:
194
+ nz = np.atleast_2d(ens.pdf(z))
195
+ self.nz = np.mean(nz, axis=0)
196
+
197
+ def to_tables(self):
198
+ """Convert a list of NZTracers to a list of astropy tables
199
+
200
+ This is used when saving data to a file.
201
+ Two or three tables are generated per tracer.
202
+
203
+ Parameters
204
+ ----------
205
+ instance_list: list
206
+ List of tracer instances
207
+
208
+ Returns
209
+ -------
210
+ tables: list
211
+ List of astropy tables
212
+ """
213
+ from ..utils import convert_to_astropy_table
214
+ tables = {}
215
+
216
+ table_dict = self.ensemble.build_tables()
217
+
218
+ data_table = convert_to_astropy_table(table_dict['data'])
219
+ data_table.meta['SACCQTTY'] = self.quantity
220
+ data_table.meta['SACCNAME'] = self.name
221
+ tables["data"] = data_table
222
+
223
+ meta_table = convert_to_astropy_table(table_dict['meta'])
224
+ meta_table.meta['SACCQTTY'] = self.quantity
225
+ meta_table.meta['SACCNAME'] = self.name
226
+
227
+ for kk, vv in self.metadata.items():
228
+ meta_table.meta['META_'+kk] = vv
229
+
230
+ tables["meta"] = meta_table
231
+
232
+ if 'ancil' in table_dict:
233
+ ancil_table = convert_to_astropy_table(table_dict['ancil'])
234
+ ancil_table.meta['SACCQTTY'] = self.quantity
235
+ ancil_table.meta['SACCNAME'] = self.name
236
+ tables["ancil"] = ancil_table
237
+
238
+ if self.z is not None:
239
+ names = ['z', 'nz']
240
+ cols = [self.z, self.nz]
241
+ fid_table = Table(data=cols, names=names)
242
+ fid_table.meta['SACCQTTY'] = self.quantity
243
+ fid_table.meta['SACCNAME'] = self.name
244
+ tables["fid"] = fid_table
245
+
246
+
247
+ return tables
248
+
249
+ @classmethod
250
+ def from_tables(cls, tables):
251
+ """Convert a dict of astropy tables into a tracer
252
+
253
+ This is used when loading data from a file.
254
+ A single tracer object is read from the table.
255
+
256
+ Parameters
257
+ ----------
258
+ tables: dict[str,astropy.table.Table]
259
+ Must contain the appropriate data, for example as saved
260
+ by to_tables.
261
+
262
+ Returns
263
+ -------
264
+ tracer: QPNZTracer
265
+ """
266
+ import qp
267
+
268
+
269
+ meta_table = tables['meta']
270
+ if 'fid' in tables:
271
+ z = tables['fid']['z']
272
+ nz = tables['fid']['nz']
273
+ else:
274
+ z = None
275
+ nz = None
276
+ ensemble = qp.from_tables(tables)
277
+ name = meta_table.meta['SACCNAME']
278
+ quantity = meta_table.meta.get('SACCQTTY', 'generic')
279
+ metadata = {}
280
+ for key, value in meta_table.meta.items():
281
+ if key.startswith("META_"):
282
+ metadata[key[5:]] = value
283
+ return cls(name, ensemble, z=z, nz=nz,
284
+ quantity=quantity,
285
+ metadata=metadata)
sacc/tracers/survey.py ADDED
@@ -0,0 +1,75 @@
1
+ from .base import BaseTracer, MULTIPLE_OBJECTS_PER_TABLE
2
+ from astropy.table import Table
3
+
4
+
5
+ class SurveyTracer(BaseTracer, type_name="survey"): # type: ignore
6
+ """A tracer for the survey definition. It shall
7
+ be used to filter data related to a given survey
8
+ and to provide the survey sky-area of analysis."""
9
+
10
+ storage_type = MULTIPLE_OBJECTS_PER_TABLE
11
+
12
+ def __eq__(self, other) -> bool:
13
+ """Test for equality. If :python:`other` is not a
14
+ :python:`SurveyTracer`, then it is not equal to :python:`self`.
15
+ Otherwise, they are equal if names and the sky-areas are equal."""
16
+ if not isinstance(other, SurveyTracer):
17
+ return False
18
+ return self.name == other.name and self.sky_area == other.sky_area
19
+
20
+ def __init__(self, name: str, sky_area: float, **kwargs):
21
+ """
22
+ Create a tracer corresponding to the survey definition.
23
+
24
+ :param name: The name of the tracer
25
+ :param sky_area: The survey's sky area in square degrees
26
+ """
27
+ super().__init__(name, **kwargs)
28
+ self.sky_area = sky_area
29
+
30
+ @classmethod
31
+ def to_table(cls, instance_list):
32
+ """Convert a list of SurveyTracer to a list of astropy tables
33
+
34
+ This is used when saving data to a file.
35
+ One table is generated with the information for all the tracers.
36
+
37
+ :param instance_list: List of tracer instances
38
+ :return: List of astropy tables with one table
39
+ """
40
+ names = ["name", "quantity", "sky_area"]
41
+
42
+ cols = [
43
+ [obj.name for obj in instance_list],
44
+ [obj.quantity for obj in instance_list],
45
+ [obj.sky_area for obj in instance_list],
46
+ ]
47
+
48
+ table = Table(data=cols, names=names)
49
+ table.meta["SACCTYPE"] = "tracer"
50
+ table.meta["SACCCLSS"] = cls.type_name
51
+ table.meta["EXTNAME"] = f"tracer:{cls.type_name}"
52
+ return table
53
+
54
+ @classmethod
55
+ def from_table(cls, table):
56
+ """Convert an astropy table into a dictionary of tracers
57
+
58
+ This is used when loading data from a file.
59
+ One tracer object is created for each "row" in each table.
60
+
61
+ :param table_list: List of astropy tables
62
+ :return: Dictionary of tracers
63
+ """
64
+ tracers = {}
65
+
66
+ for row in table:
67
+ name = row["name"]
68
+ quantity = row["quantity"]
69
+ sky_area = row["sky_area"]
70
+ tracers[name] = cls(
71
+ name,
72
+ quantity=quantity,
73
+ sky_area=sky_area,
74
+ )
75
+ return tracers
sacc/utils.py CHANGED
@@ -151,8 +151,7 @@ def invert_spd_matrix(M, strict=True):
151
151
  invM, info = scipy.linalg.lapack.dpotri(L)
152
152
  if info:
153
153
  raise ValueError("Matrix is not symmetric-positive-definite")
154
- else:
155
- invM = np.triu(invM) + np.triu(invM, k=1).T
154
+ invM = np.triu(invM) + np.triu(invM, k=1).T
156
155
  # Otherwise we use the generic (and also slower) method that will
157
156
  # work if, due to numerical issues, the matrix is not quite SPD
158
157
  else:
@@ -166,3 +165,48 @@ def camel_case_split_and_lowercase(identifier):
166
165
  '|(?<=[A-Z])(?=[A-Z][a-z])|$)',
167
166
  identifier)
168
167
  return [m.group(0).lower() for m in matches]
168
+
169
+
170
+ def convert_to_astropy_table(obj):
171
+ try:
172
+ from tables_io.convUtils import convertToApTables
173
+ version = 1
174
+ except ImportError:
175
+ try:
176
+ from tables_io import convert_table
177
+ version = 2
178
+ except ImportError:
179
+ raise ImportError("Error importing table conversion tool from tables_io. "
180
+ "Maybe they changed its name again. Please open an issue, "
181
+ "assuming you have tables_io installed.")
182
+
183
+ if version == 1:
184
+ return convertToApTables(obj)
185
+ if version == 2:
186
+ return convert_table(obj, "astropyTable")
187
+ raise ValueError("Unknown version of tables_io conversion tool.")
188
+
189
+
190
+ def numpy_to_vanilla(x):
191
+ """
192
+ Convert a NumPy scalar type to its corresponding Python built-in type.
193
+
194
+ Parameters
195
+ ----------
196
+ x : numpy scalar
197
+ A NumPy scalar value (e.g., np.str_, np.int64, np.float64, np.bool).
198
+
199
+ Returns
200
+ -------
201
+ object
202
+ The equivalent Python built-in type (e.g., str, int, float, bool).
203
+ """
204
+ if type(x) == np.str_:
205
+ x = str(x)
206
+ elif type(x) == np.int64:
207
+ x = int(x)
208
+ elif type(x) == np.float64:
209
+ x = float(x)
210
+ elif type(x) == np.bool:
211
+ x = bool(x)
212
+ return x