Skip to content

Error raise when displaying the IEX Stocks #6468

Open
@maximlt

Description

@maximlt

(Title to update when the root cause is identified...)

When attempting to modernize the IEX Trading example on examples.holoviz.org (holoviz-topics/examples#398) we encountered the error I report below. Sorry for the long code to reproduce the bug...

import datetime
import pandas as pd
import holoviews as hv
import datashader as ds
from holoviews.operation.datashader import spikes_aggregate

hv.config.image_rtol = 10e-3 # Suppresses datetime issue at high zoom level
hv.extension('bokeh')

df = pd.read_csv('/Users/mliquet/dev/examples/iex_trading/data/IEX_2019-10-21.csv')
# df = pd.read_csv('https://s3.amazonaws.com/datashader-data/IEX_2019-10-21.csv')
df['timestamp'] = df['timestamp'].astype('datetime64[ns]')
df['timestamp'] -= datetime.timedelta(hours=4)

symbol_volumes = df.groupby('symbol')['size'].sum()
top_symbol_volumes = symbol_volumes.sort_values()[-10:]
top_symbols = ', '.join(list(top_symbol_volumes.index))
top_volume_percent = top_symbol_volumes.sum() / symbol_volumes.sum() * 100

symbol_info = {
    "PInterest":'PINS',
    'Chesapeake Energy Corporation': 'CHK',
    "Snap Inc": 'SNAP',
    "NIO Inc": 'NIO',
    "McDermott International": 'MDR',
    "Teva Pharmaceutical Industries": 'TEVA',
    "Hewlett Packard Enterprise":'HPE',
    "Bank of America": 'BAC',
    "GE": 'General Electric',
    "Infosys":'INFY',
}

spikes = hv.Spikes(df, ['timestamp'], ['symbol', 'size', 'price'])

raster_opts = hv.opts.Image(
    min_height=400, responsive=True, colorbar=True,
    cmap='blues', xrotation=90, default_tools=['xwheel_zoom', 'xpan', 'xbox_zoom']
)

range_stream = hv.streams.RangeX()

def xrange_filter(spikes, x_range):
    low, high = (None, None) if x_range is None else x_range
    ranged = spikes[pd.to_datetime(low):pd.to_datetime(high)]
    total_displayed = len(ranged)
    if total_displayed >= 200:
        ranged = ranged.iloc[:0]
    return ranged.opts(spike_length=1, alpha=0)

def visualize_symbol(symbol, offset):
    selection = spikes.select(symbol=symbol)
    range_stream.source = selection
    rasterized = spikes_aggregate(selection, streams=[range_stream],
                                  offset=offset, expand=False,
                                  aggregator=ds.sum('size'),
                                  spike_length=1).opts(raster_opts)
    filtered = selection.apply(xrange_filter, streams=[range_stream])
    return rasterized * filtered.opts(tools=['hover'], position=offset)


overlay = visualize_symbol('PINS', 0) * visualize_symbol('CHK', 1)

hv.render(overlay)

print(range_stream.x_range)

Output (displayed, but with the traceback too...):
Image

Traceback:

WARNING:param.dynamic_operation: Callable raised "UFuncTypeError(<ufunc 'less_equal'>, (<class 'numpy.dtypes.DateTime64DType'>, <class 'numpy.dtypes._PyFloatDType'>, None))".
Invoked as dynamic_operation(x_range=(np.float64(nan), np.float64(nan)))
WARNING:param.dynamic_operation: Callable raised "UFuncTypeError(<ufunc 'less_equal'>, (<class 'numpy.dtypes.DateTime64DType'>, <class 'numpy.dtypes._PyFloatDType'>, None))".
Invoked as dynamic_operation(x_range=(np.float64(nan), np.float64(nan)))
numpy.exceptions.DTypePromotionError: The DType <class 'numpy.dtypes.DateTime64DType'> could not be promoted by <class 'numpy.dtypes._PyFloatDType'>. This means that no common DType exists for the given inputs. For example they cannot be stored in a single array unless the dtype is `object`. The full list of DTypes is: (<class 'numpy.dtypes.DateTime64DType'>, <class 'numpy.dtypes._PyFloatDType'>)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/mliquet/dev/holoviews/holoviews/plotting/util.py", line 300, in get_plot_frame
    return map_obj[key]
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 1216, in __getitem__
    val = self._execute_callback(*tuple_key)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 983, in _execute_callback
    retval = self.callback(*args, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 552, in __call__
    return self.callable()
  File "/Users/mliquet/dev/holoviews/holoviews/util/__init__.py", line 1052, in dynamic_operation
    key, obj = resolve(key, kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/util/__init__.py", line 1041, in resolve
    return key, map_obj[key]
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 1216, in __getitem__
    val = self._execute_callback(*tuple_key)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 983, in _execute_callback
    retval = self.callback(*args, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 552, in __call__
    return self.callable()
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 197, in dynamic_mul
    self_el = self.select(HoloMap, **key_map) if self.kdims else self[()]
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 1216, in __getitem__
    val = self._execute_callback(*tuple_key)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 983, in _execute_callback
    retval = self.callback(*args, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 552, in __call__
    return self.callable()
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 197, in dynamic_mul
    self_el = self.select(HoloMap, **key_map) if self.kdims else self[()]
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 1216, in __getitem__
    val = self._execute_callback(*tuple_key)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 983, in _execute_callback
    retval = self.callback(*args, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 581, in __call__
    ret = self.callable(*args, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/util/__init__.py", line 1052, in dynamic_operation
    key, obj = resolve(key, kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/util/__init__.py", line 1041, in resolve
    return key, map_obj[key]
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 1216, in __getitem__
    val = self._execute_callback(*tuple_key)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 983, in _execute_callback
    retval = self.callback(*args, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/core/spaces.py", line 581, in __call__
    ret = self.callable(*args, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/util/__init__.py", line 1053, in dynamic_operation
    return apply(obj, *key, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/util/__init__.py", line 1045, in apply
    processed = self._process(element, key, kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/util/__init__.py", line 1027, in _process
    return self.p.operation.process_element(element, key, **kwargs)
  File "/Users/mliquet/dev/holoviews/holoviews/core/operation.py", line 192, in process_element
    return self._apply(element, key)
  File "/Users/mliquet/dev/holoviews/holoviews/core/operation.py", line 141, in _apply
    ret = self._process(element, key)
  File "/Users/mliquet/dev/holoviews/holoviews/operation/datashader.py", line 694, in _process
    info = self._get_sampling(element, x, y, ndim=1, default=default)
  File "/Users/mliquet/dev/holoviews/holoviews/operation/resample.py", line 142, in _get_sampling
    x_range = (np.nanmin([np.nanmax([x0, ex0]), ex1]),
  File "/Users/mliquet/dev/holoviews/.pixi/envs/test-39/lib/python3.9/site-packages/numpy/lib/_nanfunctions_impl.py", line 500, in nanmax
    res = np.amax(a, axis=axis, out=out, **kwargs)
  File "/Users/mliquet/dev/holoviews/.pixi/envs/test-39/lib/python3.9/site-packages/numpy/_core/fromnumeric.py", line 2916, in amax
    return _wrapreduction(a, np.maximum, 'max', axis, None, out,
  File "/Users/mliquet/dev/holoviews/.pixi/envs/test-39/lib/python3.9/site-packages/numpy/_core/fromnumeric.py", line 86, in _wrapreduction
    return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
numpy._core._exceptions._UFuncNoLoopError: ufunc 'less_equal' did not contain a loop with signature matching types (<class 'numpy.dtypes.DateTime64DType'>, <class 'numpy.dtypes._PyFloatDType'>) -> None

I managed to avoid when making this change, as self.p.x_range is equal to (np.nan, np.nan) as this stage and causes the error.

diff --git a/holoviews/operation/resample.py b/holoviews/operation/resample.py
index bb482a79c..b858ae49e 100644
--- a/holoviews/operation/resample.py
+++ b/holoviews/operation/resample.py
@@ -130,7 +130,7 @@ class ResampleOperation2D(ResampleOperation1D):
         else:
             if x is None:
                 x_range = self.p.x_range or (-0.5, 0.5)
-            elif self.p.expand or not self.p.x_range:
+            elif self.p.expand or (self.p.x_range is None or all(not isfinite(v) for v in self.p.x_range)):
                 if self.p.x_range and all(isfinite(v) for v in self.p.x_range):
                     x_range = self.p.x_range
                 else:

I was wondering why this changed. When looking into it, I saw that spikes_aggregate._process is called 4 times, while I'd expect it to be only called 2 times. Running the same snippet in an older environment (python 3.6, holoviews 1.13.0), I see it is only called 2 times. I also noticed that, after rendering, range_stream.x_range is (np.nan, np.nan) in the newer code (with the change applied), while it was None before. Printing in Stream.update and in spikes_aggregate._process, I can see the stream's x_range is updated a bunch of times, from GenericElementPlot.get_extents() with (np.nan, np.nan) after spikes_aggregate._process is called the first 2 times. Things were much different in the old code, with the first four lines only being printed.

Stream update 5179861584 {'x_range': None}
Stream update 5179861584 {'x_range': None}
SKIPES AGGREGATE
SKIPES AGGREGATE
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (numpy.datetime64('2019-10-21T09:30:09.432334336'), numpy.datetime64('2019-10-21T15:59:58.745244160'))}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (numpy.datetime64('2019-10-21T09:30:09.432334336'), numpy.datetime64('2019-10-21T15:59:58.745244160'))}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (numpy.datetime64('2019-10-21T09:30:09.432334336'), numpy.datetime64('2019-10-21T15:59:58.745244160'))}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {'x_range': (nan, nan)}
Stream update 5179861584 {}
SKIPES AGGREGATE
SKIPES AGGREGATE

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: bugSomething isn't correct or isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions