Source code for onetick.py.core.column
import inspect
from .column_operations.base import _Operation
from .. import types as ott
[docs]class Column(_Operation):
    """
    :py:class:`~onetick.py.Source` column container.
    This is the object you get when using :py:meth:`~onetick.py.Source.__getitem__`.
    You can use this object everywhere where :py:class:`~onetick.py.Operation` object can be used.
    Examples
    --------
    >>> t = otp.Tick(A=1)
    >>> t['A']
    Column(A, <class 'int'>)
    """
    def __init__(self, name, dtype=float, obj_ref=None, to_str_func=None, precision=None,
                 pre_format=None, post_format=None):
        if not dtype or not inspect.isclass(dtype) or not ott.is_type_supported(dtype):
            raise TypeError(f'Column does not support "{dtype}" type')
        self.name = name
        super().__init__(dtype=dtype, obj_ref=obj_ref, op_str=name)
        if to_str_func:
            self.to_str_func = to_str_func
        else:
            self.to_str_func = self.__str__
        self.pre_format = None  # TODO: not used yet, but going to be used in
        self.post_format = None  # PY-35
        # optional properties
        if precision is not None:
            if issubclass(dtype, float):
                self._precision = precision
            else:
                raise ValueError("precision is supported only for columns with dtype is float")
    def rename(self, new_name, update_parent_object=True):
        if self.obj_ref and update_parent_object:
            self.obj_ref.rename({self.name: new_name}, inplace=True)
        self.name = new_name
    def __len__(self):
        if issubclass(self.dtype, str):
            if issubclass(self.dtype, ott.string):
                return self.dtype.length
            else:
                return ott.string.DEFAULT_LENGTH
        else:
            raise TypeError(f'It is not applicable for the column with type {self.dtype}')  # TODO: test
    def __hash__(self):
        return hash(self.name)
    def __str__(self):
        from onetick.py.core._source._symbol_param_source import _SymbolParamSource
        if self.obj_ref:
            result = ""
            if isinstance(self.obj_ref, _SymbolParamSource):
                # TODO: PY-35
                # This is ad-hoc check, really we need to change column formatting to
                # pre- and post-formats, and copy columns through the .copy() method
                # on the _Column instead of copying them manually in different places
                # of the _Source class
                if self.name != "_SYMBOL_NAME":
                    result = "_SYMBOL_PARAM."
            if self.obj_ref.use_name_for_column_prefix():
                if self.obj_ref.node_name().strip() == "":
                    raise Exception("You set to use name for column prefix, but name is empty")
                result = self.obj_ref.node_name() + "."
            result += self.name
            if isinstance(self.obj_ref, _SymbolParamSource):
                # symbol params always are string, need to convert
                if self.dtype is float:
                    result = f"atof({result})"
                elif self.dtype is int:
                    result = f"atol({result})"
                elif self.dtype is ott.msectime:
                    result = f"msec_str_to_nsectime({result})"
                elif self.dtype is ott.nsectime:
                    result = f"msec_str_to_nsectime({result})"
        else:
            result = self.name
        return result
    def __repr__(self):
        return f"Column({str(self)}, {self.dtype})"
    def copy(self, obj_ref=None):
        return _Column(self.name, self.dtype, obj_ref, pre_format=self.pre_format, post_format=self.post_format)
    def __bool__(self):
        if _Column.emulation_enabled:
            if issubclass(self.dtype, int):
                return (self != 0).__bool__()
            if issubclass(self.dtype, float):
                return (self != 0).__bool__()
            if issubclass(self.dtype, str):
                return (self != "").__bool__()
        raise TypeError("It is not allowed to use columns in if-else and while clauses")
[docs]    def __getitem__(self, item):
        """
        Provides an ability to get values from future or past ticks.
        - Negative values refer to past ticks
        - Zero to current tick
        - Positive - future ticks
        Boundary values will be defaulted. For instance for ``item=-1`` first tick value will be defaulted
        (there is no tick before first tick)
        Parameters
        ----------
        item: int
            number of ticks to look back/forward
        Returns
        -------
        Operation
        Examples
        --------
        >>> data = otp.Ticks({'A': [1, 2, 3]})
        >>> data['PAST1'] = data['A'][-1]
        >>> data['PAST2'] = data['A'][-2]
        >>> data['FUTURE1'] = data['A'][1]
        >>> data['FUTURE2'] = data['A'][2]
        >>> data()
                             Time  A  PAST1  PAST2  FUTURE1  FUTURE2
        0 2003-12-01 00:00:00.000  1      0      0        2        3
        1 2003-12-01 00:00:00.001  2      1      0        3        0
        2 2003-12-01 00:00:00.002  3      2      1        0        0
        """
        if not isinstance(item, int):
            raise TypeError(
                "Lag operation supports only integer const values," f" but passed value of type '{type(item)}'"
            )
        if item == 0:
            return self
        return _LagOperator(self, item) 
    def __iter__(self):
        raise TypeError("It is not allowed to use columns in for-clauses") 
class _LagOperator(_Operation):
    """
    Implements referencing to the prior tick
    """
    def __init__(self, base_column, inx):
        self._inx = inx
        op_str = f"{str(base_column)}[{self.index}]"
        super().__init__(op_params=[base_column], dtype=base_column.dtype,
                         op_str=op_str, obj_ref=base_column.obj_ref)
    @property
    def index(self):
        return self._inx
_Column = Column  # alias for backward compatibility