improved documentation

This commit is contained in:
Gourav Kumar 2022-02-27 00:22:08 +05:30
parent cad4e1f45c
commit ad68dcd930
2 changed files with 177 additions and 22 deletions

View File

@ -2,7 +2,7 @@ import datetime
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 from typing import Iterable, List, Literal, Sequence, Tuple
from .utils import _parse_date, _preprocess_timeseries from .utils import _parse_date, _preprocess_timeseries
@ -189,29 +189,40 @@ class TimeSeriesCore(UserDict):
self._end_date = None self._end_date = None
@property @property
def dates(self): def dates(self) -> Series:
"""Get a list of all the dates in the TimeSeries object"""
if self._dates is None or len(self._dates) != len(self.data): if self._dates is None or len(self._dates) != len(self.data):
self._dates = list(self.data.keys()) self._dates = list(self.data.keys())
return Series(self._dates, "date") return Series(self._dates, "date")
@property @property
def values(self): def values(self) -> Series:
"""Get a list of all the Values in the TimeSeries object"""
if self._values is None or len(self._values) != len(self.data): if self._values is None or len(self._values) != len(self.data):
self._values = list(self.data.values()) self._values = list(self.data.values())
return Series(self._values, "number") return Series(self._values, "number")
@property @property
def start_date(self): def start_date(self) -> datetime.datetime:
"""The first date in the TimeSeries object"""
return self.dates[0] return self.dates[0]
@property @property
def end_date(self): def end_date(self) -> datetime.datetime:
"""The last date in the TimeSeries object"""
return self.dates[-1] return self.dates[-1]
def _get_printable_slice(self, n: int): def _get_printable_slice(self, n: int):
"""Returns a slice of the dataframe from beginning and end""" """Helper function for __repr__ and __str__
Returns a slice of the dataframe from beginning and end.
"""
printable = {} printable = {}
iter_f = iter(self.data) iter_f = iter(self.data)
@ -322,7 +333,21 @@ class TimeSeriesCore(UserDict):
return self.data.items() return self.data.items()
@property @property
def iloc(self): def iloc(self) -> List[Tuple[datetime.datetime, float]]:
"""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.
Accepts integers or Python slice objects
Usage
-----
>>> ts = TimeSeries(data, frequency='D')
>>> ts.iloc[0] # get the first value
>>> ts.iloc[-1] # get the last value
>>> ts.iloc[:3] # get the first 3 values
>>> ts.illoc[-3:] # get the last 3 values
>>> ts.iloc[5:10] # get five values starting from the fifth value
>>> ts.iloc[::2] # get every alternate date
"""
return _IndexSlicer(self) return _IndexSlicer(self)

View File

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import datetime import datetime
from typing import List, Literal, Union from typing import Iterable, List, Literal, Mapping, Union
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
@ -15,9 +15,41 @@ from .utils import (
def create_date_series( def create_date_series(
start_date: datetime.datetime, end_date: datetime.datetime, frequency: str, eomonth: bool = False start_date: Union[str, datetime.datetime],
end_date: Union[str, datetime.datetime],
frequency: Literal["D", "W", "M", "Q", "H", "Y"],
eomonth: bool = False,
) -> List[datetime.datetime]: ) -> List[datetime.datetime]:
"""Creates a date series using a frequency""" """Create a date series with a specified frequency
Parameters
----------
start_date : str | datetime.datetime
Date series will always start at this date
end_date : str | datetime.datetime
The date till which the series should extend
Depending on the other parameters, this date may or may not be present
in the final date series
frequency : D | W | M | Q | H | Y
Frequency of the date series.
The gap between each successive date will be equivalent to this frequency
eomonth : bool, optional
Speacifies if the dates in the series should be end of month dates.
Can only be used if the frequency is Monthly or lower.
Returns
-------
List[datetime.datetime]
Returns the series as a list of datetime objects
Raises
------
ValueError
If eomonth is True and frequency is higher than monthly
"""
frequency = getattr(AllFrequencies, frequency) frequency = getattr(AllFrequencies, frequency)
if eomonth and frequency.days < AllFrequencies.M.days: if eomonth and frequency.days < AllFrequencies.M.days:
@ -43,7 +75,40 @@ def create_date_series(
class TimeSeries(TimeSeriesCore): class TimeSeries(TimeSeriesCore):
"""Container for TimeSeries objects""" """1-Dimensional Time Series object
Parameters
----------
data : List[Iterable] | Mapping
Time Series data in the form of list of tuples.
The first element of each tuple should be a date and second element should be a value.
The following types of objects can be passed to create a TimeSeries object:
* List of tuples containing date & value
* List of lists containing date & value
* List of dictionaries containing key: value pair of date and value
* List of dictionaries with 2 keys, first representing date & second representing value
* Dictionary of key: value pairs
date_format : str, optional, default "%Y-%m-%d"
Specify the format of the date
Required only if the first argument of tuples is a string. Otherwise ignored.
frequency : str, optional, default "infer"
The frequency of the time series. Default is infer.
The class will try to infer the frequency automatically and adjust to the closest member.
Note that inferring frequencies can fail if the data is too irregular.
Valid values are {D, W, M, Q, H, Y}
"""
def __init__(
self,
data: Union[List[Iterable], Mapping],
frequency: Literal["D", "W", "M", "Q", "H", "Y"],
date_format: str = "%Y-%m-%d",
):
"""Instantiate a TimeSeriesCore object"""
super().__init__(data, frequency, date_format)
def info(self): def info(self):
"""Summary info about the TimeSeries object""" """Summary info about the TimeSeries object"""
@ -154,6 +219,12 @@ class TimeSeries(TimeSeriesCore):
The mode of matching the closest date. The mode of matching the closest date.
Valid values are 'exact', 'previous', 'next' and next. Valid values are 'exact', 'previous', 'next' and next.
closest_max_days: int, default -1
The maximum acceptable gap between the provided date arguments and actual date.
Pass -1 for no limit.
Note: There's a hard max limit of 1000 days due to Python's limits on recursion.
This can be overridden by importing the sys module.
if_not_found : 'fail' | 'nan' if_not_found : 'fail' | 'nan'
What to do when required date is not found: What to do when required date is not found:
* fail: Raise a ValueError * fail: Raise a ValueError
@ -173,12 +244,6 @@ class TimeSeries(TimeSeriesCore):
Should be passed as a datetime library compatible string. Should be passed as a datetime library compatible string.
Sets the date format only for this operation. To set it globally, use FincalOptions.date_format Sets the date format only for this operation. To set it globally, use FincalOptions.date_format
closest_max_days: int, default -1
The maximum acceptable gap between the provided date arguments and actual date.
Pass -1 for no limit.
Note: There's a hard max limit of 1000 days due to Python's limits on recursion.
This can be overridden by importing the sys module.
Returns Returns
------- -------
A tuple containing the date and float value of the returns. A tuple containing the date and float value of the returns.
@ -192,6 +257,7 @@ class TimeSeries(TimeSeriesCore):
Example Example
-------- --------
>>> calculate_returns(datetime.date(2020, 1, 1), years=1) >>> calculate_returns(datetime.date(2020, 1, 1), years=1)
(datetime.datetime(2020, 1, 1, 0, 0), .0567)
""" """
as_on = _parse_date(as_on, date_format) as_on = _parse_date(as_on, date_format)
@ -214,17 +280,81 @@ class TimeSeries(TimeSeriesCore):
self, self,
from_date: Union[datetime.date, str], from_date: Union[datetime.date, str],
to_date: Union[datetime.date, str], to_date: Union[datetime.date, str],
frequency: str = None, frequency: Literal["D", "W", "M", "Q", "H", "Y"] = None,
as_on_match: str = "closest", as_on_match: str = "closest",
prior_match: str = "closest", prior_match: str = "closest",
closest: str = "previous", closest: Literal["previous", "next", "exact"] = "previous",
if_not_found: Literal["fail", "nan"] = "fail", if_not_found: Literal["fail", "nan"] = "fail",
compounding: bool = True, compounding: bool = True,
interval_type: Literal["years", "months", "days"] = "years", interval_type: Literal["years", "months", "days"] = "years",
interval_value: int = 1, interval_value: int = 1,
date_format: str = None, date_format: str = None,
) -> List[tuple]: ) -> TimeSeries:
"""Calculates the rolling return""" """Calculate the returns on a rolling basis.
This is a wrapper function around the calculate_returns function.
Parameters
----------
from_date : datetime.date | str
Start date for the return calculation.
to_date : datetime.date | str
End date for the returns calculation.
frequency : str, optional
Frequency at which the returns should be calcualated.
Valid values are {D, W, M, Q, H, Y}
as_on_match : str, optional
The match mode to be used for the as on date.
If not specified, the value for the closes parameter will be used.
prior_match : str, optional
The match mode to be used for the prior date, i.e., the date against which the return will be calculated.
If not specified, the value for the closes parameter will be used.
closest : previous | next | exact
The default match mode for dates.
* Previous: look for the immediate previous available date
* Next: look for the immediate next available date
* Exact: Only look for the exact date passed in the input
if_not_found : fail | nan
Specifies what should be done if the date is not found.
* fail: raise a DateNotFoundError.
* nan: return nan as the value.
Note, this will return float('NaN') and not 'nan' as string.
Note, this function will always raise an error if it is not possible to find a matching date.`
For instance, if the input date is before the starting of the first date of the time series,
but match mode is set to previous. A DateOutOfRangeError will be raised in such cases.
compounding : bool, optional
Should the returns be compounded annually.
interval_type : years | month | days
The interval for the return calculation.
interval_value : int, optional
The value of the interval for return calculation.
date_format : str, optional
A datetime library compatible format string.
If not specified, will use the setting in FincalOptions.date_format.
Returns
-------
Returs the rolling returns as a TimeSeries object.
Raises
------
ValueError
- If an invalid argument is passed for frequency parameter.
See also
--------
TimeSeries.calculate_returns
"""
from_date = _parse_date(from_date, date_format) from_date = _parse_date(from_date, date_format)
to_date = _parse_date(to_date, date_format) to_date = _parse_date(to_date, date_format)