html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,performed_via_github_app,issue
https://github.com/pydata/xarray/issues/3762#issuecomment-585452791,https://api.github.com/repos/pydata/xarray/issues/3762,585452791,MDEyOklzc3VlQ29tbWVudDU4NTQ1Mjc5MQ==,6491058,2020-02-12T22:34:56Z,2023-08-02T19:50:42Z,NONE,">
>
> Actually it looks like this example is relevant: https://xarray.pydata.org/en/stable/examples/apply_ufunc_vectorize_1d.html
Hi dcherian,
I had a look at the apply_ufunc() example you linked and have re-implemented my code. The example helped me understand apply_ufunc() usage better but is very different from my use case and I still am unable to parallelize using dask.
The key difference is apply_ufunc() as described in the docs and the example, applys a function to a vector of data of a single type (in the example case it is air temperature across the 3 dimensions lat,long,time).
Where as I need to apply an operation using heterogeneous data (depth_bins, lower_limit, upper_limit) over a single dimension (time) to produce a new array of depths over time (which is why I tried groupby/map initially).
Anyhow, I have an implementation using apply_ufunc() that works using xarray and numpy arrays with apply_ufunc(), but when I try to parallelize it using dask my ufunc is called with empty arrays by xarray and it fails.
I.e. You can see when running the code below it logs the following when entering the ufunc:
args: (array([], shape=(0, 0), dtype=int32), array([], dtype=int32), array([], dtype=int32), array([], dtype=int32)), kwargs: {}
I was expecting this to be called once for each chunk with 1000 items for each array.
Have I done something wrong in this work-around for the groupby/map code?
Thanks,
Brendon
```python
import sys
import math
import logging
import dask
import xarray
import numpy
logger = logging.getLogger('main')
if __name__ == '__main__':
logging.basicConfig(
stream=sys.stdout,
format='%(asctime)s %(levelname)-8s %(message)s',
level=logging.INFO,
datefmt='%Y-%m-%d %H:%M:%S')
logger.info('Starting dask client')
client = dask.distributed.Client()
SIZE = 3000
SONAR_BINS = 2000
upper_limit = numpy.random.randint(0, 10, (SIZE))
lower_limit = numpy.random.randint(20, 30, (SIZE))
sonar_data = numpy.random.randint(0, 255, (SIZE, SONAR_BINS))
time = range(0, SIZE)
channel = xarray.Dataset({
'upper_limit': (['time'], upper_limit, {'units': 'depth meters'}),
'lower_limit': (['time'], lower_limit, {'units': 'depth meters'}),
'data': (['time', 'depth_bin'], sonar_data, {'units': 'amplitude'}),
},
coords={
'time': (['time'], time),
'depth_bin': (['depth_bin'], range(0,SONAR_BINS)),
})
logger.info('get overall min/max radar range we want to normalize to called the adjusted range')
adjusted_min, adjusted_max = channel.upper_limit.min().values.item(), channel.lower_limit.max().values.item()
adjusted_min = math.floor(adjusted_min)
adjusted_max = math.ceil(adjusted_max)
logger.info('adjusted_min: %s, adjusted_max: %s', adjusted_min, adjusted_max)
bin_count = len(channel.depth_bin)
logger.info('bin_count: %s', bin_count)
adjusted_depth_per_bin = (adjusted_max - adjusted_min) / bin_count
logger.info('adjusted_depth_per_bin: %s', adjusted_depth_per_bin)
adjusted_bin_depths = [adjusted_min + (j * adjusted_depth_per_bin) for j in range(0, bin_count)]
logger.info('adjusted_bin_depths[0]: %s ... [-1]: %s', adjusted_bin_depths[0], adjusted_bin_depths[-1])
def InterpSingle(unadjusted_depth_amplitudes, unadjusted_min, unadjusted_max, time):
if (time % 1000) == 0:
total = len(channel.time)
perc = 100.0 * time / total
logger.info('%s : %s of %s', perc, time, total)
unadjusted_depth_per_bin = (unadjusted_max - unadjusted_min) / bin_count
min_index = (adjusted_min - unadjusted_min) / unadjusted_depth_per_bin
max_index = ((adjusted_min + ((bin_count - 1) * adjusted_depth_per_bin)) - unadjusted_min) / unadjusted_depth_per_bin
index_mapping = numpy.linspace(min_index, max_index, bin_count)
adjusted_depth_amplitudes = numpy.interp(index_mapping, range(0, len(unadjusted_depth_amplitudes)), unadjusted_depth_amplitudes, left=0, right=0)
return adjusted_depth_amplitudes
def Interp(*args, **kwargs):
logger.info('args: %s, kwargs: %s', args, kwargs)
data = args[0]
upper_limit = args[1]
lower_limit = args[2]
time = args[3]
#logger.info('data: %s len(data[0]): %s data.shape: %s', data, len(data[0]), data.shape)
adjusted = []
for i in range(0, len(upper_limit)):
d = data[i]
u = upper_limit[i]
l = lower_limit[i]
t = time[i]
result = InterpSingle(d, u, l, t)
adjusted.append(result)
#logger.info('adjusted: %s', adjusted)
return adjusted
channel = channel.chunk({'time':1000}) # Comment this line out to disable dask
#logger.info('Channel: %s', channel)
#logger.info('shape of data: %s len(data[0]): %s', channel.data.shape, len(channel.data[0]))
m2_lazy = xarray.apply_ufunc(
Interp,
channel.data,
channel.upper_limit,
channel.lower_limit,
channel.time,
input_core_dims=[['depth_bin'], [], [], []],
output_core_dims=[['depth']],
dask='parallelized', # Comment this line out to disable dask
output_dtypes=[numpy.dtype(numpy.int32)], # Comment this line out to disable dask
output_sizes={'depth':len(adjusted_bin_depths)}, # Comment this line out to disable dask
)
m2 = m2_lazy.compute() # Comment this line out to disable dask
m2 = m2.assign_coords({'depth':adjusted_bin_depths})
logger.info('Closing dask client')
client.close()
logger.info('Exit process')
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",,561921094
https://github.com/pydata/xarray/issues/3762#issuecomment-583775471,https://api.github.com/repos/pydata/xarray/issues/3762,583775471,MDEyOklzc3VlQ29tbWVudDU4Mzc3NTQ3MQ==,6491058,2020-02-08T20:47:23Z,2020-02-08T20:47:23Z,NONE,"> channel = channel.chunk({""time"": 1, ""depth_bin"":-1})
Thanks for the really quick response. I tried chunking and that is when the performance gets worse (from 5msec per interp to 70msec). I just added the exact chunking you suggested and tested again as well. It is slower but it also only seems to be using 10% of one CPU.
I will have to wait until tomorrow to have a try of the ufunc method. It sounds like it might be a workaround so thanks for that suggestion. I still think there is a bug in this case (either in my code or in xarray).","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",,561921094
https://github.com/pydata/xarray/issues/3762#issuecomment-583682751,https://api.github.com/repos/pydata/xarray/issues/3762,583682751,MDEyOklzc3VlQ29tbWVudDU4MzY4Mjc1MQ==,2448579,2020-02-08T01:22:12Z,2020-02-08T01:22:12Z,MEMBER,Actually it looks like this example is relevant: https://xarray.pydata.org/en/stable/examples/apply_ufunc_vectorize_1d.html,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",,561921094
https://github.com/pydata/xarray/issues/3762#issuecomment-583659669,https://api.github.com/repos/pydata/xarray/issues/3762,583659669,MDEyOklzc3VlQ29tbWVudDU4MzY1OTY2OQ==,2448579,2020-02-07T23:28:41Z,2020-02-07T23:28:41Z,MEMBER,"`sonar_data` is a numpy array; it needs to be a dask array for things to be computed lazily.
Try adding `channel = channel.chunk({""time"": 1, ""depth_bin"":-1})`. It should then ""just work"" but I haven;t looked too closely. You'll need to call `normalized_depth_data.load()` to actually get concrete values.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",,561921094