holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.66k stars 396 forks source link

Support hv.Dimension for wide dataframes #6276

Closed droumis closed 3 weeks ago

droumis commented 3 weeks ago

Support hv.Dimension as vdim input for wide dataframe columns. For instance:

import pandas as pd
import holoviews as hv; hv.extension('bokeh')

price_dim = hv.Dimension("Price", unit="$")

df = pd.read_csv('https://datasets.holoviz.org/stocks/v1/stocks.csv', parse_dates=['Date']).set_index('Date')

hv.NdOverlay({col: hv.Curve(df, 'Date', (col, price_dim)).opts(tools=['hover'], subcoordinate_y=True) for col in df.columns}, 'Ticker')
Traceback ```python --------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[17], line 8 4 price_dim = hv.Dimension("Price", unit="$") # match the index name in the df 6 df = pd.read_csv('https://datasets.holoviz.org/stocks/v1/stocks.csv', parse_dates=['Date']).set_index('Date') ----> 8 hv.NdOverlay({col: hv.Curve(df, 'Date', (col, price_dim)).opts(tools=['hover'], subcoordinate_y=True) for col in df.columns}, 'Ticker') File ~/src/holoviews/holoviews/element/selection.py:25, in SelectionIndexExpr.__init__(self, *args, **kwargs) 24 def __init__(self, *args, **kwargs): ---> 25 super().__init__(*args, **kwargs) 26 self._index_skip = False File ~/src/holoviews/holoviews/element/chart.py:51, in Chart.__init__(self, data, kdims, vdims, **params) 50 def __init__(self, data, kdims=None, vdims=None, **params): ---> 51 params.update(process_dimensions(kdims, vdims)) 52 if len(params.get('kdims', [])) == self._max_kdim_count + 1: 53 self.param.warning('Chart elements should only be supplied a single kdim') File ~/src/holoviews/holoviews/core/dimension.py:117, in process_dimensions(kdims, vdims) 110 elif not isinstance(dims, list): 111 raise ValueError( 112 f"{group} argument expects a Dimension or list of dimensions, " 113 "specified as tuples, strings, dictionaries or Dimension " 114 f"instances, not a {type(dims).__name__} type. " 115 "Ensure you passed the data as the first argument." 116 ) --> 117 dimensions[group] = [asdim(d) for d in dims] 118 return dimensions File ~/src/holoviews/holoviews/core/dimension.py:60, in asdim(dimension) 50 def asdim(dimension): 51 """Convert the input to a Dimension. 52 53 Args: (...) 58 copy is performed if the input is already a Dimension. 59 """ ---> 60 return dimension if isinstance(dimension, Dimension) else Dimension(dimension) File ~/src/holoviews/holoviews/core/dimension.py:287, in Dimension.__init__(self, spec, **params) 284 values = [] 286 all_params['values'] = list(util.unique_array(values)) --> 287 super().__init__(**all_params) 288 if self.default is not None: 289 if self.values and self.default not in values: File ~/opt/miniconda3/envs/neuro-multi-chan/lib/python3.12/site-packages/param/parameterized.py:4187, in Parameterized.__init__(self, **params) 4185 if self.param.name.default == self.__class__.__name__: 4186 self.param._generate_name() -> 4187 refs, deps = self.param._setup_params(**params) 4188 object_count += 1 4190 self._param__private.initialized = True File ~/opt/miniconda3/envs/neuro-multi-chan/lib/python3.12/site-packages/param/parameterized.py:1685, in as_uninitialized..override_initialization(self_, *args, **kw) 1683 original_initialized = parameterized_instance._param__private.initialized 1684 parameterized_instance._param__private.initialized = False -> 1685 ret = fn(self_, *args, **kw) 1686 parameterized_instance._param__private.initialized = original_initialized 1687 return ret File ~/opt/miniconda3/envs/neuro-multi-chan/lib/python3.12/site-packages/param/parameterized.py:1966, in Parameters._setup_params(self_, **params) 1954 if ref: 1955 warnings.warn( 1956 f"Parameter {name!r} on {pobj.owner} is being given a valid parameter " 1957 f"reference {val} but is implicitly allow_refs=False. " (...) 1964 stacklevel=4, 1965 ) -> 1966 setattr(self, name, val) 1967 continue 1969 # Resolve references File ~/opt/miniconda3/envs/neuro-multi-chan/lib/python3.12/site-packages/param/parameterized.py:530, in instance_descriptor.._f(self, obj, val) 528 instance_param.__set__(obj, val) 529 return --> 530 return f(self, obj, val) File ~/opt/miniconda3/envs/neuro-multi-chan/lib/python3.12/site-packages/param/parameterized.py:1497, in Parameter.__set__(self, obj, val) 1489 if self.set_hook is not _identity_hook: 1490 # PARAM3_DEPRECATION 1491 warnings.warn( 1492 'Number.set_hook has been deprecated.', 1493 category=_ParamDeprecationWarning, 1494 stacklevel=6, 1495 ) -> 1497 self._validate(val) 1499 _old = NotImplemented 1500 # obj can be None if __set__ is called for a Parameterized class File ~/opt/miniconda3/envs/neuro-multi-chan/lib/python3.12/site-packages/param/parameterized.py:1647, in String._validate(self, val) 1646 def _validate(self, val): -> 1647 self._validate_value(val, self.allow_None) 1648 self._validate_regex(val, self.regex) File ~/opt/miniconda3/envs/neuro-multi-chan/lib/python3.12/site-packages/param/parameterized.py:1641, in String._validate_value(self, val, allow_None) 1639 return 1640 if not isinstance(val, str): -> 1641 raise ValueError( 1642 f'{_validate_error_prefix(self)} only takes a string value, ' 1643 f'not value of {type(val)}.' 1644 ) ValueError: String parameter 'Dimension.label' only takes a string value, not value of . ```
hoxbro commented 3 weeks ago

I think this is what you want: hv.NdOverlay({col: hv.Curve(df, 'Date', hv.Dimension(col, label="Price", unit="$")).opts(tools=['hover'], subcoordinate_y=True) for col in df.columns}, 'Ticker').

Note that a tuple and a list do not do the same. E.g. a tuple is converted to a hv.Dimension behind the scene, and a list contains a list of Dimensions (either as string or Dimensions).

droumis commented 3 weeks ago

Nice thanks! that works