Проблема в том, что apply_along_axis
выполняет итерации по 1D-фрагментам первого аргумента для прикладной функции, а не для других. Если я правильно понимаю ваш сценарий использования, вы на самом деле хотите перебрать 1D-фрагменты весов (weights
в подписи np.bincount
) , , а не целочисленный массив (x
в подписи np.bincount
.
Одним из способов решения этой проблемы является написание тонкой функции-оболочки вокруг np.bincount
, которая просто меняет порядок аргументов:
def wrapped_bincount(weights, x):
return np.bincount(x, weights=weights)
Затем мы можем использовать np.apply_along_axis
с этой функцией для вашего варианта использования:
def apply_bincount_along_axis(x, weights, axis=-1):
return np.apply_along_axis(wrapped_bincount, axis, weights, x)
Наконец, мы можем обернуть эту новую функцию для использования с xarray, используя apply_ufunc
, отметив, что она может автоматически распараллеливаться с dask (также обратите внимание, что нам не нужно предоставлять axis
аргумент, потому что xarray автоматически переместит размер входного ядра dim
в последнюю позицию в массиве weights
перед применением функции):
def xbincount(x, weights):
if len(x.dims) != 1:
raise ValueError('x must be one-dimensional')
dim, = x.dims
nbins = x.max() + 1
return xr.apply_ufunc(apply_bincount_along_axis, x, weights,
input_core_dims=[[dim], [dim]],
output_core_dims=[['bin']], dask='parallelized',
output_dtypes=[np.float], output_sizes={'bin': nbins})
Применение этой функции к вашему примеру выглядит следующим образом:
xbincount(ridx, f)
<xarray.DataArray (time: 2, bin: 5)>
array([[ 0. , 7.934821, 34.066872, 51.118065, 152.769169],
[ 0. , 11.692989, 33.262936, 44.993856, 157.642972]])
Dimensions without coordinates: time, bin
По желанию также работает с массивами dask:
xbincount(ridx, f.chunk({'time': 1}))
<xarray.DataArray (time: 2, bin: 5)>
dask.array<shape=(2, 5), dtype=float64, chunksize=(1, 5)>
Dimensions without coordinates: time, bin