Cross-resolution truncation join (phase 6, espg/moczarr#1; zagg#198's O4).
The §5 nesting predicate — fine_id.startswith(coarse_id) on decimal
strings, clip2order(coarse_level, fine_words) == coarse_word on packed
words — makes cross-resolution work a lookup, not machinery:
:func:parent_cells fabricates the parent coordinate (one vectorized mortie
call) and :func:join_coarse broadcasts coarse variables onto the fine
domain by looking up each fine cell's parent row. The aggregation direction
is deliberately NOT here: fine→coarse reduction is plain xarray groupby over
the parent coordinate::
parents = moczarr.parent_cells(fine, level)
coarse = fine.groupby(parents).mean()
and persisted coarse products are zagg#300's pyramid sweep.
Origin-agnostic by design: the two datasets may come from two stores or
(once englacial/zagg#299 lands) two products of one multi-product store —
everything here operates on coordinate values, never on paths or leaf
basenames (tracking: espg/moczarr#11).
join_coarse(fine, coarse, *, variables=None, how='left', suffix=None)
Broadcast coarse variables onto the fine domain — the §5 lookup join.
Each fine cell's parent at the coarse dataset's level (derived from the
coarse morton coordinate/index — see :func:_coarse_lookup) selects
one coarse row; the selected variables land on fine's cells dimension,
answering "for this fine observation, what does the containing coarse
cell say." A values-level operation: any pairing of index kinds (pandas,
moc, or no index at all) on either side joins identically.
Parameters:
| Name |
Type |
Description |
Default |
fine
|
Dataset
|
Morton datasets (open_hive results, or anything carrying a
morton coordinate of packed words — e.g. a groupby product).
coarse must sit at a level at or above fine's (equal level is an
identity lookup); a finer coarse raises via :func:parent_cells.
|
required
|
coarse
|
Dataset
|
Morton datasets (open_hive results, or anything carrying a
morton coordinate of packed words — e.g. a groupby product).
coarse must sit at a level at or above fine's (equal level is an
identity lookup); a finer coarse raises via :func:parent_cells.
|
required
|
variables
|
sequence of str
|
Which coarse data variables to join; default all of them.
|
None
|
how
|
('left', 'inner')
|
"left" (default) keeps every fine row; rows whose parent is
absent from coarse get fill values with xarray's missing-value
semantics (the reindex/where defaults): float variables fill
with NaN, integer variables are promoted to float64 and fill
with NaN, datetimes fill with NaT. No dtype magic beyond that —
how="inner" drops the uncovered fine rows instead and preserves
integer dtypes.
|
"left"
|
suffix
|
str
|
Renames every joined variable (name + suffix). Collisions with a
fine variable are never resolved silently: they raise, naming the
colliding variables and suggesting suffix="_o{level}".
|
None
|
Returns:
| Type |
Description |
Dataset
|
fine (all rows, or the covered rows for "inner") with the
joined coarse variables added on its cells dimension. A moc-indexed
fine keeps its lazy index ("inner" subsets are monotonic).
|
Source code in src/moczarr/join.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210 | def join_coarse(fine, coarse, *, variables=None, how="left", suffix=None):
"""Broadcast coarse variables onto the fine domain — the §5 lookup join.
Each fine cell's parent at the coarse dataset's level (derived from the
coarse ``morton`` coordinate/index — see :func:`_coarse_lookup`) selects
one coarse row; the selected variables land on fine's cells dimension,
answering "for this fine observation, what does the containing coarse
cell say." A values-level operation: any pairing of index kinds (pandas,
moc, or no index at all) on either side joins identically.
Parameters
----------
fine, coarse : xarray.Dataset
Morton datasets (``open_hive`` results, or anything carrying a
``morton`` coordinate of packed words — e.g. a groupby product).
``coarse`` must sit at a level at or above fine's (equal level is an
identity lookup); a finer ``coarse`` raises via :func:`parent_cells`.
variables : sequence of str, optional
Which coarse data variables to join; default all of them.
how : {"left", "inner"}
``"left"`` (default) keeps every fine row; rows whose parent is
absent from coarse get fill values with xarray's missing-value
semantics (the ``reindex``/``where`` defaults): float variables fill
with NaN, integer variables are **promoted to float64** and fill
with NaN, datetimes fill with NaT. No dtype magic beyond that —
``how="inner"`` drops the uncovered fine rows instead and preserves
integer dtypes.
suffix : str, optional
Renames every joined variable (``name + suffix``). Collisions with a
fine variable are never resolved silently: they raise, naming the
colliding variables and suggesting ``suffix="_o{level}"``.
Returns
-------
xarray.Dataset
``fine`` (all rows, or the covered rows for ``"inner"``) with the
joined coarse variables added on its cells dimension. A moc-indexed
fine keeps its lazy index (``"inner"`` subsets are monotonic).
"""
import xarray as xr
if how not in ("left", "inner"):
raise ValueError(f"how={how!r}: expected 'left' or 'inner'")
level, lookup = _coarse_lookup(coarse)
parents = parent_cells(fine, level) # validates fine + the level direction
positions = np.asarray(lookup(parents.values), dtype=np.int64)
names = list(coarse.data_vars) if variables is None else list(variables)
unknown = [name for name in names if name not in coarse.data_vars]
if unknown:
raise ValueError(f"variables not in coarse: {', '.join(sorted(unknown))}")
renamed = {name: f"{name}{suffix}" if suffix else name for name in names}
collisions = [final for final in renamed.values() if final in fine.variables]
if collisions:
raise ValueError(
f"joined variables collide with fine variables: "
f"{', '.join(sorted(collisions))}; pass suffix= to rename them "
f"(e.g. suffix='_o{level}')"
)
fdim = fine["morton"].dims[0]
cdim = coarse["morton"].dims[0]
mask = positions >= 0
if how == "inner":
keep = np.flatnonzero(mask)
fine = fine.isel({fdim: keep}) # monotonic: a moc index survives
take, fill_mask = positions[mask], None
else:
# Missing parents point at row 0 for the take, then blank out below.
take, fill_mask = np.where(mask, positions, 0), mask
joined = coarse[names]
joined = joined.drop_vars(list(joined.coords)).isel({cdim: take})
if cdim in joined.dims and cdim != fdim:
joined = joined.rename({cdim: fdim})
if fill_mask is not None and not fill_mask.all():
covered = xr.DataArray(fill_mask, dims=fdim)
for name in joined.data_vars:
if fdim in joined[name].dims:
joined[name] = joined[name].where(covered)
joined = joined.rename(renamed)
return fine.assign({name: joined[name] for name in joined.data_vars})
|
parent_cells(obj, level)
Parent morton cells at level — the vectorized truncation.
Parameters:
| Name |
Type |
Description |
Default |
obj
|
Dataset or array - like
|
A dataset carrying a morton coordinate (an open_hive result;
on a moc-indexed dataset the coordinate is fabricated arithmetic —
no store reads), or a packed uint64 word array.
|
required
|
level
|
int
|
HEALPix order to truncate to. Must be at or above (coarser than or
equal to) the cells' own order: level equal to the cells' order
is the identity (clip2order returns words at or below the target
unchanged), and a finer level raises — truncation cannot refine.
|
required
|
Returns:
| Type |
Description |
DataArray or ndarray
|
For a Dataset: a uint64 DataArray named parent_o{level} on
the cells dimension, ready for assign_coords/groupby. For an
array: the parent words as a uint64 array.
|
Source code in src/moczarr/join.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 | def parent_cells(obj, level: int):
"""Parent morton cells at ``level`` — the vectorized truncation.
Parameters
----------
obj : xarray.Dataset or array-like
A dataset carrying a ``morton`` coordinate (an ``open_hive`` result;
on a moc-indexed dataset the coordinate is fabricated arithmetic —
no store reads), or a packed ``uint64`` word array.
level : int
HEALPix order to truncate to. Must be at or above (coarser than or
equal to) the cells' own order: ``level`` equal to the cells' order
is the identity (``clip2order`` returns words at or below the target
unchanged), and a finer ``level`` raises — truncation cannot refine.
Returns
-------
xarray.DataArray or numpy.ndarray
For a Dataset: a ``uint64`` DataArray named ``parent_o{level}`` on
the cells dimension, ready for ``assign_coords``/``groupby``. For an
array: the parent words as a ``uint64`` array.
"""
import xarray as xr # lazy: the package root stays xarray-import-free
level = int(level)
if not 0 <= level <= 29:
raise ValueError(f"level {level} outside the packed morton range 0..29")
is_dataset = isinstance(obj, xr.Dataset)
if is_dataset:
if "morton" not in obj.variables:
raise ValueError(
"dataset has no 'morton' coordinate — parent_cells needs a morton "
"dataset (an open_hive result) or a packed word array"
)
source = obj["morton"]
words = np.asarray(source.values, dtype=np.uint64)
else:
words = np.asarray(obj, dtype=np.uint64).ravel()
if words.size == 0:
raise ValueError("cannot derive an order from zero cells")
from mortie import clip2order, infer_order_from_morton
order = int(infer_order_from_morton(words))
if level > order:
raise ValueError(
f"level {level} is finer than the cells' order {order}; parent_cells "
f"truncates toward coarser levels only (level <= {order})"
)
parents = np.asarray(clip2order(level, words), dtype=np.uint64)
if is_dataset:
return xr.DataArray(parents, dims=source.dims, name=f"parent_o{level}")
return parents
|