Ejemplo n.º 1
0
    def apply(self, *args, apply_func=None, to_2d=False, **kwargs):
        """Apply a function `apply_func`.

        Arguments `*args` and `**kwargs` will be directly passed to `apply_func`.
        If `to_2d` is True, 2-dimensional NumPy arrays will be passed, otherwise as is.

        !!! note
            The resulted array must have the same shape as the original array.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt
        >>> import pandas as pd

        >>> sr = pd.Series([1, 2], index=['x', 'y'])
        >>> sr2.vbt.apply(apply_func=lambda x: x ** 2)
        i2
        x2    1
        y2    4
        z2    9
        Name: a2, dtype: int64
        ```
        """
        checks.assert_not_none(apply_func)
        # Optionally cast to 2d array
        if to_2d:
            obj = reshape_fns.to_2d(self._obj, raw=True)
        else:
            obj = np.asarray(self._obj)
        result = apply_func(obj, *args, **kwargs)
        return self.wrapper.wrap(result, group_by=False)
Ejemplo n.º 2
0
 def test_assert_not_none(self):
     checks.assert_not_none(0)
     try:
         checks.assert_not_none(None)
         raise Exception
     except:
         pass
Ejemplo n.º 3
0
    def map_reduce_partitions(self, map_func_nb=None, map_args=None,
                              reduce_func_nb=None, reduce_args=None):
        """See `vectorbt.signals.nb.map_reduce_partitions_nb`.

        ## Example

        Get average length of each partition in `sig`:
        ```python-repl
        >>> distance_map_nb = njit(lambda from_i, to_i, col: to_i - from_i)
        >>> mean_reduce_nb = njit(lambda col, a: np.nanmean(a))

        >>> sig.vbt.signals.map_reduce_partitions(
        ...     map_func_nb=distance_map_nb,
        ...     reduce_func_nb=mean_reduce_nb)
        a    1.0
        b    1.0
        c    3.0
        dtype: float64
        ```
        """
        checks.assert_not_none(map_func_nb)
        checks.assert_not_none(reduce_func_nb)
        checks.assert_numba_func(map_func_nb)
        checks.assert_numba_func(reduce_func_nb)
        if map_args is None:
            map_args = ()
        if reduce_args is None:
            reduce_args = ()

        result = nb.map_reduce_partitions_nb(
            self.to_2d_array(),
            map_func_nb, map_args,
            reduce_func_nb, reduce_args
        )
        return self.wrapper.wrap_reduced(result)
Ejemplo n.º 4
0
    def map_reduce_between(self,
                           other=None,
                           map_func_nb=None,
                           map_args=None,
                           reduce_func_nb=None,
                           reduce_args=None,
                           broadcast_kwargs=None):
        """See `vectorbt.signals.nb.map_reduce_between_nb`.

        If `other` specified, see `vectorbt.signals.nb.map_reduce_between_two_nb`.
        Both will be broadcast using `vectorbt.base.reshape_fns.broadcast`
        with `broadcast_kwargs`.

        Note that `map_args` and `reduce_args` won't be broadcast.

        ## Example

        Get average distance between signals in `sig`:
        ```python-repl
        >>> distance_map_nb = njit(lambda from_i, to_i, col: to_i - from_i)
        >>> mean_reduce_nb = njit(lambda col, a: np.nanmean(a))

        >>> sig.vbt.signals.map_reduce_between(
        ...     map_func_nb=distance_map_nb,
        ...     reduce_func_nb=mean_reduce_nb)
        a    NaN
        b    2.0
        c    1.0
        dtype: float64
        ```
        """
        if broadcast_kwargs is None:
            broadcast_kwargs = {}
        checks.assert_not_none(map_func_nb)
        checks.assert_not_none(reduce_func_nb)
        checks.assert_numba_func(map_func_nb)
        checks.assert_numba_func(reduce_func_nb)
        if map_args is None:
            map_args = ()
        if reduce_args is None:
            reduce_args = ()

        if other is None:
            # One input array
            result = nb.map_reduce_between_nb(self.to_2d_array(), map_func_nb,
                                              map_args, reduce_func_nb,
                                              reduce_args)
            if isinstance(self._obj, pd.Series):
                return result[0]
            return pd.Series(result, index=self.wrapper.columns)
        else:
            # Two input arrays
            obj, other = reshape_fns.broadcast(self._obj, other,
                                               **broadcast_kwargs)
            checks.assert_dtype(other, np.bool)
            result = nb.map_reduce_between_two_nb(obj.vbt.to_2d_array(),
                                                  other.vbt.to_2d_array(),
                                                  map_func_nb, map_args,
                                                  reduce_func_nb, reduce_args)
            return obj.vbt.wrapper.wrap_reduced(result)
Ejemplo n.º 5
0
 def apply_and_concat(self,
                      ntimes,
                      *args,
                      apply_func=None,
                      as_columns=None,
                      **kwargs):
     """Apply a function n times and concatenate results into a single dataframe."""
     checks.assert_not_none(apply_func)
     if checks.is_numba_func(apply_func):
         # NOTE: your apply_func must a numba-compiled function and arguments must be numba-compatible
         # Also NOTE: outputs of apply_func must always be 2-dimensional
         result = combine_fns.apply_and_concat_nb(np.asarray(self._obj),
                                                  ntimes, apply_func, *args,
                                                  **kwargs)
     else:
         result = combine_fns.apply_and_concat(np.asarray(self._obj),
                                               ntimes, apply_func, *args,
                                               **kwargs)
     # Build column hierarchy
     if as_columns is not None:
         new_columns = index_fns.combine(
             as_columns,
             reshape_fns.to_2d(self._obj).columns)
     else:
         new_columns = index_fns.tile(
             reshape_fns.to_2d(self._obj).columns, ntimes)
     return self.wrap_array(result, columns=new_columns)
Ejemplo n.º 6
0
    def __init__(self,
                 wrapper: ArrayWrapper,
                 records_arr: tp.RecordArray,
                 col_mapper: tp.Optional[ColumnMapper] = None,
                 **kwargs) -> None:
        Wrapping.__init__(
            self,
            wrapper,
            records_arr=records_arr,
            col_mapper=col_mapper,
            **kwargs
        )
        StatsBuilderMixin.__init__(self)

        # Check fields
        records_arr = np.asarray(records_arr)
        checks.assert_not_none(records_arr.dtype.fields)
        field_names = {
            dct.get('name', field_name)
            for field_name, dct in self.field_config.get('settings', {}).items()
        }
        dtype = self.field_config.get('dtype', None)
        if dtype is not None:
            for field in dtype.names:
                if field not in records_arr.dtype.names:
                    if field not in field_names:
                        raise TypeError(f"Field '{field}' from {dtype} cannot be found in records or config")

        self._records_arr = records_arr
        if col_mapper is None:
            col_mapper = ColumnMapper(wrapper, self.col_arr)
        self._col_mapper = col_mapper
Ejemplo n.º 7
0
    def wrap_reduced(self,
                     a,
                     index=None,
                     columns=None,
                     time_units=False,
                     dtype=None,
                     group_by=None):
        """Wrap result of reduction.

        `index` can be set when reducing to an array of values (vs. one value) per column.
        `columns` can be set to override object's default columns.

        If `time_units` is set, calls `to_time_units`."""
        checks.assert_not_none(self.ndim)
        _self = self.resolve(group_by=group_by)

        if columns is None:
            columns = _self.columns
        a = np.asarray(a)
        if dtype is not None:
            try:
                a = a.astype(dtype)
            except Exception as e:
                warnings.warn(repr(e), stacklevel=2)
        if time_units:
            a = _self.to_time_units(a)
        if a.ndim == 0:
            # Scalar per Series/DataFrame
            if time_units:
                return pd.to_timedelta(a.item())
            return a.item()
        if a.ndim == 1:
            if _self.ndim == 1:
                if a.shape[0] == 1:
                    # Scalar per Series/DataFrame with one column
                    if time_units:
                        return pd.to_timedelta(a[0])
                    return a[0]
                # Array per Series
                name = columns[0]
                if name == 0:  # was a Series before
                    name = None
                return pd.Series(a, index=index, name=name, dtype=dtype)
            # Scalar per column in a DataFrame
            if index is None:
                index = columns
            return pd.Series(a, index=index, dtype=dtype)
        if a.ndim == 2:
            if a.shape[1] == 1 and _self.ndim == 1:
                # Array per Series
                name = columns[0]
                if name == 0:  # was a Series before
                    name = None
                return pd.Series(a[:, 0], index=index, name=name, dtype=dtype)
            # Array per column in a DataFrame
            return pd.DataFrame(a, index=index, columns=columns, dtype=dtype)
        raise ValueError(f"{a.ndim}-d input is not supported")
Ejemplo n.º 8
0
    def apply_and_concat(self,
                         ntimes,
                         *args,
                         apply_func=None,
                         to_2d=False,
                         keys=None,
                         wrap_kwargs=None,
                         **kwargs):
        """Apply `apply_func` `ntimes` times and concatenate the results along columns.
        See `vectorbt.base.combine_fns.apply_and_concat_one`.

        Arguments `*args` and `**kwargs` will be directly passed to `apply_func`.
        If `to_2d` is True, 2-dimensional NumPy arrays will be passed, otherwise as is.
        Use `keys` as the outermost level.

        !!! note
            The resulted arrays to be concatenated must have the same shape as broadcast input arrays.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt
        >>> import pandas as pd

        >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b'])
        >>> df.vbt.apply_and_concat(3, [1, 2, 3],
        ...     apply_func=lambda i, a, b: a * b[i], keys=['c', 'd', 'e'])
              c       d       e
           a  b   a   b   a   b
        x  3  4   6   8   9  12
        y  5  6  10  12  15  18
        ```
        """
        checks.assert_not_none(apply_func)
        # Optionally cast to 2d array
        if to_2d:
            obj_arr = reshape_fns.to_2d(self._obj, raw=True)
        else:
            obj_arr = np.asarray(self._obj)
        if checks.is_numba_func(apply_func):
            result = combine_fns.apply_and_concat_one_nb(
                ntimes, apply_func, obj_arr, *args, **kwargs)
        else:
            result = combine_fns.apply_and_concat_one(ntimes, apply_func,
                                                      obj_arr, *args, **kwargs)
        # Build column hierarchy
        if keys is not None:
            new_columns = index_fns.combine_indexes(keys, self.wrapper.columns)
        else:
            top_columns = pd.Index(np.arange(ntimes), name='apply_idx')
            new_columns = index_fns.combine_indexes(top_columns,
                                                    self.wrapper.columns)
        return self.wrapper.wrap(result,
                                 group_by=False,
                                 **merge_dicts(dict(columns=new_columns),
                                               wrap_kwargs))
Ejemplo n.º 9
0
    def information_ratio(self):
        """Information ratio of a strategy.

        !!! note
            `factor_returns` must be set."""
        checks.assert_not_none(self.factor_returns)

        return self.wrapper.wrap_reduced(
            nb.information_ratio_nb(self.returns.vbt.to_2d_array(),
                                    self.factor_returns.vbt.to_2d_array()))
Ejemplo n.º 10
0
    def down_capture(self):
        """Capture ratio for periods when the benchmark return is negative.

        !!! note
            `factor_returns` must be set."""
        checks.assert_not_none(self.factor_returns)

        return self.wrapper.wrap_reduced(
            nb.down_capture_nb(self.returns.vbt.to_2d_array(),
                               self.factor_returns.vbt.to_2d_array(),
                               self.ann_factor))
Ejemplo n.º 11
0
    def capture(self):
        """Capture ratio.

        !!! note
            `factor_returns` must be set."""
        checks.assert_not_none(self.factor_returns)

        return self.wrapper.wrap_reduced(
            nb.capture_nb(self.returns.vbt.to_2d_array(),
                          self.factor_returns.vbt.to_2d_array(),
                          self.ann_factor))
Ejemplo n.º 12
0
    def beta(self):
        """Beta.

        !!! note
            `factor_returns` must be set."""
        checks.assert_not_none(self.factor_returns)

        return self.wrapper.wrap_reduced(
            nb.beta_nb(self.returns.vbt.to_2d_array(),
                       self.factor_returns.vbt.to_2d_array(),
                       risk_free=self.risk_free))
Ejemplo n.º 13
0
    def wrap_reduced(self,
                     a,
                     index=None,
                     columns=None,
                     time_units=False,
                     collapse=None,
                     **kwargs):
        """Wrap result of reduction.

        `index` can be set when reducing to an array of values (vs. one value) per column.
        `columns` can be set to override object's default columns.

        If `time_units` is set, calls `to_time_units`."""
        checks.assert_not_none(self.ndim)
        group_by = self.grouper.resolve_group_by(**kwargs)
        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

        a = np.asarray(a)
        if time_units:
            a = self.to_time_units(a)
        if a.ndim == 0:
            # Scalar per Series/DataFrame
            if time_units:
                return pd.to_timedelta(a.item())
            return a.item()
        if a.ndim == 1:
            if self.ndim == 1 or (self.ndim == 2 and len(columns) == 1
                                  and collapse):
                if a.shape[0] == 1:
                    # Scalar per Series/DataFrame with one column
                    if time_units:
                        return pd.to_timedelta(a[0])
                    return a[0]
                # Array per Series
                name = columns[0]
                if name == 0:  # was a Series before
                    name = None
                return pd.Series(a, index=index, name=name)
            # Scalar per column in a DataFrame
            if index is None:
                index = columns
            return pd.Series(a, index=index)
        if self.ndim == 1:
            # Array per Series
            name = columns[0]
            if name == 0:  # was a Series before
                name = None
            return pd.Series(a[:, 0], index=index, name=name)
        # Array per column in a DataFrame
        return pd.DataFrame(a, index=index, columns=columns)
Ejemplo n.º 14
0
    def combine_with(self,
                     other,
                     *args,
                     combine_func=None,
                     to_2d=False,
                     broadcast_kwargs=None,
                     wrap_kwargs=None,
                     **kwargs):
        """Combine both using `combine_func` into a Series/DataFrame of the same shape.

        All arguments will be broadcast using `vectorbt.base.reshape_fns.broadcast`
        with `broadcast_kwargs`.

        Arguments `*args` and `**kwargs` will be directly passed to `combine_func`.
        If `to_2d` is True, 2-dimensional NumPy arrays will be passed, otherwise as is.

        !!! note
            The resulted array must have the same shape as broadcast input arrays.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt
        >>> import pandas as pd

        >>> sr = pd.Series([1, 2], index=['x', 'y'])
        >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b'])
        >>> sr.vbt.combine_with(df, combine_func=lambda x, y: x + y)
           a  b
        x  4  5
        y  7  8
        ```
        """
        if isinstance(other, BaseAccessor):
            other = other._obj
        checks.assert_not_none(combine_func)
        if broadcast_kwargs is None:
            broadcast_kwargs = {}
        if checks.is_numba_func(combine_func):
            # Numba requires writable arrays
            broadcast_kwargs = merge_dicts(
                dict(require_kwargs=dict(requirements='W')), broadcast_kwargs)
        new_obj, new_other = reshape_fns.broadcast(self._obj, other,
                                                   **broadcast_kwargs)
        # Optionally cast to 2d array
        if to_2d:
            new_obj_arr = reshape_fns.to_2d(new_obj, raw=True)
            new_other_arr = reshape_fns.to_2d(new_other, raw=True)
        else:
            new_obj_arr = np.asarray(new_obj)
            new_other_arr = np.asarray(new_other)
        result = combine_func(new_obj_arr, new_other_arr, *args, **kwargs)
        return new_obj.vbt.wrapper.wrap(result, **merge_dicts({}, wrap_kwargs))
Ejemplo n.º 15
0
    def alpha(self):
        """Annualized alpha.

        !!! note
            `factor_returns` must be set."""
        checks.assert_not_none(self.factor_returns)

        return self.wrapper.wrap_reduced(
            nb.alpha_nb(self.returns.vbt.to_2d_array(),
                        self.factor_returns.vbt.to_2d_array(),
                        reshape_fns.to_1d(self.beta, raw=True),
                        self.ann_factor,
                        risk_free=self.risk_free))
Ejemplo n.º 16
0
def unstack_to_df(arg, index_levels=None, column_levels=None, symmetric=False):
    """Reshape `arg` based on its multi-index into a DataFrame.

    Use `index_levels` to specify what index levels will form new index, and `column_levels` 
    for new columns. Set `symmetric` to `True` to make DataFrame symmetric.

    Example:
        ```python-repl
        >>> import pandas as pd
        >>> from vectorbt.utils.reshape_fns import unstack_to_df

        >>> index = pd.MultiIndex.from_arrays(
        ...     [[1, 1, 2, 2], [3, 4, 3, 4], ['a', 'b', 'c', 'd']], 
        ...     names=['x', 'y', 'z'])
        >>> sr = pd.Series([1, 2, 3, 4], index=index)

        >>> print(unstack_to_df(sr, index_levels=(0, 1), column_levels=2))
        z      a    b    c    d
        x y                    
        1 3  1.0  NaN  NaN  NaN
        1 4  NaN  2.0  NaN  NaN
        2 3  NaN  NaN  3.0  NaN
        2 4  NaN  NaN  NaN  4.0
        ```"""
    # Perform checks
    checks.assert_type(arg, (pd.Series, pd.DataFrame))
    if checks.is_frame(arg):
        if arg.shape[0] == 1:
            arg = arg.iloc[0, :]
        elif arg.shape[1] == 1:
            arg = arg.iloc[:, 0]
    checks.assert_type(arg.index, pd.MultiIndex)
    sr = to_1d(arg)

    if len(sr.index.levels) > 2:
        checks.assert_not_none(index_levels)
        checks.assert_not_none(column_levels)
    else:
        index_levels = 0
        column_levels = 1

    # Build new index and column hierarchies
    new_index = index_fns.select_levels(arg.index, index_levels).unique()
    new_columns = index_fns.select_levels(arg.index, column_levels).unique()

    # Unstack and post-process
    unstacked = unstack_to_array(sr, levels=(index_levels, column_levels))
    df = pd.DataFrame(unstacked, index=new_index, columns=new_columns)
    if symmetric:
        return make_symmetric(df)
    return df
Ejemplo n.º 17
0
    def map_reduce_between(self,
                           *args,
                           other=None,
                           map_func_nb=None,
                           reduce_func_nb=None,
                           broadcast_kwargs={}):
        """See `vectorbt.signals.nb.map_reduce_between_nb`. 

        If `other` specified, see `vectorbt.signals.nb.map_reduce_between_two_nb`.

        Arguments will be broadcasted using `vectorbt.utils.reshape_fns.broadcast`
        with `broadcast_kwargs`.

        Example:
            Get maximum distance between signals in `signals`:

            ```python-repl
            >>> distance_map_nb = njit(lambda col, prev_i, next_i: next_i - prev_i)
            >>> max_reduce_nb = njit(lambda col, a: np.nanmax(a))

            >>> print(signals.vbt.signals.map_reduce_between(
            ...     map_func_nb=distance_map_nb, reduce_func_nb=max_reduce_nb))
            a    3.0
            b    3.0
            c    NaN
            dtype: float64
            ```"""
        checks.assert_not_none(map_func_nb)
        checks.assert_not_none(reduce_func_nb)
        checks.assert_numba_func(map_func_nb)
        checks.assert_numba_func(reduce_func_nb)

        if other is None:
            # One input array
            result = nb.map_reduce_between_nb(self.to_2d_array(), map_func_nb,
                                              reduce_func_nb, *args)
            if isinstance(self._obj, pd.Series):
                return result[0]
            return pd.Series(result, index=self.columns)
        else:
            # Two input arrays
            obj, other = reshape_fns.broadcast(self._obj, other,
                                               **broadcast_kwargs)
            other.vbt.signals.validate()
            result = nb.map_reduce_between_two_nb(self.to_2d_array(),
                                                  other.vbt.to_2d_array(),
                                                  map_func_nb, reduce_func_nb,
                                                  *args)
            if isinstance(obj, pd.Series):
                return result[0]
            return pd.Series(result, index=obj.vbt.columns)
Ejemplo n.º 18
0
    def apply(self,
              *args,
              apply_func: tp.Optional[tp.Callable] = None,
              keep_pd: bool = False,
              to_2d: bool = False,
              wrap_kwargs: tp.KwargsLike = None,
              **kwargs) -> tp.SeriesFrame:
        """Apply a function `apply_func`.

        Args:
            *args: Variable arguments passed to `apply_func`.
            apply_func (callable): Apply function.

                Can be Numba-compiled.
            keep_pd (bool): Whether to keep inputs as pandas objects, otherwise convert to NumPy arrays.
            to_2d (bool): Whether to reshape inputs to 2-dim arrays, otherwise keep as-is.
            wrap_kwargs (dict): Keyword arguments passed to `vectorbt.base.array_wrapper.ArrayWrapper.wrap`.
            **kwargs: Keyword arguments passed to `combine_func`.

        !!! note
            The resulted array must have the same shape as the original array.

        ## Example

        ```python-repl
        >>> import vectorbt as vbt
        >>> import pandas as pd

        >>> sr = pd.Series([1, 2], index=['x', 'y'])
        >>> sr2.vbt.apply(apply_func=lambda x: x ** 2)
        i2
        x2    1
        y2    4
        z2    9
        Name: a2, dtype: int64
        ```
        """
        checks.assert_not_none(apply_func)
        # Optionally cast to 2d array
        if to_2d:
            obj = reshape_fns.to_2d(self.obj, raw=not keep_pd)
        else:
            if not keep_pd:
                obj = np.asarray(self.obj)
            else:
                obj = self.obj
        result = apply_func(obj, *args, **kwargs)
        return self.wrapper.wrap(result,
                                 group_by=False,
                                 **merge_dicts({}, wrap_kwargs))
Ejemplo n.º 19
0
    def map_reduce_between(self,
                           *args,
                           other=None,
                           map_func_nb=None,
                           reduce_func_nb=None,
                           broadcast_kwargs={}):
        """See `vectorbt.signals.nb.map_reduce_between_nb`. 

        If `other` specified, see `vectorbt.signals.nb.map_reduce_between_two_nb`.

        Arguments will be broadcasted using `vectorbt.base.reshape_fns.broadcast`
        with `broadcast_kwargs`.

        Example:
            Get average distance between signals in `sig`:

            ```python-repl
            >>> distance_map_nb = njit(lambda col, from_i, to_i: to_i - from_i)
            >>> mean_reduce_nb = njit(lambda col, a: np.nanmean(a))

            >>> print(sig.vbt.signals.map_reduce_between(
            ...     map_func_nb=distance_map_nb, reduce_func_nb=mean_reduce_nb))
            a    NaN
            b    2.0
            c    1.0
            dtype: float64
            ```"""
        checks.assert_not_none(map_func_nb)
        checks.assert_not_none(reduce_func_nb)
        checks.assert_numba_func(map_func_nb)
        checks.assert_numba_func(reduce_func_nb)

        if other is None:
            # One input array
            result = nb.map_reduce_between_nb(self.to_2d_array(), map_func_nb,
                                              reduce_func_nb, *args)
            if isinstance(self._obj, pd.Series):
                return result[0]
            return pd.Series(result, index=self.columns)
        else:
            # Two input arrays
            obj, other = reshape_fns.broadcast(self._obj, other,
                                               **broadcast_kwargs)
            checks.assert_dtype(other, np.bool_)
            result = nb.map_reduce_between_two_nb(self.to_2d_array(),
                                                  other.vbt.to_2d_array(),
                                                  map_func_nb, reduce_func_nb,
                                                  *args)
            return self.wrap_reduced(result)
Ejemplo n.º 20
0
    def __init__(self, records_arr, wrapper, idx_field=None):
        if not isinstance(records_arr, np.ndarray):
            records_arr = np.asarray(records_arr)
        checks.assert_not_none(records_arr.dtype.fields)
        checks.assert_value_in('col', records_arr.dtype.names)
        checks.assert_type(wrapper, ArrayWrapper)
        if idx_field is not None:
            checks.assert_value_in(idx_field, records_arr.dtype.names)
        else:
            if 'idx' in records_arr.dtype.names:
                idx_field = 'idx'

        self.records_arr = records_arr
        self.wrapper = wrapper
        self.idx_field = idx_field

        PandasIndexer.__init__(self, _records_indexing_func)
Ejemplo n.º 21
0
    def apply_and_concat(self,
                         ntimes,
                         *args,
                         apply_func=None,
                         pass_2d=False,
                         as_columns=None,
                         **kwargs):
        """Apply `apply_func` `ntimes` times and concatenate the results along columns.
        See `vectorbt.utils.combine_fns.apply_and_concat`.

        Arguments `*args` and `**kwargs` will be directly passed to `apply_func`.
        If `pass_2d` is `True`, 2-dimensional NumPy arrays will be passed, otherwise as is.
        Use `as_columns` as a top-level column level.

        Example:
            ```python-repl
            >>> import pandas as pd
            >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b'])

            >>> print(df.vbt.apply_and_concat(3, [1, 2, 3], 
            ...     apply_func=lambda i, a, b: a * b[i], as_columns=['c', 'd', 'e']))
                  c       d       e    
               a  b   a   b   a   b
            x  3  4   6   8   9  12
            y  5  6  10  12  15  18
            ```"""
        checks.assert_not_none(apply_func)
        # Optionally cast to 2d array
        if pass_2d:
            obj_arr = reshape_fns.to_2d(np.asarray(self._obj))
        else:
            obj_arr = np.asarray(self._obj)
        if checks.is_numba_func(apply_func):
            result = combine_fns.apply_and_concat_nb(obj_arr, ntimes,
                                                     apply_func, *args,
                                                     **kwargs)
        else:
            result = combine_fns.apply_and_concat(obj_arr, ntimes, apply_func,
                                                  *args, **kwargs)
        # Build column hierarchy
        if as_columns is not None:
            new_columns = index_fns.combine_indexes(as_columns, self.columns)
        else:
            new_columns = index_fns.tile_index(self.columns, ntimes)
        return self.wrap_array(result, columns=new_columns)
Ejemplo n.º 22
0
    def combine_with(self,
                     other,
                     *args,
                     combine_func=None,
                     pass_2d=False,
                     broadcast_kwargs={},
                     **kwargs):
        """Combine both using `combine_func` into a Series/DataFrame of the same shape.

        All arguments will be broadcasted using `vectorbt.utils.reshape_fns.broadcast`
        with `broadcast_kwargs`.

        Arguments `*args` and `**kwargs` will be directly passed to `combine_func`.
        If `pass_2d` is `True`, 2-dimensional NumPy arrays will be passed, otherwise as is.

        Example:
            ```python-repl
            >>> import pandas as pd
            >>> sr = pd.Series([1, 2], index=['x', 'y'])
            >>> df = pd.DataFrame([[3, 4], [5, 6]], index=['x', 'y'], columns=['a', 'b'])

            >>> print(sr.vbt.combine_with(df, combine_func=lambda x, y: x + y))
               a  b
            x  4  5
            y  7  8
            ```"""
        if isinstance(other, Base_Accessor):
            other = other._obj
        checks.assert_not_none(combine_func)
        if checks.is_numba_func(combine_func):
            # Numba requires writable arrays
            broadcast_kwargs = {**dict(writeable=True), **broadcast_kwargs}
        new_obj, new_other = reshape_fns.broadcast(self._obj, other,
                                                   **broadcast_kwargs)
        # Optionally cast to 2d array
        if pass_2d:
            new_obj_arr = reshape_fns.to_2d(np.asarray(new_obj))
            new_other_arr = reshape_fns.to_2d(np.asarray(new_other))
        else:
            new_obj_arr = np.asarray(new_obj)
            new_other_arr = np.asarray(new_other)
        result = combine_func(new_obj_arr, new_other_arr, *args, **kwargs)
        return new_obj.vbt.wrap_array(result)
Ejemplo n.º 23
0
    def wrap_reduced(self, a, index=None, columns=None, time_units=False):
        """Wrap result of reduction.

        `index` can be set when reducing to an array of values (vs. one value) per column.
        `columns` can be set to override object's default columns.

        If `time_units` is set, calls `to_time_units`."""
        checks.assert_not_none(self.ndim)
        if columns is None:
            columns = self.columns

        a = np.asarray(a)
        if time_units:
            a = self.to_time_units(a)
        if a.ndim == 0:
            # Scalar value
            if time_units:
                return pd.to_timedelta(a.item())
            return a.item()
        if a.ndim == 1:
            if self.ndim == 1:
                if a.shape[0] == 1:
                    # Scalar value
                    if time_units:
                        return pd.to_timedelta(a[0])
                    return a[0]
                # Array per series
                name = columns[0]
                if name == 0:  # was a Series before
                    name = None
                return pd.Series(a, index=index, name=name)
            # Value per column
            if index is None:
                index = columns
            return pd.Series(a, index=index)
        if self.ndim == 1:
            # Array per series
            name = columns[0]
            if name == 0:  # was a Series before
                name = None
            return pd.Series(a[:, 0], index=index, name=name)
        # Value per column
        return pd.DataFrame(a, index=index, columns=columns)
Ejemplo n.º 24
0
    def __init__(self, wrapper, records_arr, idx_field='auto', **kwargs):
        Wrapping.__init__(self,
                          wrapper,
                          records_arr=records_arr,
                          idx_field=idx_field,
                          **kwargs)
        records_arr = np.asarray(records_arr)
        checks.assert_not_none(records_arr.dtype.fields)
        checks.assert_in('id', records_arr.dtype.names)
        checks.assert_in('col', records_arr.dtype.names)
        if idx_field == 'auto':
            if 'idx' in records_arr.dtype.names:
                idx_field = 'idx'
        elif idx_field is not None:
            checks.assert_in(idx_field, records_arr.dtype.names)

        self._records_arr = records_arr
        self._idx_field = idx_field
        self._col_mapper = ColumnMapper(wrapper, records_arr['col'])
Ejemplo n.º 25
0
    def combine_with(self,
                     other,
                     *args,
                     combine_func=None,
                     broadcast_kwargs={},
                     **kwargs):
        """Broadcast with other and combine.

        The returned shape is the same as broadcasted shape."""
        if isinstance(other, Base_Accessor):
            other = other._obj
        checks.assert_not_none(combine_func)
        if checks.is_numba_func(combine_func):
            # Numba requires writable arrays
            broadcast_kwargs = {**dict(writeable=True), **broadcast_kwargs}
        new_obj, new_other = reshape_fns.broadcast(self._obj, other,
                                                   **broadcast_kwargs)
        return new_obj.vbt.wrap_array(
            combine_func(np.asarray(new_obj), np.asarray(new_other), *args,
                         **kwargs))
Ejemplo n.º 26
0
    def __init__(self, columns, group_by=None, allow_enable=True, allow_disable=True, allow_modify=True):
        Configured.__init__(
            self,
            columns=columns,
            group_by=group_by,
            allow_enable=allow_enable,
            allow_disable=allow_disable,
            allow_modify=allow_modify
        )

        checks.assert_not_none(columns)
        self._columns = columns
        if group_by is None or isinstance(group_by, bool):
            self._group_by = None
        else:
            self._group_by = group_by_to_index(columns, group_by)

        # Everything is allowed by default
        self._allow_enable = allow_enable
        self._allow_disable = allow_disable
        self._allow_modify = allow_modify
Ejemplo n.º 27
0
    def map_reduce_partitions(
            self,
            map_func_nb: tp.Optional[tp.SignalMapFunc] = None,
            map_args: tp.Optional[tp.Args] = None,
            reduce_func_nb: tp.Optional[tp.SignalReduceFunc] = None,
            reduce_args: tp.Optional[tp.Args] = None,
            wrap_kwargs: tp.KwargsLike = None) -> tp.MaybeSeries:
        """See `vectorbt.signals.nb.map_reduce_partitions_nb`.

        ## Example

        Get average length of each partition in `sig`:
        ```python-repl
        >>> distance_map_nb = njit(lambda from_i, to_i, col: to_i - from_i)
        >>> mean_reduce_nb = njit(lambda col, a: np.nanmean(a))

        >>> sig.vbt.signals.map_reduce_partitions(
        ...     map_func_nb=distance_map_nb,
        ...     reduce_func_nb=mean_reduce_nb)
        a    1.0
        b    1.0
        c    3.0
        dtype: float64
        ```
        """
        checks.assert_not_none(map_func_nb)
        checks.assert_not_none(reduce_func_nb)
        checks.assert_numba_func(map_func_nb)
        checks.assert_numba_func(reduce_func_nb)
        if map_args is None:
            map_args = ()
        if reduce_args is None:
            reduce_args = ()

        result = nb.map_reduce_partitions_nb(self.to_2d_array(), map_func_nb,
                                             map_args, reduce_func_nb,
                                             reduce_args)
        wrap_kwargs = merge_dicts(dict(name_or_index='map_reduce_partitions'),
                                  wrap_kwargs)
        return self.wrapper.wrap_reduced(result, **wrap_kwargs)
Ejemplo n.º 28
0
    def wrap_reduced(self, a, index=None, name=None, time_units=False):
        """Wrap result of reduction.

        Argument `index` can be passed when reducing to an array of values (vs. one value) per column.

        If `time_units` is set, calls `to_time_units`."""
        checks.assert_not_none(self.ndim)

        a = np.asarray(a)
        if time_units:
            a = self.to_time_units(a)
        if a.ndim == 0:
            # Scalar value
            if time_units:
                return pd.to_timedelta(a.item())
            return a.item()
        elif a.ndim == 1:
            if self.ndim == 1:
                if a.shape[0] == 1:
                    # Scalar value
                    if time_units:
                        return pd.to_timedelta(a[0])
                    return a[0]
                # Array per series
                if name is None:
                    name = self.columns[0]
                if name == 0:  # was a Series before
                    name = None
                return pd.Series(a, index=index, name=name)
            # Value per column
            return pd.Series(a, index=self.columns, name=name)
        if self.ndim == 1:
            # Array per series
            if name is None:
                name = self.columns[0]
            if name == 0:  # was a Series before
                name = None
            return pd.Series(a[:, 0], index=index, name=name)
        # Value per column
        return pd.DataFrame(a, index=index, columns=self.columns)
Ejemplo n.º 29
0
    def __init__(self, index, columns, ndim, freq=None, column_only_select=None,
                 group_select=None, grouped_ndim=None, **kwargs):
        config = dict(
            index=index,
            columns=columns,
            ndim=ndim,
            freq=freq,
            column_only_select=column_only_select,
            group_select=group_select,
            grouped_ndim=grouped_ndim,
        )

        checks.assert_not_none(index)
        checks.assert_not_none(columns)
        checks.assert_not_none(ndim)
        if not isinstance(index, pd.Index):
            index = pd.Index(index)
        if not isinstance(columns, pd.Index):
            columns = pd.Index(columns)

        self._index = index
        self._columns = columns
        self._ndim = ndim
        self._freq = freq
        self._column_only_select = column_only_select
        self._group_select = group_select
        self._grouper = ColumnGrouper(columns, **kwargs)
        self._grouped_ndim = grouped_ndim

        PandasIndexer.__init__(self)
        Configured.__init__(self, **merge_dicts(config, self._grouper._config))
Ejemplo n.º 30
0
def unstack_to_df(arg, index_levels=None, column_levels=None, symmetric=False):
    """Reshape object based on multi-index into dataframe."""
    # Perform checks
    checks.assert_type(arg, (pd.Series, pd.DataFrame))
    if checks.is_frame(arg):
        if arg.shape[0] == 1:
            arg = arg.iloc[0, :]
        elif arg.shape[1] == 1:
            arg = arg.iloc[:, 0]
    checks.assert_type(arg.index, pd.MultiIndex)
    sr = to_1d(arg)

    if len(sr.index.levels) > 2:
        checks.assert_not_none(index_levels)
        checks.assert_not_none(column_levels)
    else:
        index_levels = 0
        column_levels = 1

    # Build new index and column hierarchies
    new_index = np.unique(index_fns.select_levels(arg.index, index_levels))
    new_columns = np.unique(index_fns.select_levels(arg.index, column_levels))
    if isinstance(index_levels, (list, tuple)):
        new_index = pd.MultiIndex.from_tuples(new_index, names=index_levels)
    else:
        new_index = pd.Index(new_index, name=index_levels)
    if isinstance(column_levels, (list, tuple)):
        new_columns = pd.MultiIndex.from_tuples(new_columns,
                                                names=column_levels)
    else:
        new_columns = pd.Index(new_columns, name=column_levels)

    # Unstack and post-process
    unstacked = unstack_to_array(sr, levels=(index_levels, column_levels))
    df = pd.DataFrame(unstacked, index=new_index, columns=new_columns)
    if symmetric:
        return make_symmetric(df)
    return df