Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aggregate #97

Merged
merged 10 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/savefig/fig_hedge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/savefig/fig_offtake.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions docs/specialized_topics/dataprep.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ Don't worry if our data does not yet have the timezone we want to use in our app
Frequency
---------

The index must have a frequency (``fr.index.freq``); it must be one of the ones in ``portfolyo.FREQUENCIES``. The following abbreviations are used by ``pandas`` and throughout this package:
The index must have a frequency (``fr.index.freq``) and it must be a valid frequency. To check validity of the frequency, one may use ``portfolyo.assert_freq_valid()``. The following abbreviations are used by ``pandas`` and throughout this package:

* ``15T``: quarterhourly;
* ``H``: hourly;
* ``D``: daily;
* ``MS``: monthly;
* ``QS``: quarterly;
* ``AS``: yearly.
* ``QS``: quarterly. Also allowed ``QS-FEB``, ``QS-MAR``, etc.;
* ``AS``: yearly. Also allowed ``AS-FEB``, ``AS-MAR``, etc.

If the frequency is not set, we can try to make pandas infer it:

Expand Down Expand Up @@ -155,4 +155,4 @@ This function also tries to localize ``fr`` if it is not timezone-aware, and the

.. rubric:: Footnotes

.. [#f1] However, there is no harm in doing the localization to the target timezone if it is possible. In specific situations, localization is not possible (if we (a) have (quarter)hourly values that we (b) want to localize to a timezone with daylight-savings-time such as "Europe/Berlin" and (c) the moment of the DST-transition is included in the input data) and ``fr.tz_localize()`` raises a ``NonExistentTimeError`` or a ``AmbiguousTimeError``.
.. [#f1] However, there is no harm in doing the localization to the target timezone if it is possible. In specific situations, localization is not possible (if we (a) have (quarter)hourly values that we (b) want to localize to a timezone with daylight-savings-time such as "Europe/Berlin" and (c) the moment of the DST-transition is included in the input data) and ``fr.tz_localize()`` raises a ``NonExistentTimeError`` or a ``AmbiguousTimeError``.
24 changes: 2 additions & 22 deletions docs/tutorial/part1.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -240,27 +240,7 @@
"source": [
"(``portfolyo`` ensures that the values are aggregated correctly. In this case, the price (``p``) values are weighted-averaged (weighted with the duration of each datapoint - in this case a uniform 24h). See [Resampling](../specialized_topics/resampling.rst) for more information.)\n",
"\n",
"The argument ``\"QS\"`` specifies that we want quarterly values. The allowed values, in decreasing duration, are in the ``pf.FREQUENCIES`` constant (``\"15T\"`` means quarterhourly):"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['AS', 'QS', 'MS', 'D', 'H', '15T']"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pf.FREQUENCIES"
"The argument ``\"QS\"`` specifies that we want quarterly values starting from January (same as ``\"QS-JAN\"``). The allowed values, in increasing duration, are following: ``\"15T\"`` (=quarterhourly), ``\"H\"`` (=hourly), ``\"D\"`` (=daily), ``\"MS\"`` (=monthly), ``\"QS\"`` (=quarterly, or ``\"QS-FEB\"``, ``\"QS-MAR\"``, etc.), or ``\"AS\"`` (=yearly, or ``\"AS-FEB\"``, ``\"AS-MAR\"``, etc.).\n"
]
},
{
Expand Down Expand Up @@ -809,7 +789,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.13"
"version": "3.10.14"
},
"orig_nbformat": 4,
"vscode": {
Expand Down
2 changes: 1 addition & 1 deletion portfolyo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from .tools.changefreq import averagable as asfreq_avg
from .tools.changefreq import summable as asfreq_sum
from .tools.changeyear import characterize_index, map_frame_to_year
from .tools.freq import FREQUENCIES
from .tools.freq import assert_freq_valid
from .tools.hedge import hedge
from .tools.peakfn import PeakFunction
from .tools.peakfn import factory as create_peakfn
Expand Down
9 changes: 5 additions & 4 deletions portfolyo/core/commodity/commodity.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ class Commodity:
offset_hours: int = 0

def __post_init__(self):
if self.freq not in (freqs := tools.freq.FREQUENCIES):
raise ValueError(
f"``freq`` must be one of {', '.join(freqs)}; got {self.freq}."
)
# if self.freq not in (freqs := tools.freq.FREQUENCIES):
# raise ValueError(
# f"``freq`` must be one of {', '.join(freqs)}; got {self.freq}."
# )
tools.freq.assert_freq_valid(self.freq)


power = Commodity(
Expand Down
4 changes: 2 additions & 2 deletions portfolyo/core/pfline/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
def assert_longest_allowed_freq(freq):
def decorator(fn):
def wrapped(self, *args, **kwargs):
if tools.freq.up_or_down(self.index.freq, freq) == 1:
if tools.freq.up_or_down2(self.index.freq, freq) == 1:
raise ValueError(
"The frequency of the index is too long; longest allowed:"
f" {freq}; passed: {self.index.freq}."
Expand All @@ -21,7 +21,7 @@ def wrapped(self, *args, **kwargs):
def assert_shortest_allowed_freq(freq):
def decorator(fn):
def wrapped(self, *args, **kwargs):
if tools.freq.up_or_down(self.index.freq, freq) == -1:
if tools.freq.up_or_down2(self.index.freq, freq) == -1:
raise ValueError(
"The frequency of the index is too short; shortest allowed:"
f" {freq}; passed: {self.index.freq}."
Expand Down
6 changes: 3 additions & 3 deletions portfolyo/dev/develop.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ def get_index(
if not startdate:
a, m, d = 2016, 1, 1 # earliest possible
a += np.random.randint(0, 8) if _seed else (periods % 8)
if tools.freq.up_or_down(freq, "MS") <= 0:
if tools.freq.up_or_down2(freq, "MS") <= 0:
m += np.random.randint(0, 12) if _seed else (periods % 12)
if tools.freq.up_or_down(freq, "D") <= 0:
if tools.freq.up_or_down2(freq, "D") <= 0:
d += np.random.randint(0, 28) if _seed else (periods % 28)
startdate = f"{a}-{m}-{d}"
if not start_of_day:
Expand All @@ -48,7 +48,7 @@ def get_index(
start = tools.stamp.create(startdate, tz, start_of_day)
i = pd.date_range(start, periods=periods, freq=freq) # tz included in start
# Some checks.
if tools.freq.up_or_down(freq, "H") <= 0:
if tools.freq.up_or_down2(freq, "H") <= 0:
i = _shorten_index_if_necessary(i, start_of_day)
return i

Expand Down
2 changes: 1 addition & 1 deletion portfolyo/tools/ceil.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def stamp(
----------
ts : pd.Timestamp
Timestamp to ceil.
freq : {{{', '.join(tools_freq.FREQUENCIES)}}}
freq : {{{tools_freq.ALLOWED_FREQUENCIES_DOCS}}}
Frequency for which to ceil the timestamp.
future : int, optional (default: 0)
0 to ceil to current period. 1 (-1) to round to period after (before) that, etc.
Expand Down
20 changes: 10 additions & 10 deletions portfolyo/tools/changefreq.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ def _downsample_summable(s: pd.Series, freq: str) -> pd.Series:
return _emptyseries(s, freq)

offset = tools_startofday.get(s.index, "timedelta")
source_vs_daily = tools_freq.up_or_down(s.index.freq, "D")
target_vs_daily = tools_freq.up_or_down(freq, "D")
source_vs_daily = tools_freq.up_or_down2(s.index.freq, "D")
target_vs_daily = tools_freq.up_or_down2(freq, "D")

# We cannot simply `.resample()`, e.g. from hourly to monthly, because in that
# case the start-of-day is lost. We need to do it in two steps.
Expand Down Expand Up @@ -89,8 +89,8 @@ def _upsample_avgable(s: pd.Series, freq: str) -> pd.Series:
return _emptyseries(s, freq)

offset = tools_startofday.get(s.index, "timedelta")
source_vs_daily = tools_freq.up_or_down(s.index.freq, "D")
target_vs_daily = tools_freq.up_or_down(freq, "D")
source_vs_daily = tools_freq.up_or_down2(s.index.freq, "D")
target_vs_daily = tools_freq.up_or_down2(freq, "D")

# Several isuses with pandas resampling:

Expand Down Expand Up @@ -127,7 +127,7 @@ def _general(is_summable: bool, s: pd.Series, freq: str = "MS") -> pd.Series:
True if data is summable, False if it is averagable.
s : pd.Series
Series that needs to be resampled.
freq : {{{', '.join(tools_freq.FREQUENCIES)}}}, optional (default: 'MS')
freq : {{{tools_freq.ALLOWED_FREQUENCIES_DOCS}}}, optional (default: 'MS')
Target frequency.

Returns
Expand All @@ -144,7 +144,7 @@ def _general(is_summable: bool, s: pd.Series, freq: str = "MS") -> pd.Series:

# s is now a Series with a 'float' or 'pint' dtype.

up_or_down = tools_freq.up_or_down(s.index.freq, freq)
up_or_down = tools_freq.up_or_down2(s.index.freq, freq)

# Nothing more needed; portfolio already in desired frequency.
if up_or_down == 0:
Expand Down Expand Up @@ -172,14 +172,14 @@ def index(i: pd.DatetimeIndex, freq: str = "MS") -> pd.DatetimeIndex:
----------
i : pd.DatetimeIndex
Index to resample.
freq : {{{', '.join(tools_freq.FREQUENCIES)}}}
freq : {{{tools_freq.ALLOWED_FREQUENCIES_DOCS}}}
Target frequency.

Returns
-------
pd.DatetimeIndex
"""
up_or_down = tools_freq.up_or_down(i.freq, freq)
up_or_down = tools_freq.up_or_down2(i.freq, freq)

# Nothing more needed; index already in desired frequency.
if up_or_down == 0:
Expand All @@ -203,7 +203,7 @@ def summable(fr: Series_or_DataFrame, freq: str = "MS") -> Series_or_DataFrame:
----------
fr : Series or DataFrame
Pandas Series or DataFrame to be resampled.
freq : {{{', '.join(tools_freq.FREQUENCIES)}}}, optional (default: 'MS')
freq : {{{tools_freq.ALLOWED_FREQUENCIES_DOCS}}}, optional (default: 'MS')
Target frequency.

Returns
Expand Down Expand Up @@ -239,7 +239,7 @@ def averagable(fr: Series_or_DataFrame, freq: str = "MS") -> Series_or_DataFrame
----------
fr : Series or DataFrame
Pandas Series or DataFrame to be resampled.
freq : {{{', '.join(tools_freq.FREQUENCIES)}}}, optional (default: 'MS')
freq : {{{tools_freq.ALLOWED_FREQUENCIES_DOCS}}}, optional (default: 'MS')
Target frequency.

Returns
Expand Down
2 changes: 1 addition & 1 deletion portfolyo/tools/duration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def stamp(ts: pd.Timestamp, freq: str) -> tools_unit.Q_:
----------
ts : pd.Timestamp
Timestamp for which to calculate the duration.
freq : {{{', '.join(tools_freq.FREQUENCIES)}}}
freq : {{{tools_freq.ALLOWED_FREQUENCIES_DOCS}}}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we only need single (not tripple) braces

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Frequency to use in determining the duration.

Returns
Expand Down
2 changes: 1 addition & 1 deletion portfolyo/tools/floor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def stamp(
----------
ts : pd.Timestamp
Timestamp to floor.
freq : {{{', '.join(tools_freq.FREQUENCIES)}}}
freq : {{{tools_freq.ALLOWED_FREQUENCIES_DOCS}}}
Frequency for which to floor the timestamp.
future : int, optional (default: 0)
0 to floor to current period. 1 (-1) to round to period after (before) that, etc.
Expand Down
Loading
Loading