def map_array(self, a: tp.ArrayLike, idx_field: tp.Optional[str] = None, value_map: tp.Optional[tp.ValueMapLike] = None, group_by: tp.GroupByLike = None, **kwargs) -> MappedArray: """Convert array to mapped array. The length of the array should match that of the records.""" if not isinstance(a, np.ndarray): a = np.asarray(a) checks.assert_shape_equal(a, self.values) if idx_field is None: idx_field = self.idx_field if idx_field is not None: idx_arr = self.values[idx_field] else: idx_arr = None return MappedArray(self.wrapper, a, self.values['col'], id_arr=self.values['id'], idx_arr=idx_arr, value_map=value_map, **kwargs).regroup(group_by)
def map_array(self, a, idx_arr=None, group_by=None, **kwargs): """Convert array to `MappedArray`. The length of the array should match that of the records.""" if not isinstance(a, np.ndarray): a = np.asarray(a) checks.assert_shape_equal(a, self.records_arr) if idx_arr is None: if self.idx_field is not None: idx_arr = self.records_arr[self.idx_field] else: idx_arr = None if self.wrapper.grouper.is_grouping_changed(group_by=group_by): self.wrapper.grouper.check_group_by(group_by=group_by) wrapper = self.wrapper.copy(group_by=group_by) else: wrapper = self.wrapper return MappedArray( wrapper, a, self.records_arr['col'], idx_arr=idx_arr, **kwargs )
def __init__(self, wrapper, mapped_arr, col_arr, idx_arr=None): Configured.__init__( self, wrapper=wrapper, mapped_arr=mapped_arr, col_arr=col_arr, idx_arr=idx_arr ) checks.assert_type(wrapper, ArrayWrapper) if not isinstance(mapped_arr, np.ndarray): mapped_arr = np.asarray(mapped_arr) if not isinstance(col_arr, np.ndarray): col_arr = np.asarray(col_arr) checks.assert_shape_equal(mapped_arr, col_arr, axis=0) if idx_arr is not None: if not isinstance(idx_arr, np.ndarray): idx_arr = np.asarray(idx_arr) checks.assert_shape_equal(mapped_arr, idx_arr, axis=0) self._wrapper = wrapper self._mapped_arr = mapped_arr self._col_arr = col_arr self._idx_arr = idx_arr PandasIndexer.__init__(self, _mapped_array_indexing_func)
def __init__(self, wrapper: ArrayWrapper, mapped_arr: tp.ArrayLike, col_arr: tp.ArrayLike, id_arr: tp.Optional[tp.ArrayLike] = None, idx_arr: tp.Optional[tp.ArrayLike] = None, mapping: tp.Optional[tp.MappingLike] = None, col_mapper: tp.Optional[ColumnMapper] = None, **kwargs) -> None: Wrapping.__init__(self, wrapper, mapped_arr=mapped_arr, col_arr=col_arr, id_arr=id_arr, idx_arr=idx_arr, mapping=mapping, col_mapper=col_mapper, **kwargs) StatsBuilderMixin.__init__(self) mapped_arr = np.asarray(mapped_arr) col_arr = np.asarray(col_arr) checks.assert_shape_equal(mapped_arr, col_arr, axis=0) if id_arr is None: id_arr = np.arange(len(mapped_arr)) else: id_arr = np.asarray(id_arr) if idx_arr is not None: idx_arr = np.asarray(idx_arr) checks.assert_shape_equal(mapped_arr, idx_arr, axis=0) if mapping is not None: if isinstance(mapping, str): if mapping.lower() == 'index': mapping = self.wrapper.index elif mapping.lower() == 'columns': mapping = self.wrapper.columns mapping = to_mapping(mapping) self._mapped_arr = mapped_arr self._id_arr = id_arr self._col_arr = col_arr self._idx_arr = idx_arr self._mapping = mapping if col_mapper is None: col_mapper = ColumnMapper(wrapper, col_arr) self._col_mapper = col_mapper
def wrap(self, a: tp.ArrayLike, index: tp.Optional[tp.IndexLike] = None, columns: tp.Optional[tp.IndexLike] = None, dtype: tp.Optional[tp.PandasDTypeLike] = None, group_by: tp.GroupByLike = None) -> tp.SeriesFrame: """Wrap a NumPy array using the stored metadata.""" checks.assert_ndim(a, (1, 2)) _self = self.resolve(group_by=group_by) if index is None: index = _self.index if not isinstance(index, pd.Index): index = pd.Index(index) if columns is None: columns = _self.columns if not isinstance(columns, pd.Index): columns = pd.Index(columns) if len(columns) == 1: name = columns[0] if name == 0: # was a Series before name = None else: name = None arr = np.asarray(a) arr = reshape_fns.soft_to_ndim(arr, self.ndim) checks.assert_shape_equal(arr, index, axis=(0, 0)) if arr.ndim == 2: checks.assert_shape_equal(arr, columns, axis=(1, 0)) if arr.ndim == 1: return pd.Series(arr, index=index, name=name, dtype=dtype) if arr.ndim == 2: if arr.shape[1] == 1 and _self.ndim == 1: return pd.Series(arr[:, 0], index=index, name=name, dtype=dtype) return pd.DataFrame(arr, index=index, columns=columns, dtype=dtype) raise ValueError(f"{arr.ndim}-d input is not supported")
def _wrap(arr): arr = np.asarray(arr) checks.assert_ndim(arr, (1, 2)) if fillna is not None: arr[pd.isnull(arr)] = fillna arr = reshape_fns.soft_to_ndim(arr, self.ndim) checks.assert_shape_equal(arr, index, axis=(0, 0)) if arr.ndim == 2: checks.assert_shape_equal(arr, columns, axis=(1, 0)) if arr.ndim == 1: return pd.Series(arr, index=index, name=name, dtype=dtype) if arr.ndim == 2: if arr.shape[1] == 1 and _self.ndim == 1: return pd.Series(arr[:, 0], index=index, name=name, dtype=dtype) return pd.DataFrame(arr, index=index, columns=columns, dtype=dtype) raise ValueError(f"{arr.ndim}-d input is not supported")
def map_array(self, a: tp.ArrayLike, idx_arr: tp.Optional[tp.ArrayLike] = None, mapping: tp.Optional[tp.MappingLike] = None, group_by: tp.GroupByLike = None, **kwargs) -> MappedArray: """Convert array to mapped array. The length of the array should match that of the records.""" if not isinstance(a, np.ndarray): a = np.asarray(a) checks.assert_shape_equal(a, self.values) if idx_arr is None: idx_arr = self.idx_arr return MappedArray(self.wrapper, a, self.col_arr, id_arr=self.id_arr, idx_arr=idx_arr, mapping=mapping, col_mapper=self.col_mapper, **kwargs).regroup(group_by)
def wrap(self, a, index=None, columns=None, dtype=None, collapse=None, **kwargs): """Wrap a NumPy array using the stored metadata.""" checks.assert_ndim(a, (1, 2)) group_by = self.grouper.resolve_group_by(**kwargs) a = np.asarray(a) a = reshape_fns.soft_to_ndim(a, self.ndim) if index is None: index = self.index if columns is None: columns = self.grouper.get_columns(**kwargs) if collapse is None: collapse = group_by is not None and group_by is not False and self.grouped_ndim == 1 if columns is not None and len(columns) == 1: name = columns[0] if name == 0: # was a Series before name = None else: name = None # Perform checks if index is not None: checks.assert_shape_equal(a, index, axis=(0, 0)) if a.ndim == 2 and columns is not None: checks.assert_shape_equal(a, columns, axis=(1, 0)) if a.ndim == 1: return pd.Series(a, index=index, name=name, dtype=dtype) if a.ndim == 2 and a.shape[1] == 1 and collapse: return pd.Series(a[:, 0], index=index, name=name, dtype=dtype) return pd.DataFrame(a, index=index, columns=columns, dtype=dtype)
def __init__(self, data: tp.Optional[tp.ArrayLike] = None, x_labels: tp.Optional[tp.Labels] = None, y_labels: tp.Optional[tp.Labels] = None, z_labels: tp.Optional[tp.Labels] = None, trace_kwargs: tp.KwargsLike = None, add_trace_kwargs: tp.KwargsLike = None, scene_name: str = 'scene', fig: tp.Optional[tp.BaseFigure] = None, **layout_kwargs) -> None: """Create a volume plot. Args: data (array_like): Data in any format that can be converted to NumPy. Must be a 3-dim array. x_labels (array_like): X-axis labels. y_labels (array_like): Y-axis labels. z_labels (array_like): Z-axis labels. trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Volume`. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. scene_name (str): Reference to the 3D scene. fig (Figure or FigureWidget): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. !!! note Figure widgets have currently problems displaying NaNs. Use `.show()` method for rendering. ## Example ```python-repl >>> import vectorbt as vbt >>> import numpy as np >>> volume = vbt.plotting.Volume( ... data=np.random.randint(1, 10, size=(3, 3, 3)), ... x_labels=['a', 'b', 'c'], ... y_labels=['d', 'e', 'f'], ... z_labels=['g', 'h', 'i'] ... ) >>> volume.fig ``` ![](/docs/img/Volume.svg) """ Configured.__init__( self, data=data, x_labels=x_labels, y_labels=y_labels, z_labels=z_labels, trace_kwargs=trace_kwargs, add_trace_kwargs=add_trace_kwargs, scene_name=scene_name, fig=fig, **layout_kwargs ) from vectorbt._settings import settings layout_cfg = settings['plotting']['layout'] if trace_kwargs is None: trace_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} if data is not None: checks.assert_ndim(data, 3) data = np.asarray(data) x_len, y_len, z_len = data.shape if x_labels is not None: checks.assert_shape_equal(data, x_labels, (0, 0)) if y_labels is not None: checks.assert_shape_equal(data, y_labels, (1, 0)) if z_labels is not None: checks.assert_shape_equal(data, z_labels, (2, 0)) else: if x_labels is None or y_labels is None or z_labels is None: raise ValueError("At least data, or x_labels, y_labels and z_labels must be passed") x_len = len(x_labels) y_len = len(y_labels) z_len = len(z_labels) if x_labels is None: x_labels = np.arange(x_len) else: x_labels = clean_labels(x_labels) if y_labels is None: y_labels = np.arange(y_len) else: y_labels = clean_labels(y_labels) if z_labels is None: z_labels = np.arange(z_len) else: z_labels = clean_labels(z_labels) x_labels = np.asarray(x_labels) y_labels = np.asarray(y_labels) z_labels = np.asarray(z_labels) if fig is None: fig = make_figure() if 'width' in layout_cfg: # Calculate nice width and height fig.update_layout( width=layout_cfg['width'], height=0.7 * layout_cfg['width'] ) # Non-numeric data types are not supported by go.Volume, so use ticktext # Note: Currently plotly displays the entire tick array, in future versions it will be more sensible more_layout = dict() if not np.issubdtype(x_labels.dtype, np.number): x_ticktext = x_labels x_labels = np.arange(x_len) more_layout[scene_name] = dict( xaxis=dict( ticktext=x_ticktext, tickvals=x_labels, tickmode='array' ) ) if not np.issubdtype(y_labels.dtype, np.number): y_ticktext = y_labels y_labels = np.arange(y_len) more_layout[scene_name] = dict( yaxis=dict( ticktext=y_ticktext, tickvals=y_labels, tickmode='array' ) ) if not np.issubdtype(z_labels.dtype, np.number): z_ticktext = z_labels z_labels = np.arange(z_len) more_layout[scene_name] = dict( zaxis=dict( ticktext=z_ticktext, tickvals=z_labels, tickmode='array' ) ) fig.update_layout(**more_layout) fig.update_layout(**layout_kwargs) # Arrays must have the same length as the flattened data array x = np.repeat(x_labels, len(y_labels) * len(z_labels)) y = np.tile(np.repeat(y_labels, len(z_labels)), len(x_labels)) z = np.tile(z_labels, len(x_labels) * len(y_labels)) volume = go.Volume( x=x, y=y, z=z, opacity=0.2, surface_count=15, # keep low for big data colorscale='Plasma' ) volume.update(**trace_kwargs) fig.add_trace(volume, **add_trace_kwargs) TraceUpdater.__init__(self, fig, (fig.data[-1],)) if data is not None: self.update(data)
def __init__(self, data: tp.Optional[tp.ArrayLike] = None, x_labels: tp.Optional[tp.Labels] = None, y_labels: tp.Optional[tp.Labels] = None, is_x_category: bool = False, is_y_category: bool = False, trace_kwargs: tp.KwargsLike = None, add_trace_kwargs: tp.KwargsLike = None, fig: tp.Optional[tp.BaseFigure] = None, **layout_kwargs) -> None: """Create a heatmap plot. Args: data (array_like): Data in any format that can be converted to NumPy. Must be of shape (`y_labels`, `x_labels`). x_labels (array_like): X-axis labels, corresponding to columns in pandas. y_labels (array_like): Y-axis labels, corresponding to index in pandas. is_x_category (bool): Whether X-axis is a categorical axis. is_y_category (bool): Whether Y-axis is a categorical axis. trace_kwargs (dict): Keyword arguments passed to `plotly.graph_objects.Heatmap`. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. fig (Figure or FigureWidget): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> import vectorbt as vbt >>> heatmap = vbt.plotting.Heatmap( ... data=[[1, 2], [3, 4]], ... x_labels=['a', 'b'], ... y_labels=['x', 'y'] ... ) >>> heatmap.fig ``` ![](/docs/img/Heatmap.svg) """ Configured.__init__( self, data=data, x_labels=x_labels, y_labels=y_labels, trace_kwargs=trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig, **layout_kwargs ) from vectorbt._settings import settings layout_cfg = settings['plotting']['layout'] if trace_kwargs is None: trace_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} if data is not None: data = reshape_fns.to_2d_array(data) if x_labels is not None: checks.assert_shape_equal(data, x_labels, (1, 0)) if y_labels is not None: checks.assert_shape_equal(data, y_labels, (0, 0)) else: if x_labels is None or y_labels is None: raise ValueError("At least data, or x_labels and y_labels must be passed") if x_labels is not None: x_labels = clean_labels(x_labels) if y_labels is not None: y_labels = clean_labels(y_labels) if fig is None: fig = make_figure() if 'width' in layout_cfg: # Calculate nice width and height max_width = layout_cfg['width'] if data is not None: x_len = data.shape[1] y_len = data.shape[0] else: x_len = len(x_labels) y_len = len(y_labels) width = math.ceil(renormalize( x_len / (x_len + y_len), (0, 1), (0.3 * max_width, max_width) )) width = min(width + 150, max_width) # account for colorbar height = math.ceil(renormalize( y_len / (x_len + y_len), (0, 1), (0.3 * max_width, max_width) )) height = min(height, max_width * 0.7) # limit height fig.update_layout( width=width, height=height ) heatmap = go.Heatmap( hoverongaps=False, colorscale='Plasma', x=x_labels, y=y_labels ) heatmap.update(**trace_kwargs) fig.add_trace(heatmap, **add_trace_kwargs) axis_kwargs = dict() if is_x_category: if fig.data[-1]['xaxis'] is not None: axis_kwargs['xaxis' + fig.data[-1]['xaxis'][1:]] = dict(type='category') else: axis_kwargs['xaxis'] = dict(type='category') if is_y_category: if fig.data[-1]['yaxis'] is not None: axis_kwargs['yaxis' + fig.data[-1]['yaxis'][1:]] = dict(type='category') else: axis_kwargs['yaxis'] = dict(type='category') fig.update_layout(**axis_kwargs) fig.update_layout(**layout_kwargs) TraceUpdater.__init__(self, fig, (fig.data[-1],)) if data is not None: self.update(data)
def __init__(self, data: tp.Optional[tp.ArrayLike] = None, trace_names: tp.TraceNames = None, horizontal: bool = False, remove_nan: bool = True, from_quantile: tp.Optional[float] = None, to_quantile: tp.Optional[float] = None, trace_kwargs: tp.KwargsLikeSequence = None, add_trace_kwargs: tp.KwargsLike = None, fig: tp.Optional[tp.BaseFigure] = None, **layout_kwargs) -> None: """Create a box plot. For keyword arguments, see `Histogram`. ## Example ```python-repl >>> import vectorbt as vbt >>> box = vbt.plotting.Box( ... data=[[1, 2], [3, 4], [2, 1]], ... trace_names=['a', 'b'] ... ) >>> box.fig ``` ![](/docs/img/Box.svg) """ Configured.__init__( self, data=data, trace_names=trace_names, horizontal=horizontal, remove_nan=remove_nan, from_quantile=from_quantile, to_quantile=to_quantile, trace_kwargs=trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig, **layout_kwargs ) if trace_kwargs is None: trace_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} if data is not None: data = reshape_fns.to_2d_array(data) if trace_names is not None: checks.assert_shape_equal(data, trace_names, (1, 0)) else: if trace_names is None: raise ValueError("At least data or trace_names must be passed") if trace_names is None: trace_names = [None] * data.shape[1] if isinstance(trace_names, str): trace_names = [trace_names] if fig is None: fig = make_figure() fig.update_layout(**layout_kwargs) for i, trace_name in enumerate(trace_names): _trace_kwargs = resolve_dict(trace_kwargs, i=i) trace_name = _trace_kwargs.pop('name', trace_name) if trace_name is not None: trace_name = str(trace_name) box = go.Box( name=trace_name, showlegend=trace_name is not None ) box.update(**_trace_kwargs) fig.add_trace(box, **add_trace_kwargs) TraceUpdater.__init__(self, fig, fig.data[-len(trace_names):]) self._horizontal = horizontal self._remove_nan = remove_nan self._from_quantile = from_quantile self._to_quantile = to_quantile if data is not None: self.update(data)
def __init__(self, data: tp.Optional[tp.ArrayLike] = None, trace_names: tp.TraceNames = None, horizontal: bool = False, remove_nan: bool = True, from_quantile: tp.Optional[float] = None, to_quantile: tp.Optional[float] = None, trace_kwargs: tp.KwargsLikeSequence = None, add_trace_kwargs: tp.KwargsLike = None, fig: tp.Optional[tp.BaseFigure] = None, **layout_kwargs) -> None: """Create a histogram plot. Args: data (array_like): Data in any format that can be converted to NumPy. Must be of shape (any, `trace_names`). trace_names (str or list of str): Trace names, corresponding to columns in pandas. horizontal (bool): Whether to plot horizontally. remove_nan (bool): Whether to remove NaN values. from_quantile (float): Filter out data points before this quantile. Should be in range `[0, 1]`. to_quantile (float): Filter out data points after this quantile. Should be in range `[0, 1]`. trace_kwargs (dict or list of dict): Keyword arguments passed to `plotly.graph_objects.Histogram`. Can be specified per trace as a sequence of dicts. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. fig (Figure or FigureWidget): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> import vectorbt as vbt >>> hist = vbt.plotting.Histogram( ... data=[[1, 2], [3, 4], [2, 1]], ... trace_names=['a', 'b'] ... ) >>> hist.fig ``` ![](/docs/img/Histogram.svg) """ Configured.__init__( self, data=data, trace_names=trace_names, horizontal=horizontal, remove_nan=remove_nan, from_quantile=from_quantile, to_quantile=to_quantile, trace_kwargs=trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig, **layout_kwargs ) if trace_kwargs is None: trace_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} if data is not None: data = reshape_fns.to_2d_array(data) if trace_names is not None: checks.assert_shape_equal(data, trace_names, (1, 0)) else: if trace_names is None: raise ValueError("At least data or trace_names must be passed") if trace_names is None: trace_names = [None] * data.shape[1] if isinstance(trace_names, str): trace_names = [trace_names] if fig is None: fig = make_figure() fig.update_layout(barmode='overlay') fig.update_layout(**layout_kwargs) for i, trace_name in enumerate(trace_names): _trace_kwargs = resolve_dict(trace_kwargs, i=i) trace_name = _trace_kwargs.pop('name', trace_name) if trace_name is not None: trace_name = str(trace_name) hist = go.Histogram( opacity=0.75 if len(trace_names) > 1 else 1, name=trace_name, showlegend=trace_name is not None ) hist.update(**_trace_kwargs) fig.add_trace(hist, **add_trace_kwargs) TraceUpdater.__init__(self, fig, fig.data[-len(trace_names):]) self._horizontal = horizontal self._remove_nan = remove_nan self._from_quantile = from_quantile self._to_quantile = to_quantile if data is not None: self.update(data)
def __init__(self, data: tp.Optional[tp.ArrayLike] = None, trace_names: tp.TraceNames = None, x_labels: tp.Optional[tp.Labels] = None, trace_kwargs: tp.KwargsLikeSequence = None, add_trace_kwargs: tp.KwargsLike = None, fig: tp.Optional[tp.BaseFigure] = None, **layout_kwargs) -> None: """Create a scatter plot. Args: data (array_like): Data in any format that can be converted to NumPy. Must be of shape (`x_labels`, `trace_names`). trace_names (str or list of str): Trace names, corresponding to columns in pandas. x_labels (array_like): X-axis labels, corresponding to index in pandas. trace_kwargs (dict or list of dict): Keyword arguments passed to `plotly.graph_objects.Scatter`. Can be specified per trace as a sequence of dicts. add_trace_kwargs (dict): Keyword arguments passed to `add_trace`. fig (Figure or FigureWidget): Figure to add traces to. **layout_kwargs: Keyword arguments for layout. ## Example ```python-repl >>> import vectorbt as vbt >>> scatter = vbt.plotting.Scatter( ... data=[[1, 2], [3, 4]], ... trace_names=['a', 'b'], ... x_labels=['x', 'y'] ... ) >>> scatter.fig ``` ![](/docs/img/Scatter.svg) """ Configured.__init__( self, data=data, trace_names=trace_names, x_labels=x_labels, trace_kwargs=trace_kwargs, add_trace_kwargs=add_trace_kwargs, fig=fig, **layout_kwargs ) if trace_kwargs is None: trace_kwargs = {} if add_trace_kwargs is None: add_trace_kwargs = {} if data is not None: data = reshape_fns.to_2d_array(data) if trace_names is not None: checks.assert_shape_equal(data, trace_names, (1, 0)) else: if trace_names is None: raise ValueError("At least data or trace_names must be passed") if trace_names is None: trace_names = [None] * data.shape[1] if isinstance(trace_names, str): trace_names = [trace_names] if x_labels is not None: x_labels = clean_labels(x_labels) if fig is None: fig = make_figure() fig.update_layout(**layout_kwargs) for i, trace_name in enumerate(trace_names): _trace_kwargs = resolve_dict(trace_kwargs, i=i) trace_name = _trace_kwargs.pop('name', trace_name) if trace_name is not None: trace_name = str(trace_name) scatter = go.Scatter( x=x_labels, name=trace_name, showlegend=trace_name is not None ) scatter.update(**_trace_kwargs) fig.add_trace(scatter, **add_trace_kwargs) TraceUpdater.__init__(self, fig, fig.data[-len(trace_names):]) if data is not None: self.update(data)
def test_assert_shape_equal(self): checks.assert_shape_equal(0, 1) checks.assert_shape_equal([1, 2, 3], np.asarray([1, 2, 3])) checks.assert_shape_equal([1, 2, 3], pd.Series([1, 2, 3])) checks.assert_shape_equal(np.zeros((3, 3)), pd.Series([1, 2, 3]), axis=0) checks.assert_shape_equal(np.zeros((2, 3)), pd.Series([1, 2, 3]), axis=(1, 0)) with pytest.raises(Exception) as e_info: checks.assert_shape_equal(np.zeros((2, 3)), pd.Series([1, 2, 3]), axis=(0, 1))