def repeat(self, n, as_columns=None): repeated = reshape_fns.repeat(self._obj, n, along_axis=1) if as_columns is not None: new_columns = index_fns.combine( reshape_fns.to_2d(self._obj).columns, as_columns) return self.wrap_array(repeated.values, columns=new_columns) return repeated
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(as_columns, broadcasted[0].columns) else: concated = np.hstack(broadcasted) return concated
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)
def tile(self, n, as_columns=None): tiled = reshape_fns.tile(self._obj, n, along_axis=1) if as_columns is not None: new_columns = index_fns.combine( as_columns, reshape_fns.to_2d(self._obj).columns) return self.wrap_array(tiled.values, columns=new_columns) return tiled
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(self.columns, as_columns) return self.wrap_array(repeated.values, columns=new_columns) return repeated
def rolling_window(self, window, n=None): 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._obj.index[idxs], name='start_date') new_columns = index_fns.combine(range_columns, reshape_fns.to_2d(self._obj).columns) return pd.DataFrame(matrix, columns=new_columns)
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.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(param_columns, param_index) if param_columns is not None: return index_fns.combine(param_columns, ts_columns) return ts_columns
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(as_columns, self.columns) else: new_columns = index_fns.tile(self.columns, ntimes) return self.wrap_array(result, columns=new_columns)
def concat(self_or_cls, *others, as_columns=None, broadcast_kwargs={}): 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(as_columns, broadcasted[0].columns) else: concated = np.hstack(broadcasted) return concated
def generate_stop_loss(self, ts, stops, trailing=False, as_columns=None, broadcast_kwargs={}): """See `vectorbt.signals.nb.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 order for 10% and 20% below the price `ts`: ```python-repl >>> print(entries.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) # 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.from_values(stops, name=name) columns = index_fns.combine(param_columns, entries.vbt.columns) return entries.vbt.wrap_array(exits, columns=columns)
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(self.columns, range_columns) return pd.DataFrame(matrix, columns=new_columns)
def combine_with_multiple(self, others, *args, combine_func=None, concat=False, broadcast_kwargs={}, as_columns=None, **kwargs): """Broadcast with other objects to the same shape and combine them all pairwise. The returned shape is the same as broadcasted shape if concat is False. The returned shape is concatenation of broadcasted shapes if concat is True.""" 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) broadcasted = tuple(map(np.asarray, (new_obj, *new_others))) if concat: # Concat the results horizontally if checks.is_numba_func(combine_func): for i in range(1, len(broadcasted)): # NOTE: all inputs must have the same dtype checks.assert_same_meta(broadcasted[i - 1], broadcasted[i]) result = combine_fns.combine_and_concat_nb( broadcasted[0], broadcasted[1:], combine_func, *args, **kwargs) else: result = combine_fns.combine_and_concat( broadcasted[0], broadcasted[1:], combine_func, *args, **kwargs) if as_columns is not None: new_columns = index_fns.combine( as_columns, reshape_fns.to_2d(new_obj).columns) else: new_columns = index_fns.tile( reshape_fns.to_2d(new_obj).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(broadcasted)): # NOTE: all inputs must have the same dtype checks.assert_same_dtype(broadcasted[i - 1], broadcasted[i]) result = combine_fns.combine_multiple_nb( broadcasted, combine_func, *args, **kwargs) else: result = combine_fns.combine_multiple(broadcasted, combine_func, *args, **kwargs) return new_obj.vbt.wrap_array(result)
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(as_columns, columns) else: new_columns = index_fns.tile(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)