holoviz / datashader

Quickly and accurately render even the largest data.
http://datashader.org
BSD 3-Clause "New" or "Revised" License
3.3k stars 365 forks source link

shade(..., how="eq_hist") with no valid data raises index out of bounds error #1166

Closed ianthomas23 closed 1 year ago

ianthomas23 commented 1 year ago

First reported in holoviews as holoviz/holoviews#5595.

A simpler datashader-only reproducer is:

import datashader.transfer_functions as tf
import numpy as np
import xarray as xr

agg = xr.DataArray(
    data=np.zeros((2, 2, 2), dtype=np.uint32),
    coords=dict(y=[0, 1], x=[0, 1], cat=['a', 'b']),
)
im = tf.shade(agg, how='eq_hist')
print(im)

which produces:

Traceback (most recent call last):
  File "/Users/iant/github_temp/datashader_temp/empty_cdf.py", line 9, in <module>
    im = tf.shade(agg, how='eq_hist')
  File "/Users/iant/github/datashader/datashader/transfer_functions/__init__.py", line 721, in shade
    return _colorize(agg, color_key, how, alpha, span, min_alpha, name, color_baseline, rescale_discrete_levels)
  File "/Users/iant/github/datashader/datashader/transfer_functions/__init__.py", line 436, in _colorize
    a = _interpolate_alpha(data, total, mask, how, alpha, span, min_alpha, rescale_discrete_levels)
  File "/Users/iant/github/datashader/datashader/transfer_functions/__init__.py", line 474, in _interpolate_alpha
    a_scaled = _normalize_interpolate_how(how)(total - offset, mask)
  File "/Users/iant/github/datashader/datashader/transfer_functions/__init__.py", line 220, in eq_hist
    cdf = cdf / float(cdf[-1])
IndexError: index -1 is out of bounds for axis 0 with size 0

The problem is caused by passing an xr.DataArray that contains all values that will be masked out (i.e. zeros or nans depending on context) to transfer_functions.image(..., how="eq_hist"). This calls the eq_hist() function that is unable to determine a cumulative distribution function as there is no valid data.

The fix is to detect if all the supplied data to eq_hist() is masked out and return to the caller early.