Skip to content

Commit 8e8279d

Browse files
feat: define list accessor for bigframes Series (#946)
* feat: define list accessor for bigframes Series * Add doc for list accessor * Fix bug in docstring and inheritance * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Skip test if Pandas version is too old * Fix docstring format, and provide notebook examples. * Use func link under see also --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 3b35860 commit 8e8279d

File tree

8 files changed

+318
-42
lines changed

8 files changed

+318
-42
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import bigframes.operations as ops
16+
17+
18+
def convert_index(key: int) -> ops.ArrayIndexOp:
19+
if key < 0:
20+
raise NotImplementedError("Negative indexing is not supported.")
21+
return ops.ArrayIndexOp(index=key)
22+
23+
24+
def convert_slice(key: slice) -> ops.ArraySliceOp:
25+
if key.step is not None and key.step != 1:
26+
raise NotImplementedError(f"Only a step of 1 is allowed, got {key.step}")
27+
28+
if (key.start is not None and key.start < 0) or (
29+
key.stop is not None and key.stop < 0
30+
):
31+
raise NotImplementedError("Slicing with negative numbers is not allowed.")
32+
33+
return ops.ArraySliceOp(
34+
start=key.start if key.start is not None else 0,
35+
stop=key.stop,
36+
step=key.step,
37+
)

bigframes/operations/lists.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import annotations
16+
17+
import inspect
18+
from typing import Union
19+
20+
import bigframes_vendored.pandas.core.arrays.arrow.accessors as vendoracessors
21+
22+
from bigframes.core import log_adapter
23+
import bigframes.operations as ops
24+
from bigframes.operations._op_converters import convert_index, convert_slice
25+
import bigframes.operations.base
26+
import bigframes.series as series
27+
28+
29+
@log_adapter.class_logger
30+
class ListAccessor(
31+
bigframes.operations.base.SeriesMethods, vendoracessors.ListAccessor
32+
):
33+
__doc__ = vendoracessors.ListAccessor.__doc__
34+
35+
def len(self):
36+
return self._apply_unary_op(ops.len_op)
37+
38+
def __getitem__(self, key: Union[int, slice]) -> series.Series:
39+
if isinstance(key, int):
40+
return self._apply_unary_op(convert_index(key))
41+
elif isinstance(key, slice):
42+
return self._apply_unary_op(convert_slice(key))
43+
else:
44+
raise ValueError(f"key must be an int or slice, got {type(key).__name__}")
45+
46+
__getitem__.__doc__ = inspect.getdoc(vendoracessors.ListAccessor.__getitem__)

bigframes/operations/strings.py

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from bigframes.core import log_adapter
2424
import bigframes.dataframe as df
2525
import bigframes.operations as ops
26+
from bigframes.operations._op_converters import convert_index, convert_slice
2627
import bigframes.operations.base
2728
import bigframes.series as series
2829

@@ -40,28 +41,9 @@ class StringMethods(bigframes.operations.base.SeriesMethods, vendorstr.StringMet
4041

4142
def __getitem__(self, key: Union[int, slice]) -> series.Series:
4243
if isinstance(key, int):
43-
if key < 0:
44-
raise NotImplementedError("Negative indexing is not supported.")
45-
return self._apply_unary_op(ops.ArrayIndexOp(index=key))
44+
return self._apply_unary_op(convert_index(key))
4645
elif isinstance(key, slice):
47-
if key.step is not None and key.step != 1:
48-
raise NotImplementedError(
49-
f"Only a step of 1 is allowed, got {key.step}"
50-
)
51-
if (key.start is not None and key.start < 0) or (
52-
key.stop is not None and key.stop < 0
53-
):
54-
raise NotImplementedError(
55-
"Slicing with negative numbers is not allowed."
56-
)
57-
58-
return self._apply_unary_op(
59-
ops.ArraySliceOp(
60-
start=key.start if key.start is not None else 0,
61-
stop=key.stop,
62-
step=key.step,
63-
)
64-
)
46+
return self._apply_unary_op(convert_slice(key))
6547
else:
6648
raise ValueError(f"key must be an int or slice, got {type(key).__name__}")
6749

bigframes/series.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import bigframes.operations.aggregations as agg_ops
5454
import bigframes.operations.base
5555
import bigframes.operations.datetimes as dt
56+
import bigframes.operations.lists as lists
5657
import bigframes.operations.plotting as plotting
5758
import bigframes.operations.strings as strings
5859
import bigframes.operations.structs as structs
@@ -66,6 +67,8 @@
6667
" Try converting it to a remote function."
6768
)
6869

70+
_list = list # Type alias to escape Series.list property
71+
6972

7073
@log_adapter.class_logger
7174
class Series(bigframes.operations.base.SeriesMethods, vendored_pandas_series.Series):
@@ -161,6 +164,10 @@ def query_job(self) -> Optional[bigquery.QueryJob]:
161164
def struct(self) -> structs.StructAccessor:
162165
return structs.StructAccessor(self._block)
163166

167+
@property
168+
def list(self) -> lists.ListAccessor:
169+
return lists.ListAccessor(self._block)
170+
164171
@property
165172
@validations.requires_ordering()
166173
def T(self) -> Series:
@@ -1708,7 +1715,7 @@ def to_latex(
17081715
buf, columns=columns, header=header, index=index, **kwargs
17091716
)
17101717

1711-
def tolist(self) -> list:
1718+
def tolist(self) -> _list:
17121719
return self.to_pandas().to_list()
17131720

17141721
to_list = tolist

docs/reference/bigframes.pandas/series.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ String handling
3535
:inherited-members:
3636
:undoc-members:
3737

38+
List handling
39+
^^^^^^^^^^^^^
40+
41+
.. automodule:: bigframes.operations.lists
42+
:members:
43+
:inherited-members:
44+
:undoc-members:
45+
3846
Struct handling
3947
^^^^^^^^^^^^^^^
4048

0 commit comments

Comments
 (0)