Browse Source

improved getitem, head, and tail functions

switch-to-decimal
Gourav Kumar 2 years ago
parent
commit
d7b06fbe24
  1. 110
      fincal/core.py

110
fincal/core.py

@ -3,7 +3,7 @@ import inspect
from collections import UserDict, UserList from collections import UserDict, UserList
from dataclasses import dataclass from dataclasses import dataclass
from numbers import Number from numbers import Number
from typing import Iterable, List, Literal, Sequence, Tuple from typing import Iterable, List, Literal, Mapping, Sequence, Union
from .utils import _parse_date, _preprocess_timeseries from .utils import _parse_date, _preprocess_timeseries
@ -32,7 +32,7 @@ def date_parser(*pos):
... return diff.days ... return diff.days
... return diff ... return diff
... ...
>>> calculate_difference(date1='2019-01-01'm date2='2020-01-01') >>> calculate_difference(date1='2019-01-01', date2='2020-01-01')
datetime.timedelta(365) datetime.timedelta(365)
Each of the dates is automatically parsed into a datetime.datetime object from string. Each of the dates is automatically parsed into a datetime.datetime object from string.
@ -83,7 +83,7 @@ class AllFrequencies:
class _IndexSlicer: class _IndexSlicer:
"""Class to create a slice using iloc in TimeSeriesCore""" """Class to create a slice using iloc in TimeSeriesCore"""
def __init__(self, parent_obj): def __init__(self, parent_obj: object):
self.parent = parent_obj self.parent = parent_obj
def __getitem__(self, n): def __getitem__(self, n):
@ -95,7 +95,7 @@ class _IndexSlicer:
if len(item) == 1: if len(item) == 1:
return item[0] return item[0]
return item return self.parent.__class__(item, self.parent.frequency.symbol)
class Series(UserList): class Series(UserList):
@ -319,38 +319,46 @@ class TimeSeriesCore(UserDict):
printable_str = "[{}]".format(",\n ".join([str(i) for i in self.data.items()])) printable_str = "[{}]".format(",\n ".join([str(i) for i in self.data.items()]))
return printable_str return printable_str
def __getitem__(self, key): @date_parser(1)
if isinstance(key, Series): def _get_item_from_date(self, date: Union[str, datetime.datetime]):
if not key.dtype == bool: return date, self.data[date]
raise ValueError(f"Cannot slice {self.__class__.__name__} using a Series of {key.dtype.__name__}")
elif len(key) != len(self.dates):
raise Exception(f"Length of Series: {len(key)} did not match length of object: {len(self.dates)}")
else:
dates = self.dates
dates_to_return = [dates[i] for i, j in enumerate(key) if j]
data_to_return = [(key, self.data[key]) for key in dates_to_return]
return self.__class__(data_to_return, frequency=self.frequency.symbol)
def _get_item_from_key(self, key: Union[str, datetime.datetime]):
if isinstance(key, int): if isinstance(key, int):
raise KeyError(f"{key}. For index based slicing, use .iloc[{key}]") raise KeyError(f"{key}. \nHint: use .iloc[{key}] for index based slicing.")
elif isinstance(key, (datetime.datetime, datetime.date)):
key = _parse_date(key) if key in ["dates", "values"]:
item = (key, self.data[key]) return getattr(self, key)
elif isinstance(key, str):
if key == "dates": return self._get_item_from_date(key)
return self.dates
elif key == "values": def _get_item_from_list(self, date_list: Sequence[Union[str, datetime.datetime]]):
return self.values data_to_return = [self._get_item_from_key(key) for key in date_list]
return self.__class__(data_to_return, frequency=self.frequency.symbol)
dt_key = _parse_date(key)
item = (dt_key, self.data[dt_key]) def _get_item_from_series(self, series: Series):
if series.dtype == bool:
elif isinstance(key, Sequence): if len(series) != len(self.dates):
keys = [_parse_date(i) for i in key] raise ValueError(f"Length of Series: {len(series)} did not match length of object: {len(self.dates)}")
item = [(k, self.data[k]) for k in keys] dates_to_return = [self.dates[i] for i, j in enumerate(series) if j]
elif series.dtype == datetime.datetime:
dates_to_return = list(series)
else: else:
raise TypeError(f"Invalid type {repr(type(key).__name__)} for slicing.") raise TypeError(f"Cannot slice {self.__class__.__name__} using a Series of {series.dtype.__name__}")
return item
return self._get_item_from_list(dates_to_return)
def __getitem__(self, key):
if isinstance(key, (int, str, datetime.datetime, datetime.date)):
return self._get_item_from_key(key)
if isinstance(key, Series):
return self._get_item_from_series(key)
if isinstance(key, Sequence):
return self._get_item_from_list(key)
raise TypeError(f"Invalid type {repr(type(key).__name__)} for slicing.")
def __iter__(self): def __iter__(self):
self.n = 0 self.n = 0
@ -364,31 +372,12 @@ class TimeSeriesCore(UserDict):
self.n += 1 self.n += 1
return key, self.data[key] return key, self.data[key]
@date_parser(1)
def __contains__(self, key: object) -> bool: def __contains__(self, key: object) -> bool:
key = _parse_date(key)
return super().__contains__(key) return super().__contains__(key)
def head(self, n: int = 6):
"""Returns the first n items of the TimeSeries object"""
keys = list(self.data.keys())
keys = keys[:n]
result = [(key, self.data[key]) for key in keys]
return result
def tail(self, n: int = 6):
"""Returns the last n items of the TimeSeries object"""
keys = list(self.data.keys())
keys = keys[-n:]
result = [(key, self.data[key]) for key in keys]
return result
def items(self):
return self.data.items()
@property @property
def iloc(self) -> List[Tuple[datetime.datetime, float]]: def iloc(self) -> Mapping:
"""Returns an item or a set of items based on index """Returns an item or a set of items based on index
supports slicing using numerical index. supports slicing using numerical index.
@ -406,3 +395,16 @@ class TimeSeriesCore(UserDict):
""" """
return _IndexSlicer(self) return _IndexSlicer(self)
def head(self, n: int = 6):
"""Returns the first n items of the TimeSeries object"""
return self.iloc[:n]
def tail(self, n: int = 6):
"""Returns the last n items of the TimeSeries object"""
return self.iloc[-n:]
def items(self):
return self.data.items()

Loading…
Cancel
Save