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

deps: update to ibis-framework 9.x and newer sqlglot #827

Merged
merged 86 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
b4e6a50
deps: update to ibis-framework 9.x and newer sqlglot
tswast Jul 8, 2024
f1ce09d
update sqlglot and ibis
tswast Jul 8, 2024
3c0cae0
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 8, 2024
d224a52
bump minimum pandas
tswast Jul 8, 2024
28b6a31
bump pyarrow
tswast Jul 8, 2024
bb68b2b
fix bfill and ffill
tswast Jul 9, 2024
05550ac
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 9, 2024
d5622b6
nearly implement describe
tswast Jul 9, 2024
3596edd
remove remaining reference to vendored_ibis_ops.ApproximateMultiQuantile
tswast Jul 9, 2024
32c2ab6
support ToJsonString
tswast Jul 9, 2024
5e0c1e7
partial support for quantile
tswast Jul 9, 2024
d877261
fix inmemorytable
tswast Jul 12, 2024
3d63801
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 12, 2024
f8d2864
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 15, 2024
847f459
fixed Series.explode
tswast Jul 15, 2024
6a0bedc
nearly fix to_datetime
tswast Jul 15, 2024
9d56ee7
remove tests I added
tswast Jul 15, 2024
fc84cb8
patch for python 3.9 support
tswast Jul 16, 2024
bb2408f
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 16, 2024
de32335
fix unit tests
tswast Jul 16, 2024
e7dd60f
fix explode with time type
tswast Jul 17, 2024
8672495
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 17, 2024
016a203
fix array_agg
tswast Jul 17, 2024
00163fe
fix array_agg for asc order
tswast Jul 17, 2024
129cfae
actually fix array_agg
tswast Jul 17, 2024
fdfc503
fix remote function
tswast Jul 17, 2024
6ac497a
fix in-memory nullable integer compilation
tswast Jul 18, 2024
7c68e17
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 18, 2024
bbb7615
fix test_df_construct_pandas_default on Python 3.9
tswast Jul 18, 2024
a4c49cd
fix ShiftOp windows
tswast Jul 19, 2024
11318e8
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 19, 2024
c0a5a5a
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Jul 24, 2024
616c99f
fix inf to SQL by treating values as literal in in memory table
tswast Jul 24, 2024
f977cff
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Aug 2, 2024
773d9e1
Merge remote-tracking branch 'origin/main' into b350749011-ibis-9
tswast Aug 7, 2024
f266e34
fix unit tests for ibis-framework 9.2.0
tswast Aug 7, 2024
7a03585
fix Python 3.10 unit tests by syncing deps
tswast Aug 7, 2024
e1c7f5e
Merge branch 'main' into b350749011-ibis-9
chelsea-lin Aug 8, 2024
f3d6fed
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Aug 13, 2024
387fbd9
fixing remote function after merge
chelsea-lin Aug 13, 2024
502eb16
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Aug 16, 2024
232f2f9
fix visit_NonNullLiteral for int types
chelsea-lin Aug 17, 2024
b9d6826
visit_WindowFunction to fix s.median() method
chelsea-lin Aug 19, 2024
79c8f68
fix lint
chelsea-lin Aug 19, 2024
a44a745
fix s.diff with window
chelsea-lin Aug 20, 2024
d7089ca
fix mypy
chelsea-lin Aug 20, 2024
133e053
patch visit_And to fix is_monotonic methods
chelsea-lin Aug 20, 2024
ded2334
fix mypy and fillna warning
chelsea-lin Aug 21, 2024
2edaa9d
undo window changes for test_series_autocorr
chelsea-lin Aug 21, 2024
8f6165f
undo fill_null because it was missed at 9.0 version
chelsea-lin Aug 21, 2024
da32b61
Merge branch 'main' into b350749011-ibis-9
tswast Aug 23, 2024
e423f89
vendor more of ibis for python 3.9 compatibility
tswast Aug 23, 2024
46c6e25
add default arg for nulls_first for python 3.9 support
tswast Aug 23, 2024
cedc6fc
restore integer conversion
tswast Aug 23, 2024
5016018
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Aug 28, 2024
16f7878
fix window tests: diff, duplicated, shift
chelsea-lin Aug 29, 2024
6be3a38
fixing ibis parenthesize_inputs bugs and related tests
chelsea-lin Aug 30, 2024
90cae7c
fixing lint
chelsea-lin Aug 30, 2024
82c0489
disable test_query_complexity_error
chelsea-lin Sep 3, 2024
1c3fead
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 3, 2024
b3f52c9
fix doctest np.int64(0) upgrades
chelsea-lin Sep 4, 2024
c77120e
fix doctest np.int64(0) upgrades more
chelsea-lin Sep 4, 2024
3d10584
fix groupby diff
chelsea-lin Sep 4, 2024
fe7aa81
addressing system-3.12/doctest issues related to numpy 2.1.1
chelsea-lin Sep 5, 2024
24aa3df
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 5, 2024
7ff77f3
fix test_df_apply_axis_1_complex
chelsea-lin Sep 5, 2024
db1e57f
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 5, 2024
5cc8597
Merge branch 'main' into b350749011-ibis-9
chelsea-lin Sep 6, 2024
dea7aed
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 10, 2024
00834c7
address compiler errors after merge
chelsea-lin Sep 10, 2024
e8e5e97
Merge branch 'b350749011-ibis-9' of github.com:googleapis/python-bigq…
chelsea-lin Sep 10, 2024
822180a
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Sep 10, 2024
6f054f4
fix unit-test compile errors
chelsea-lin Sep 10, 2024
c7167db
remove unused ibis codes
chelsea-lin Sep 10, 2024
555453b
fix fillna deprecated warning
chelsea-lin Sep 10, 2024
0762299
add _remove_null_ordering_from_unsupported_window back to fix test_pr…
chelsea-lin Sep 11, 2024
8d36966
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 11, 2024
4c90516
fix is_monotonic_decreasing test
chelsea-lin Sep 11, 2024
ad122af
Merge branch 'main' into b350749011-ibis-9
chelsea-lin Sep 11, 2024
ab84436
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 12, 2024
d425aa9
fix explode after merge
chelsea-lin Sep 12, 2024
fa46553
fix numpy on remote function test
chelsea-lin Sep 12, 2024
4b3fb0f
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 12, 2024
f175596
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Sep 12, 2024
f3a43b1
ml numpy sql generations
chelsea-lin Sep 13, 2024
8ef190f
Merge branch 'main' of github.com:googleapis/python-bigquery-datafram…
chelsea-lin Sep 13, 2024
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
7 changes: 3 additions & 4 deletions bigframes/core/block_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,9 @@ def value_counts(

def pct_change(block: blocks.Block, periods: int = 1) -> blocks.Block:
column_labels = block.column_labels
window_spec = windows.rows(
preceding=periods if periods > 0 else None,
following=-periods if periods < 0 else None,
)

# Window framing clause is not allowed for analytic function lag.
window_spec = windows.unbound()

original_columns = block.value_columns
block, shift_columns = block.multi_apply_window_op(
Expand Down
15 changes: 6 additions & 9 deletions bigframes/core/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
import bigframes.core.schema as bf_schema
import bigframes.core.sql as sql
import bigframes.core.utils as utils
import bigframes.core.window_spec as window_specs
import bigframes.core.window_spec as windows
import bigframes.dtypes
import bigframes.exceptions
import bigframes.features
Expand Down Expand Up @@ -900,7 +900,7 @@ def multi_apply_window_op(
self,
columns: typing.Sequence[str],
op: agg_ops.WindowOp,
window_spec: window_specs.WindowSpec,
window_spec: windows.WindowSpec,
*,
skip_null_groups: bool = False,
never_skip_nulls: bool = False,
Expand Down Expand Up @@ -959,7 +959,7 @@ def apply_window_op(
self,
column: str,
op: agg_ops.WindowOp,
window_spec: window_specs.WindowSpec,
window_spec: windows.WindowSpec,
*,
result_label: Label = None,
skip_null_groups: bool = False,
Expand Down Expand Up @@ -1475,7 +1475,7 @@ def grouped_head(
value_columns: typing.Sequence[str],
n: int,
):
window_spec = window_specs.cumulative_rows(grouping_keys=tuple(by_column_ids))
window_spec = windows.cumulative_rows(grouping_keys=tuple(by_column_ids))

block, result_id = self.apply_window_op(
value_columns[0],
Expand Down Expand Up @@ -2383,10 +2383,7 @@ def _is_monotonic(
return self._stats_cache[column_name][op_name]

period = 1
window = window_specs.rows(
preceding=period,
following=None,
)
window_spec = windows.rows()

# any NaN value means not monotonic
block, last_notna_id = self.apply_unary_op(column_ids[0], ops.notnull_op)
Expand All @@ -2402,7 +2399,7 @@ def _is_monotonic(
last_result_id = None
for column_id in column_ids[::-1]:
block, lag_result_id = block.apply_window_op(
column_id, agg_ops.ShiftOp(period), window
column_id, agg_ops.ShiftOp(period), window_spec
)
block, strict_monotonic_id = block.apply_binary_op(
column_id, lag_result_id, ops.gt_op if increasing else ops.lt_op
Expand Down
38 changes: 27 additions & 11 deletions bigframes/core/compile/aggregate_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
import functools
import typing
from typing import cast, Optional
from typing import cast, List, Optional

import bigframes_vendored.constants as constants
import bigframes_vendored.ibis.expr.operations as vendored_ibis_ops
Expand All @@ -31,6 +31,17 @@
scalar_compiler = scalar_compilers.scalar_op_compiler


# TODO(swast): We can remove this if ibis adds general approx_quantile
# See: https://github.com/ibis-project/ibis/issues/9541
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

FYI (no change necessary for this PR): An approximate quantile node type has been merged into ibis. ibis-project/ibis#9881

Once we start vendoring the Ibis expressions in addition to the compiler, we can take advantage of that (if we want).

@ibis.udf.agg.builtin
def approx_quantiles(expression: float, number) -> List[float]:
"""APPROX_QUANTILES

https://cloud.google.com/bigquery/docs/reference/standard-sql/approximate_aggregate_functions#approx_quantiles
"""
return [] # pragma: NO COVER


def compile_aggregate(
aggregate: ex.Aggregation,
bindings: typing.Dict[str, ibis_types.Value],
Expand Down Expand Up @@ -176,15 +187,12 @@ def _(
column: ibis_types.NumericColumn,
window=None,
) -> ibis_types.NumericValue:
# PERCENTILE_CONT has very few allowed windows. For example, "window
# framing clause is not allowed for analytic function percentile_cont".
# APPROX_QUANTILES has very few allowed windows.
if window is not None:
raise NotImplementedError(
f"Approx Quartiles with windowing is not supported. {constants.FEEDBACK_LINK}"
)
value = vendored_ibis_ops.ApproximateMultiQuantile(
column, num_bins=4 # type: ignore
).to_expr()[op.quartile]
value = approx_quantiles(column, 4)[op.quartile] # type: ignore
return cast(ibis_types.NumericValue, value)


Expand Down Expand Up @@ -513,11 +521,15 @@ def _(
column: ibis_types.Column,
window=None,
) -> ibis_types.BooleanValue:
# BQ will return null for empty column, result would be true in pandas.
result = _is_true(column).all()
# BQ will return null for empty column, result would be false in pandas.
result = _apply_window_if_present(_is_true(column).all(), window)
literal = ibis_types.literal(True)

return cast(
ibis_types.BooleanScalar,
_apply_window_if_present(result, window).fillna(ibis_types.literal(True)),
result.fill_null(literal)
if hasattr(result, "fill_null")
else result.fillna(literal),
)


Expand All @@ -528,10 +540,14 @@ def _(
window=None,
) -> ibis_types.BooleanValue:
# BQ will return null for empty column, result would be false in pandas.
result = _is_true(column).any()
result = _apply_window_if_present(_is_true(column).any(), window)
literal = ibis_types.literal(False)

return cast(
ibis_types.BooleanScalar,
_apply_window_if_present(result, window).fillna(ibis_types.literal(False)),
result.fill_null(literal)
if hasattr(result, "fill_null")
else result.fillna(literal),
)


Expand Down
55 changes: 21 additions & 34 deletions bigframes/core/compile/compiled.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
import typing
from typing import Collection, Literal, Optional, Sequence

import bigframes_vendored.ibis.expr.operations as vendored_ibis_ops
import bigframes_vendored.ibis.backends.bigquery.backend as ibis_bigquery
import google.cloud.bigquery
import ibis
import ibis.backends.bigquery as ibis_bigquery
import ibis.backends.bigquery.datatypes
import ibis.common.deferred # type: ignore
import ibis.expr.datatypes as ibis_dtypes
Expand Down Expand Up @@ -407,18 +406,13 @@ def explode(self, offsets: typing.Sequence[int]) -> UnorderedIR:

# The offset array ensures null represents empty arrays after unnesting.
offset_array_id = bigframes.core.guid.generate_guid("offset_array_")
offset_array = (
vendored_ibis_ops.GenerateArray(
ibis.greatest(
0,
ibis.least(
*[table[column_id].length() - 1 for column_id in column_ids]
),
)
)
.to_expr()
.name(offset_array_id),
)
offset_array = ibis.range(
0,
ibis.greatest(
1, # We always want at least 1 element to fill in NULLs for empty arrays.
ibis.least(*[table[column_id].length() for column_id in column_ids]),
),
).name(offset_array_id)
table_w_offset_array = table.select(
offset_array,
*self._column_names,
Expand Down Expand Up @@ -718,21 +712,13 @@ def explode(self, offsets: typing.Sequence[int]) -> OrderedIR:
column_ids = tuple(table.columns[offset] for offset in offsets)

offset_array_id = bigframes.core.guid.generate_guid("offset_array_")
offset_array = (
vendored_ibis_ops.GenerateArray(
ibis.greatest(
0,
ibis.least(
*[
table[table.columns[offset]].length() - 1
for offset in offsets
]
),
)
)
.to_expr()
.name(offset_array_id),
)
offset_array = ibis.range(
0,
ibis.greatest(
1, # We always want at least 1 element to fill in NULLs for empty arrays.
ibis.least(*[table[column_id].length() for column_id in column_ids]),
),
).name(offset_array_id)
table_w_offset_array = table.select(
offset_array,
*self._column_names,
Expand Down Expand Up @@ -870,7 +856,7 @@ def project_window_op(

clauses = []
if op.skips_nulls and not never_skip_nulls:
clauses.append((column.isnull(), ibis.NA))
clauses.append((column.isnull(), ibis.null()))
if window_spec.min_periods:
if op.skips_nulls:
# Most operations do not count NULL values towards min_periods
Expand All @@ -891,7 +877,7 @@ def project_window_op(
clauses.append(
(
observation_count < ibis_types.literal(window_spec.min_periods),
ibis.NA,
ibis.null(),
)
)
if clauses:
Expand Down Expand Up @@ -1322,9 +1308,10 @@ def _ibis_window_from_spec(
bounds.preceding, bounds.following, how="range"
)
if isinstance(bounds, RowsWindowBounds):
window = window.preceding_following(
bounds.preceding, bounds.following, how="rows"
)
if bounds.preceding is not None or bounds.following is not None:
window = window.preceding_following(
bounds.preceding, bounds.following, how="rows"
)
else:
raise ValueError(f"unrecognized window bounds {bounds}")
return window
Expand Down
7 changes: 6 additions & 1 deletion bigframes/core/compile/default_ordering.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ def _convert_to_nonnull_string(column: ibis_types.Column) -> ibis_types.StringVa
# Needed for JSON, STRUCT and ARRAY datatypes
result = vendored_ibis_ops.ToJsonString(column).to_expr() # type: ignore
# Escape backslashes and use backslash as delineator
escaped = cast(ibis_types.StringColumn, result.fillna("")).replace("\\", "\\\\") # type: ignore
escaped = cast(
ibis_types.StringColumn,
result.fill_null("") if hasattr(result, "fill_null") else result.fillna(""),
).replace(
"\\", "\\\\"
) # type: ignore
return cast(ibis_types.StringColumn, ibis.literal("\\")).concat(escaped)


Expand Down
17 changes: 13 additions & 4 deletions bigframes/core/compile/scalar_op_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ def isin_op_impl(x: ibis_types.Value, op: ops.IsInOp):
@scalar_op_compiler.register_unary_op(ops.ToDatetimeOp, pass_op=True)
def to_datetime_op_impl(x: ibis_types.Value, op: ops.ToDatetimeOp):
if x.type() == ibis_dtypes.str:
return vendored_ibis_ops.SafeCastToDatetime(x).to_expr()
return x.try_cast(ibis_dtypes.Timestamp(None))
else:
# Numerical inputs.
if op.format:
Expand Down Expand Up @@ -995,8 +995,14 @@ def eq_nulls_match_op(
y: ibis_types.Value,
):
"""Variant of eq_op where nulls match each other. Only use where dtypes are known to be same."""
left = x.cast(ibis_dtypes.str).fillna(ibis_types.literal("$NULL_SENTINEL$"))
right = y.cast(ibis_dtypes.str).fillna(ibis_types.literal("$NULL_SENTINEL$"))
literal = ibis_types.literal("$NULL_SENTINEL$")
if hasattr(x, "fill_null"):
Copy link
Contributor

Choose a reason for hiding this comment

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

What is causing the fillna/fill_nbull divide?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ibis 9.0.0 -> Ibis 9.1.0. We need Ibis 9.0.0 for Python 3.9 support, at least until we vendor the ibis expr APIs too, which we'll want to do sometime between now and bigframes 2.0.

left = x.cast(ibis_dtypes.str).fill_null(literal)
right = y.cast(ibis_dtypes.str).fill_null(literal)
else:
left = x.cast(ibis_dtypes.str).fillna(literal)
right = y.cast(ibis_dtypes.str).fillna(literal)

return left == right


Expand Down Expand Up @@ -1379,7 +1385,10 @@ def fillna_op(
x: ibis_types.Value,
y: ibis_types.Value,
):
return x.fillna(typing.cast(ibis_types.Scalar, y))
if hasattr(x, "fill_null"):
return x.fill_null(typing.cast(ibis_types.Scalar, y))
else:
return x.fillna(typing.cast(ibis_types.Scalar, y))


@scalar_op_compiler.register_binary_op(ops.round_op)
Expand Down
6 changes: 5 additions & 1 deletion bigframes/core/compile/single_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,8 @@ def value_to_join_key(value: ibis_types.Value):
"""Converts nullable values to non-null string SQL will not match null keys together - but pandas does."""
if not value.type().is_string():
value = value.cast(ibis_dtypes.str)
return value.fillna(ibis_types.literal("$NULL_SENTINEL$"))
return (
value.fill_null(ibis_types.literal("$NULL_SENTINEL$"))
if hasattr(value, "fill_null")
else value.fillna(ibis_types.literal("$NULL_SENTINEL$"))
)
Comment on lines +182 to +186
Copy link
Contributor

Choose a reason for hiding this comment

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

What is going on here? This seems fragile - what types no longer have fillna?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

fillna was renamed to fill_null in ibis-project/ibis#9300 (ibis 9.1.0)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, fillna was renamed to fill_null in Ibis 9.1.0. Using fillna with this version or later will trigger a deprecation warning. However, we cann't use fill_null because it's not available in Ibis 9.0.0. Since Ibis 9.1.0 and later dropped support for Python 3.9, we must use Ibis 9.0.0 to maintain compatibility with Python 3.9

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I do hate to have hasattr checks in our code. Should we just use fillna and suppress warnings?

13 changes: 4 additions & 9 deletions bigframes/core/groupby/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,19 +255,17 @@ def cumprod(self, *args, **kwargs) -> df.DataFrame:

@validations.requires_ordering()
def shift(self, periods=1) -> series.Series:
window = window_specs.rows(
# Window framing clause is not allowed for analytic function lag.
window = window_specs.unbound(
grouping_keys=tuple(self._by_col_ids),
preceding=periods if periods > 0 else None,
following=-periods if periods < 0 else None,
)
return self._apply_window_op(agg_ops.ShiftOp(periods), window=window)

@validations.requires_ordering()
def diff(self, periods=1) -> series.Series:
# Window framing clause is not allowed for analytic function lag.
window = window_specs.rows(
grouping_keys=tuple(self._by_col_ids),
preceding=periods if periods > 0 else None,
following=-periods if periods < 0 else None,
)
return self._apply_window_op(agg_ops.DiffOp(periods), window=window)

Expand Down Expand Up @@ -685,19 +683,16 @@ def cumcount(self, *args, **kwargs) -> series.Series:
@validations.requires_ordering()
def shift(self, periods=1) -> series.Series:
"""Shift index by desired number of periods."""
# Window framing clause is not allowed for analytic function lag.
window = window_specs.rows(
grouping_keys=tuple(self._by_col_ids),
preceding=periods if periods > 0 else None,
following=-periods if periods < 0 else None,
)
return self._apply_window_op(agg_ops.ShiftOp(periods), window=window)

@validations.requires_ordering()
def diff(self, periods=1) -> series.Series:
window = window_specs.rows(
grouping_keys=tuple(self._by_col_ids),
preceding=periods if periods > 0 else None,
following=-periods if periods < 0 else None,
)
return self._apply_window_op(agg_ops.DiffOp(periods), window=window)

Expand Down
1 change: 0 additions & 1 deletion bigframes/core/window_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ def rows(
Returns:
WindowSpec
"""
assert (preceding is not None) or (following is not None)
bounds = RowsWindowBounds(preceding=preceding, following=following)
return WindowSpec(
grouping_keys=grouping_keys,
Expand Down
Loading