Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error raise when displaying the IEX Stocks #6468

Open
maximlt opened this issue Dec 8, 2024 · 0 comments
Open

Error raise when displaying the IEX Stocks #6468

maximlt opened this issue Dec 8, 2024 · 0 comments
Labels
type: bug Something isn't correct or isn't working

Comments

@maximlt
Copy link
Member

maximlt commented Dec 8, 2024

(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
@maximlt maximlt added the type: bug Something isn't correct or isn't working label Dec 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't correct or isn't working
Projects
None yet
Development

No branches or pull requests

1 participant