Skip to content

Commit

Permalink
ENH(catalog): human-friendly label for catalogues (#58)
Browse files Browse the repository at this point in the history
Adds a `label` attribute to the catalogue classes that takes an
arbitrary human-friendly identifier for the base catalogue. This label,
as well as the selection of catalogue views, is stored as metadata in
maps.

Closes: #57
  • Loading branch information
ntessore authored Nov 12, 2023
1 parent 880acb3 commit bad37eb
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 84 deletions.
21 changes: 21 additions & 0 deletions heracles/catalog/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ def __getitem__(self, where):
"""create a view with the given selection"""
...

@property
def label(self):
"""return a human-friendly identifier for the source of data"""
...

@property
def base(self):
"""return the base catalogue of a view, or ``None`` if not a view"""
Expand Down Expand Up @@ -186,6 +191,11 @@ def base(self):
"""base catalogue of this view"""
return self._catalog

@property
def label(self):
"""human-friendly label of the catalogue (not settable in view)"""
return self._catalog.label

@property
def selection(self):
"""selection of this view"""
Expand Down Expand Up @@ -251,6 +261,7 @@ def __init__(self):

self._page_size = self.default_page_size
self._filters = []
self._label = None
self._visibility = None

def __copy__(self):
Expand All @@ -259,6 +270,7 @@ def __copy__(self):
other = self.__class__.__new__(self.__class__)
other._page_size = self._page_size
other._filters = self._filters.copy()
other._label = self._label
other._visibility = self._visibility
return other

Expand Down Expand Up @@ -304,6 +316,15 @@ def base(self):
"""returns ``None`` since this is not a view of another catalogue"""
return

@property
def label(self):
"""optional human-friendly label for catalogue"""
return self._label

@label.setter
def label(self, label):
self._label = label

@property
def selection(self):
"""returns ``None`` since this is not a view of another catalogue"""
Expand Down
6 changes: 6 additions & 0 deletions heracles/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,20 @@


_METADATA_COMMENTS = {
"catalog": "catalog of map",
"spin": "spin weight of map",
"kernel": "mapping kernel of map",
"nside": "NSIDE parameter of HEALPix map",
"power": "area power of map",
"catalog_1": "catalog of first map",
"spin_1": "spin weight of first map",
"kernel_1": "mapping kernel of first map",
"nside_1": "NSIDE parameter of first HEALPix map",
"power_1": "area power of first map",
"catalog_2": "catalog of second map",
"spin_2": "spin weight of second map",
"kernel_2": "mapping kernel of second map",
"nside_2": "NSIDE parameter of second HEALPix map",
"power_2": "area power of second map",
"noisbias": "noise bias of spectrum",
}
Expand Down
29 changes: 24 additions & 5 deletions heracles/maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,11 @@ def mapper(page: "CatalogPage") -> None:
# set metadata of array
update_metadata(
pos,
catalog=catalog.label,
spin=0,
nbar=nbar,
kernel="healpix",
nside=self.nside,
power=power,
bias=bias,
)
Expand Down Expand Up @@ -434,9 +436,11 @@ def mapper(page: "CatalogPage") -> None:
# set metadata of array
update_metadata(
val,
catalog=catalog.label,
spin=0,
wbar=wbar,
kernel="healpix",
nside=self.nside,
power=power,
bias=bias,
)
Expand Down Expand Up @@ -588,9 +592,11 @@ def mapper(page: "CatalogPage") -> None:
# set metadata of array
update_metadata(
val,
catalog=catalog.label,
spin=self.spin,
wbar=wbar,
kernel="healpix",
nside=self.nside,
power=power,
bias=bias,
)
Expand Down Expand Up @@ -627,7 +633,14 @@ def __call__(self, catalog: "Catalog") -> MapData:
# make a copy for updates to metadata
vmap = np.copy(vmap)

update_metadata(vmap, spin=0, kernel="healpix", power=0)
update_metadata(
vmap,
catalog=catalog.label,
spin=0,
kernel="healpix",
nside=self.nside,
power=0,
)

return vmap

Expand Down Expand Up @@ -691,7 +704,15 @@ def mapper(page: "CatalogPage") -> None:
power = 1

# set metadata of arrays
update_metadata(wht, spin=0, wbar=wbar, kernel="healpix", power=power)
update_metadata(
wht,
catalog=catalog.label,
spin=0,
wbar=wbar,
kernel="healpix",
nside=self.nside,
power=power,
)

# return the weight map
return wht
Expand Down Expand Up @@ -849,7 +870,6 @@ def transform_maps(

# convert maps to alms, taking care of complex and spin-weighted maps
for (k, i), m in maps.items():
nside = hp.get_nside(m)
if isinstance(lmax, Mapping):
_lmax = lmax.get((k, i)) or lmax.get(k)
else:
Expand Down Expand Up @@ -877,8 +897,7 @@ def transform_maps(
alms = {(f"{k}_E", i): alms[1], (f"{k}_B", i): alms[2]}

for ki, alm in alms.items():
if md:
update_metadata(alm, nside=nside, **md)
update_metadata(alm, **md)
out[ki] = alm

del m, alms, alm
Expand Down
9 changes: 9 additions & 0 deletions tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ def test_catalog_base_properties(catalog):
catalog.filters = []
assert catalog.filters == []

assert catalog.label is None
catalog.label = "label 123"
assert catalog.label == "label 123"

v = object()
assert catalog.visibility is None
catalog.visibility = v
Expand Down Expand Up @@ -235,6 +239,7 @@ def _pages(self, selection):
def test_catalog_view(catalog):
from heracles.catalog import Catalog

catalog.label = "label 123"
catalog.visibility = cvis = object()

where = object()
Expand All @@ -247,9 +252,13 @@ def test_catalog_view(catalog):
assert catalog.base is None
assert catalog.selection is None
assert view.base is catalog
assert view.label == "label 123"
assert view.selection is where
assert view.visibility is catalog.visibility

with pytest.raises(AttributeError):
view.label = "different label"

view.visibility = vvis = object()

assert view.visibility is not catalog.visibility
Expand Down
16 changes: 12 additions & 4 deletions tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ def mock_cls(rng):
import numpy as np

cl = rng.random(101)
cl.dtype = np.dtype(cl.dtype, metadata={"nside_1": 32, "nside_2": 64})
cl.dtype = np.dtype(
cl.dtype,
metadata={
"catalog_1": "cat-a.fits",
"nside_1": 32,
"catalog_2": "cat-b.fits",
"nside_2": 64,
},
)

return {
("P", "P", 0, 0): cl,
Expand Down Expand Up @@ -191,9 +199,9 @@ def test_write_read_maps(rng, tmp_path):
v = rng.random(npix)
g = rng.random((2, npix))

p.dtype = np.dtype(p.dtype, metadata={"spin": 0})
v.dtype = np.dtype(v.dtype, metadata={"spin": 0})
g.dtype = np.dtype(g.dtype, metadata={"spin": 0})
p.dtype = np.dtype(p.dtype, metadata={"catalog": "cat.fits", "spin": 0})
v.dtype = np.dtype(v.dtype, metadata={"catalog": "cat.fits", "spin": 0})
g.dtype = np.dtype(g.dtype, metadata={"catalog": "cat.fits", "spin": 2})

maps = {
("P", 1): p,
Expand Down
Loading

0 comments on commit bad37eb

Please sign in to comment.