Example #1
0
    def concat(self_or_cls, *others, as_columns=None, broadcast_kwargs={}):
        """Concatenate with `others` along columns.

        All arguments will be broadcasted using `vectorbt.utils.reshape_fns.broadcast`
        with `broadcast_kwargs`. Use `as_columns` as a top-level column level.

        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.concat(df, as_columns=['c', 'd']))
                  c     d
               a  b  a  b
            x  1  1  3  4
            y  2  2  5  6
            ```"""
        others = tuple(
            map(lambda x: x._obj
                if isinstance(x, Base_Accessor) else x, others))
        if isinstance(self_or_cls, type):
            objs = others
        else:
            objs = (self_or_cls._obj, ) + others
        broadcasted = reshape_fns.broadcast(*objs, **broadcast_kwargs)
        broadcasted = tuple(map(reshape_fns.to_2d, broadcasted))
        if checks.is_pandas(broadcasted[0]):
            concated = pd.concat(broadcasted, axis=1)
            if as_columns is not None:
                concated.columns = index_fns.combine_indexes(
                    as_columns, broadcasted[0].columns)
        else:
            concated = np.hstack(broadcasted)
        return concated
Example #2
0
    def repeat(self, n, as_columns=None):
        """See `vectorbt.utils.reshape_fns.repeat`.

        Use `as_columns` as a top-level column level."""
        repeated = reshape_fns.repeat(self._obj, n, axis=1)
        if as_columns is not None:
            new_columns = index_fns.combine_indexes(self.columns, as_columns)
            return self.wrap_array(repeated.values, columns=new_columns)
        return repeated
Example #3
0
    def tile(self, n, as_columns=None):
        """See `vectorbt.utils.reshape_fns.tile`.

        Use `as_columns` as a top-level column level."""
        tiled = reshape_fns.tile(self._obj, n, axis=1)
        if as_columns is not None:
            new_columns = index_fns.combine_indexes(as_columns, self.columns)
            return self.wrap(tiled.values, columns=new_columns)
        return tiled
Example #4
0
    def generate_stop_loss(self,
                           ts,
                           stops,
                           trailing=False,
                           relative=True,
                           as_columns=None,
                           broadcast_kwargs={}):
        """See `vectorbt.signals.nb.generate_stop_loss_nb`.

        Arguments will be broadcasted using `vectorbt.utils.reshape_fns.broadcast`
        with `broadcast_kwargs`. Argument `stops` can be either a single number, an array of 
        numbers, or a 3D array, where each matrix corresponds to a single configuration. 
        Use `as_columns` as a top-level column level.

        Example:
            For each entry in `signals`, set stop loss for 10% and 20% below the price `ts`:

            ```python-repl
            >>> print(signals.vbt.signals.generate_stop_loss(ts, [0.1, 0.2]))
            stop_loss                   0.1                  0.2
                            a      b      c      a      b      c
            2018-01-01  False  False  False  False  False  False
            2018-01-02  False   True  False  False  False  False
            2018-01-03  False  False  False  False  False  False
            2018-01-04  False   True   True  False   True   True
            2018-01-05  False  False  False  False  False  False
            ```"""
        entries = self._obj
        checks.assert_type(ts, (pd.Series, pd.DataFrame))

        entries, ts = reshape_fns.broadcast(entries,
                                            ts,
                                            **broadcast_kwargs,
                                            writeable=True)
        stops = reshape_fns.broadcast_to_array_of(stops,
                                                  entries.vbt.to_2d_array())
        exits = nb.generate_stop_loss_nb(entries.vbt.to_2d_array(),
                                         ts.vbt.to_2d_array(), stops, trailing,
                                         relative)

        # Build column hierarchy
        if as_columns is not None:
            param_columns = as_columns
        else:
            name = 'trail_stop' if trailing else 'stop_loss'
            param_columns = index_fns.index_from_values(stops, name=name)
        columns = index_fns.combine_indexes(param_columns, entries.vbt.columns)
        return entries.vbt.wrap_array(exits, columns=columns)
Example #5
0
def build_column_hierarchy(param_list, level_names, ts_columns):
    """For each parameter in `param_list`, create a new column level with parameter values. 
    Combine this level with columns `ts_columns` using Cartesian product."""
    checks.assert_same_shape(param_list, level_names, axis=0)
    param_indexes = [
        index_fns.index_from_values(param_list[i], name=level_names[i])
        for i in range(len(param_list))
    ]
    param_columns = None
    for param_index in param_indexes:
        if param_columns is None:
            param_columns = param_index
        else:
            param_columns = index_fns.stack_indexes(param_columns, param_index)
    if param_columns is not None:
        return index_fns.combine_indexes(param_columns, ts_columns)
    return ts_columns
Example #6
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)
Example #7
0
    def rolling_window(self, window, n=None):
        """Split time series into `n` time ranges each `window` long.

        The result will be a new DataFrame with index of length `window` and columns of length
        `len(columns) * n`. If `n` is `None`, will return the maximum number of time ranges.

        Example:
            ```python-repl
            >>> print(df.vbt.timeseries.rolling_window(2, n=2))
                                a                     b                     c           
            start_date 2018-01-01 2018-01-04 2018-01-01 2018-01-04 2018-01-01 2018-01-04
            0                 1.0        4.0        5.0        2.0        1.0        2.0
            1                 2.0        5.0        4.0        1.0        2.0        1.0 
            ```"""
        cube = nb.rolling_window_nb(self.to_2d_array(), window)
        if n is not None:
            idxs = np.round(np.linspace(0, cube.shape[2] - 1, n)).astype(int)
            cube = cube[:, :, idxs]
        else:
            idxs = np.arange(cube.shape[2])
        matrix = np.hstack(cube)
        range_columns = pd.Index(self.index[idxs], name='start_date')
        new_columns = index_fns.combine_indexes(self.columns, range_columns)
        return pd.DataFrame(matrix, columns=new_columns)
Example #8
0
    def combine_with_multiple(self,
                              others,
                              *args,
                              combine_func=None,
                              pass_2d=False,
                              concat=False,
                              broadcast_kwargs={},
                              as_columns=None,
                              **kwargs):
        """Combine with `others` using `combine_func`.

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

        If `concat` is `True`, concatenate the results along columns, 
        see `vectorbt.utils.combine_fns.combine_and_concat`.
        Otherwise, pairwise combine into a Series/DataFrame of the same shape, 
        see `vectorbt.utils.combine_fns.combine_multiple`.

        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.
        Use `as_columns` as a top-level column level.

        !!! note
            If `combine_func` is Numba-compiled, will broadcast using `writeable=True` and
            copy using `order='C'` flags, which can lead to an expensive computation overhead if
            passed objects are large and have different shape/memory order. You also must ensure 
            that all objects have the same data type.

            Also remember to bring each in `*args` to a Numba-compatible format.

        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_multiple([df, df*2], 
            ...     combine_func=lambda x, y: x + y))
                a   b
            x  10  13
            y  17  20

            >>> print(sr.vbt.combine_with_multiple([df, df*2], 
            ...     combine_func=lambda x, y: x + y, concat=True, as_columns=['c', 'd']))
                  c       d    
               a  b   a   b
            x  4  5   7   9
            y  7  8  12  14
            ```"""
        others = tuple(
            map(lambda x: x._obj
                if isinstance(x, Base_Accessor) else x, others))
        checks.assert_not_none(combine_func)
        checks.assert_type(others, Iterable)
        # Broadcast arguments
        if checks.is_numba_func(combine_func):
            # Numba requires writable arrays
            broadcast_kwargs = {**dict(writeable=True), **broadcast_kwargs}
            # Plus all of our arrays must be in the same order
            broadcast_kwargs['copy_kwargs'] = {
                **dict(order='C'),
                **broadcast_kwargs.get('copy_kwargs', {})
            }
        new_obj, *new_others = reshape_fns.broadcast(self._obj, *others,
                                                     **broadcast_kwargs)
        # Optionally cast to 2d array
        if pass_2d:
            bc_arrays = tuple(
                map(lambda x: reshape_fns.to_2d(np.asarray(x)),
                    (new_obj, *new_others)))
        else:
            bc_arrays = tuple(
                map(lambda x: np.asarray(x), (new_obj, *new_others)))
        if concat:
            # Concat the results horizontally
            if checks.is_numba_func(combine_func):
                for i in range(1, len(bc_arrays)):
                    checks.assert_same_meta(bc_arrays[i - 1], bc_arrays[i])
                result = combine_fns.combine_and_concat_nb(
                    bc_arrays[0], bc_arrays[1:], combine_func, *args, **kwargs)
            else:
                result = combine_fns.combine_and_concat(
                    bc_arrays[0], bc_arrays[1:], combine_func, *args, **kwargs)
            columns = new_obj.vbt.columns
            if as_columns is not None:
                new_columns = index_fns.combine_indexes(as_columns, columns)
            else:
                new_columns = index_fns.tile_index(columns, len(others))
            return new_obj.vbt.wrap_array(result, columns=new_columns)
        else:
            # Combine arguments pairwise into one object
            if checks.is_numba_func(combine_func):
                for i in range(1, len(bc_arrays)):
                    checks.assert_same_dtype(bc_arrays[i - 1], bc_arrays[i])
                result = combine_fns.combine_multiple_nb(
                    bc_arrays, combine_func, *args, **kwargs)
            else:
                result = combine_fns.combine_multiple(bc_arrays, combine_func,
                                                      *args, **kwargs)
            return new_obj.vbt.wrap_array(result)