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

consolidate trailing window functions #1809

Merged
merged 4 commits into from
Jun 8, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
34 changes: 34 additions & 0 deletions ibis/expr/tests/test_window_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np
import pytest

import ibis
from ibis.expr.window import _determine_how
from ibis.tests.util import assert_equal


Expand Down Expand Up @@ -174,3 +176,35 @@ def test_preceding_following_validate(alltypes):
@pytest.mark.xfail(raises=AssertionError, reason='NYT')
def test_window_equals(alltypes):
assert False


def test_determine_how():
how = _determine_how((None, 5))
assert how == 'rows'

how = _determine_how((3, 1))
assert how == 'rows'

how = _determine_how(5)
assert how == 'rows'

how = _determine_how(np.int64(7))
assert how == 'rows'

how = _determine_how(ibis.interval(days=3))
assert how == 'range'

how = _determine_how(ibis.interval(months=5) + ibis.interval(days=10))
assert how == 'range'

with pytest.raises(TypeError):
_determine_how(8.9)

with pytest.raises(TypeError):
_determine_how('invalid preceding')

with pytest.raises(TypeError):
_determine_how({'start': 1, 'end': 2})

with pytest.raises(TypeError):
_determine_how([3, 5])
41 changes: 37 additions & 4 deletions ibis/expr/window.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Encapsulation of SQL window clauses."""

import numpy as np

import ibis.common as com
import ibis.expr.operations as ops
import ibis.expr.types as ir
Expand All @@ -10,6 +12,29 @@ def _sequence_to_tuple(x):
return tuple(x) if util.is_iterable(x) else x


def _determine_how(preceding):
if isinstance(preceding, tuple):
start, end = preceding
if start is None:
offset_type = type(end)
else:
offset_type = type(start)
else:
offset_type = type(preceding)

if issubclass(offset_type, (int, np.integer)):
how = 'rows'
elif issubclass(offset_type, ir.IntervalScalar):
cpcloud marked this conversation as resolved.
Show resolved Hide resolved
how = 'range'
else:
raise TypeError(
'Type {} is not supported for row- or range- based trailing '
'window operations'.format(offset_type)
)

return how


class Window:
"""Class to encapsulate the details of a window frame.

Expand Down Expand Up @@ -310,13 +335,16 @@ def cumulative_window(group_by=None, order_by=None):
)


def trailing_window(rows, group_by=None, order_by=None):
def trailing_window(preceding, group_by=None, order_by=None):
"""Create a trailing window for use with aggregate window functions.

Parameters
----------
rows : int
Number of trailing rows to include. 0 includes only the current row
preceding : int, float or expression of intervals, i.e.
ibis.interval(days=1) + ibis.interval(hours=5)
Int indicates number of trailing rows to include;
0 includes only the current row.
Interval indicates a trailing range window.
group_by : expressions, default None
Either specify here or with TableExpr.group_by
order_by : expressions, default None
Expand All @@ -328,8 +356,13 @@ def trailing_window(rows, group_by=None, order_by=None):
Window

"""
how = _determine_how(preceding)
return Window(
preceding=rows, following=0, group_by=group_by, order_by=order_by
preceding=preceding,
following=0,
group_by=group_by,
order_by=order_by,
how=how
)


Expand Down