First of all this might turn out to be more of an xarray question/issue, so apologies in advance. However it is very much related to dask and the bigger picture issue could be solved with dask.
What am I trying to do?
I am trying to write a function similar to scipy.ndimage.sum_labels, but for dask arrays (I won’t go into much detail on why I’m not using dask-image for this except I experienced some performance problems with it). The caveat is that instead of directly providing an index, I want to use da.unique to identify unique labels. Note that the labels and values arrays are results of other lazy computations.
What do I have so far?
The following is a minimal reproducible example.
import dask.array as da
import numpy as np
import xarray as xr
from scipy.ndimage import sum_labels
coords = {"y": [0, 1], "x": [0, 1, 2]}
chunks = {"y": 2, "x": 1}
values = xr.DataArray([[0.6, 0.1, 0.0], [0.2, 0.1, 0.4]], coords=coords).chunk(chunks)
labels = xr.DataArray([[1, 0, 3], [1, 1, 3]], coords=coords).chunk(chunks)
index = da.unique(labels.data)
# index.compute_chunk_sizes()
# index = index.compute()
def sum_labels_block(value_block : xr.DataArray, label_block: xr.DataArray, index) -> xr.DataArray:
block_sum = sum_labels(value_block.values, label_block.values, index)
block_sum = block_sum[np.newaxis, np.newaxis, :]
# Returns an array of size (1, 1, len(index))
return xr.DataArray(block_sum, dims=["y", "x", "index"], coords={"index": index})
y_chunks, x_chunks = map(len, values.chunks)
template_shape = (y_chunks, x_chunks, len(index))
template_chunks = (1, 1, -1)
template_values = da.zeros(shape=template_shape, dtype=values.dtype, chunks=template_chunks)
template = xr.DataArray(template_values, dims=["y", "x", "index"], coords={"index": index})
block_sums = values.map_blocks(sum_labels_block, args=(labels, index), template=template)
block_sums = block_sums.chunk(-1)
result = block_sums.sum(dim=["y", "x"])
result = result.compute()
print(result)
What is my main issue?
As it is, I get a ValueError: Cannot call len() on object with unknown chunk size.. I get that, I need to know how much space to allocate for the index dimension before computing. I can solve this in principle with a compute_chunk_sizes call. This leads to my first question:
- From a perspective of delaying computation as much as possible, is
compute_chunk_sizesactually (much) better than just callingcompute? For both I imagine all the preceding computations to arrive at the values and labels arrays must be done.
In any case, assuming compute_chunk_sizes is better than compute, I end up with an index that is a dask array. However, I still get an error: AttributeError: 'numpy.ndarray' object has no attribute '_meta'. Apparently the sum_labels_block receives an index that is also a dask array, which scipy then doesn’t seem to handle well. What I’d like is for the index in the block to be a computed numpy array (the index itself isn’t that large, the point of it being a dask array is mostly about it being lazy). Therefore my second question:
- Is there a way to pass a lazy index into the map_blocks func and make dask compute it before the blocks are actually computed, but not compute it eagerly (that is, between the da.unique and the values.map_blocks call). I also don’t want to call compute within
sum_labels_blockbecause as I see it it will get computed individually for every block which is inefficient.
And finally, because I worry my questions are already too narrowly focused on a certain solution:
- Is there in general a better approach to this problem I’m trying to solve? Preferably one that is completely lazy, i.e. can defer all computations until the final result.compute call. I’m thinking something using dask.delayed, but I’m open for anything.
Versions used for the example:
Python 3.13.5
dask 2025.7.0
xarray 2025.7.1