Skip to content

CLN: Move rolling helper functions to where they are used #34269

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

Merged
merged 3 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
80 changes: 0 additions & 80 deletions pandas/core/window/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@

import numpy as np

from pandas.core.dtypes.common import is_integer
from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries

import pandas.core.common as com
from pandas.core.generic import _shared_docs
from pandas.core.groupby.base import GroupByMixin
from pandas.core.indexes.api import MultiIndex
Expand Down Expand Up @@ -224,75 +222,6 @@ def dataframe_from_int_dict(data, frame_template):
return _flex_binary_moment(arg2, arg1, f)


def _get_center_of_mass(comass, span, halflife, alpha):
valid_count = com.count_not_none(comass, span, halflife, alpha)
if valid_count > 1:
raise ValueError("comass, span, halflife, and alpha are mutually exclusive")

# Convert to center of mass; domain checks ensure 0 < alpha <= 1
if comass is not None:
if comass < 0:
raise ValueError("comass must satisfy: comass >= 0")
elif span is not None:
if span < 1:
raise ValueError("span must satisfy: span >= 1")
comass = (span - 1) / 2.0
elif halflife is not None:
if halflife <= 0:
raise ValueError("halflife must satisfy: halflife > 0")
decay = 1 - np.exp(np.log(0.5) / halflife)
comass = 1 / decay - 1
elif alpha is not None:
if alpha <= 0 or alpha > 1:
raise ValueError("alpha must satisfy: 0 < alpha <= 1")
comass = (1.0 - alpha) / alpha
else:
raise ValueError("Must pass one of comass, span, halflife, or alpha")

return float(comass)


def calculate_center_offset(window):
if not is_integer(window):
window = len(window)
return int((window - 1) / 2.0)


def calculate_min_periods(
window: int,
min_periods: Optional[int],
num_values: int,
required_min_periods: int,
floor: int,
) -> int:
"""
Calculates final minimum periods value for rolling aggregations.

Parameters
----------
window : passed window value
min_periods : passed min periods value
num_values : total number of values
required_min_periods : required min periods per aggregation function
floor : required min periods per aggregation function

Returns
-------
min_periods : int
"""
if min_periods is None:
min_periods = window
else:
min_periods = max(required_min_periods, min_periods)
if min_periods > window:
raise ValueError(f"min_periods {min_periods} must be <= window {window}")
elif min_periods > num_values:
min_periods = num_values + 1
elif min_periods < 0:
raise ValueError("min_periods must be >= 0")
return max(min_periods, floor)


def zsqrt(x):
with np.errstate(all="ignore"):
result = np.sqrt(x)
Expand All @@ -317,12 +246,3 @@ def prep_binary(arg1, arg2):
Y = arg2 + 0 * arg1

return X, Y


def get_weighted_roll_func(cfunc: Callable) -> Callable:
def func(arg, window, min_periods=None):
if min_periods is None:
min_periods = len(window)
return cfunc(arg, window, min_periods)

return func
36 changes: 30 additions & 6 deletions pandas/core/window/ewm.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@
from pandas.core.dtypes.generic import ABCDataFrame

from pandas.core.base import DataError
from pandas.core.window.common import (
_doc_template,
_get_center_of_mass,
_shared_docs,
zsqrt,
)
import pandas.core.common as com
from pandas.core.window.common import _doc_template, _shared_docs, zsqrt
from pandas.core.window.rolling import _flex_binary_moment, _Rolling

_bias_template = """
Expand All @@ -27,6 +23,34 @@
"""


def _get_center_of_mass(comass, span, halflife, alpha):
valid_count = com.count_not_none(comass, span, halflife, alpha)
if valid_count > 1:
raise ValueError("comass, span, halflife, and alpha are mutually exclusive")

# Convert to center of mass; domain checks ensure 0 < alpha <= 1
if comass is not None:
if comass < 0:
raise ValueError("comass must satisfy: comass >= 0")
elif span is not None:
if span < 1:
raise ValueError("span must satisfy: span >= 1")
comass = (span - 1) / 2.0
elif halflife is not None:
if halflife <= 0:
raise ValueError("halflife must satisfy: halflife > 0")
decay = 1 - np.exp(np.log(0.5) / halflife)
comass = 1 / decay - 1
elif alpha is not None:
if alpha <= 0 or alpha > 1:
raise ValueError("alpha must satisfy: 0 < alpha <= 1")
comass = (1.0 - alpha) / alpha
else:
raise ValueError("Must pass one of comass, span, halflife, or alpha")

return float(comass)


class EWM(_Rolling):
r"""
Provide exponential weighted (EW) functions.
Expand Down
78 changes: 75 additions & 3 deletions pandas/core/window/rolling.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@
_doc_template,
_flex_binary_moment,
_shared_docs,
calculate_center_offset,
calculate_min_periods,
get_weighted_roll_func,
zsqrt,
)
from pandas.core.window.indexers import (
Expand All @@ -59,6 +56,81 @@
from pandas.tseries.offsets import DateOffset


def calculate_center_offset(window) -> int:
"""
Calculates an offset necessary to have the window label to be centered
Copy link
Member

@MarcoGorelli MarcoGorelli May 20, 2020

Choose a reason for hiding this comment

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

Sorry to be pedantic :) But should this follow the docstring guide?

The short summary must start with a capital letter, end with a dot, and fit in a single line. It needs to express what the object does without providing details. For functions and methods, the short summary must start with an infinitive verb.

same for the calculate_min_periods docstring


Parameters
----------
window: ndarray or int
window weights or window

Returns
-------
int
"""
if not is_integer(window):
window = len(window)
return int((window - 1) / 2.0)


def calculate_min_periods(
window: int,
min_periods: Optional[int],
num_values: int,
required_min_periods: int,
floor: int,
) -> int:
"""
Calculates final minimum periods value for rolling aggregations.

Parameters
----------
window : passed window value
min_periods : passed min periods value
num_values : total number of values
required_min_periods : required min periods per aggregation function
floor : required min periods per aggregation function

Returns
-------
min_periods : int
"""
if min_periods is None:
min_periods = window
else:
min_periods = max(required_min_periods, min_periods)
if min_periods > window:
raise ValueError(f"min_periods {min_periods} must be <= window {window}")
elif min_periods > num_values:
min_periods = num_values + 1
elif min_periods < 0:
raise ValueError("min_periods must be >= 0")
return max(min_periods, floor)


def get_weighted_roll_func(cfunc: Callable) -> Callable:
"""
Wrap weighted rolling cython function with min periods argument

Parameters
----------
cfunc : function
Cython weighted rolling function

Returns
-------
function
"""

def func(arg, window, min_periods=None):
if min_periods is None:
min_periods = len(window)
return cfunc(arg, window, min_periods)

return func


class _Window(PandasObject, ShallowMixin, SelectionMixin):
_attributes: List[str] = [
"window",
Expand Down