Source code for panel.pane.vizzu

from __future__ import annotations

import datetime as dt
import sys

from typing import (
    TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Optional,
)

import numpy as np
import param

from bokeh.models import ColumnDataSource
from pyviz_comms import JupyterComm

from ..reactive import SyncableData
from ..util import isdatetime, lazy_load
from .base import ModelPane

if TYPE_CHECKING:
    from bokeh.document import Document
    from bokeh.model import Model
    from pyviz_comms import Comm


[docs]class Vizzu(ModelPane, SyncableData): """ The `Vizzu` pane provides an interactive visualization component for large, real-time datasets built on the Vizzu project. Reference: https://panel.holoviz.org/reference/panes/Vizzu.html :Example: >>> Vizzu(df) """ animation = param.Dict(default={}, nested_refs=True, doc=""" Animation settings (see https://lib.vizzuhq.com/latest/reference/modules/Anim/).""") config = param.Dict(default={}, nested_refs=True, doc=""" The config contains all of the parameters needed to render a particular static chart or a state of an animated chart (see https://lib.vizzuhq.com/latest/reference/interfaces/Config.Chart/).""") click = param.Parameter(doc=""" Data associated with the latest click event.""") column_types = param.Dict(default={}, nested_refs=True, doc=""" Optional column definitions. If not defined will be inferred from the data.""") duration = param.Integer(default=500, doc=""" The config contains all of the parameters needed to render a particular static chart or a state of an animated chart.""") style = param.Dict(default={}, nested_refs=True, doc=""" Style configuration of the chart.""") tooltip = param.Boolean(default=False, doc=""" Whether to enable tooltips on the chart.""") _data_params: ClassVar[List[str]] = ['object'] _rename: ClassVar[Dict[str, str | None]] = { 'click': None, 'column_types': None, 'object': None } _rerender_params: ClassVar[List[str]] = [] _updates: ClassVar[bool] = True def __init__(self, object=None, **params): click_handler = params.pop('on_click', None) super().__init__(object, **params) self._event_handlers = [] if click_handler: self.on_click(click_handler)
[docs] @classmethod def applies(cls, object): if isinstance(object, dict) and all(isinstance(v, (list, np.ndarray)) for v in object.values()): return 0 if object else None elif 'pandas' in sys.modules: import pandas as pd if isinstance(object, pd.DataFrame): return 0 return False
def _get_data(self): if self.object is None: return {}, {} if isinstance(self.object, dict): cols = data = dict(self.object) else: data = self.object cols = ColumnDataSource.from_df(self.object) return data, {str(k): v for k, v in cols.items()} def _get_columns(self): import pandas as pd columns = [] for col, array in self._data.items(): if col in self.column_types: columns.append({'name': col, 'type': self.column_types[col]}) continue if not isinstance(array, np.ndarray): array = np.asarray(array) kind = array.dtype.kind if kind == 'M': columns.append({'name': col, 'type': 'datetime'}) elif kind in 'uif': columns.append({'name': col, 'type': 'measure'}) elif kind in 'bsU': columns.append({'name': col, 'type': 'dimension'}) else: if len(array): value = array[0] if isinstance(value, dt.date): columns.append({'name': col, 'type': 'datetime'}) elif isdatetime(value) or isinstance(value, pd.Period): columns.append({'name': col, 'type': 'datetime'}) elif isinstance(value, str): columns.append({'name': col, 'type': 'dimension'}) elif isinstance(value, (float, np.float64, np.int_, int)): columns.append({'name': col, 'type': 'measure'}) else: columns.append({'name': col, 'type': 'dimension'}) else: columns.append({'name': col, 'type': 'dimension'}) return columns def _get_properties(self, doc, source=None): props = super()._get_properties(doc) props['duration'] = self.duration if source is None: props['source'] = ColumnDataSource(data=self._data) else: source.data = self._data props['source'] = source return props def _process_param_change(self, params): if 'object' in params: self._processed, self._data = self._get_data() if 'object' in params or 'column_types' in params: params['columns'] = self._get_columns() return super()._process_param_change(params) def _get_model( self, doc: Document, root: Optional[Model] = None, parent: Optional[Model] = None, comm: Optional[Comm] = None ) -> Model: self._bokeh_model = lazy_load( 'panel.models.vizzu', 'VizzuChart', isinstance(comm, JupyterComm), root ) model = super()._get_model(doc, root, parent, comm) self._register_events('vizzu_event', model=model, doc=doc, comm=comm) return model def _process_event(self, event): self.click = event.data for handler in self._event_handlers: handler(event.data) def _update(self, ref: str, model: Model) -> None: pass
[docs] def animate( self, anim: Dict[str, Any], options: int | Dict[str, Any] | None = None ) -> None: """ Updates the chart with a new configuration. """ if not any(key in anim for key in ('config', 'data', 'style')): anim = {'config': anim} updates = {} for p, value in anim.items(): if p not in self.param: raise ValueError( f'Could not update {p!r}. You must pass either a dictionary ' 'containing config, data and/or style values OR a single ' 'config dictionary. ' ) updates[p] = dict(getattr(self, p), **value) if isinstance(options, int): self.duration = options elif isinstance(options, dict): self.animation = options self.param.update(updates)
# Public API
[docs] def on_click(self, callback: Callable[[Dict], None]): """ Register a callback to be executed when any element in the chart is clicked on. Arguments --------- callback: (callable) The callback to run on click events. """ self._event_handlers.append(callback)