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

Add struct accessor to dask-cudf #8874

Merged
merged 25 commits into from
Aug 24, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fd6625d
allocate correct bufer for children
shaneding Jul 7, 2021
f50b129
update child buffer creation
shaneding Jul 7, 2021
9175e5e
added test cases
shaneding Jul 7, 2021
be44b91
remove comment
shaneding Jul 8, 2021
b6c2410
Implemented StructMethods struct accessor
Jul 19, 2021
c3912b6
Fixed example to use dask_cudf in field method comments
Jul 19, 2021
394b8e7
Partial fix for incorrect metadata in struct accessor field method
Jul 20, 2021
42eec5b
Merge remote-tracking branch 'origin/branch-21.08' into struct-accessor
Jul 20, 2021
2e41f40
Added better handling of key types in struct field() accessor method
Jul 21, 2021
10f8a85
Fixed KeyError handling for field() method in Struct Accessor
Jul 22, 2021
5bff6cb
Added testing of struct series creation and struct.field() method
Jul 27, 2021
3bc0214
Modified testing of accessor field method
Jul 27, 2021
9f88244
Modified struct accessor testing further
Jul 27, 2021
b80097c
Update python/dask_cudf/dask_cudf/tests/test_accessor.py
NV-jpt Jul 28, 2021
7fcd16d
Moved return statement out of try/catch block
Jul 28, 2021
dec19f2
Merge branch 'struct-accessor' of https://github.com/NV-jpt/cudf into…
Jul 28, 2021
5c90ba9
Merge remote-tracking branch 'parent/branch-21.10' into struct-accessor
Jul 28, 2021
cde7808
Removed test case with nonexistent field - need to design another tes…
Jul 29, 2021
d30afbd
Implemented some suggestions from Rick Zamora; thank you, Rickgit status
Jul 29, 2021
99a18d5
Added better error handling and tests
Aug 9, 2021
1a8270f
Consolidated try-except blocks into single block, and implemented app…
Aug 13, 2021
c63e5c9
Merge remote-tracking branch 'parent/branch-21.10' into struct-accessor
Aug 18, 2021
bb52b2c
Allow errors from invalid field keys to be thrown from cudf, instead …
Aug 18, 2021
c9af377
Cleaned up tests through removal of repeat definitions of struct data…
Aug 18, 2021
b552833
Removal of commented-out import statement
Aug 18, 2021
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
56 changes: 56 additions & 0 deletions python/dask_cudf/dask_cudf/accessors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,62 @@
# Copyright (c) 2021, NVIDIA CORPORATION.


# from cudf.python.dask_cudf.dask_cudf.core import Index
NV-jpt marked this conversation as resolved.
Show resolved Hide resolved


class StructMethods:
def __init__(self, d_series):
self.d_series = d_series

def field(self, key):
"""
Extract children of the specified struct column
in the Series
Parameters
----------
key: int or str
index/position or field name of the respective
struct column
Returns
-------
Series
Examples
--------
>>> s = cudf.Series([{'a': 1, 'b': 2}, {'a': 3, 'b': 4}])
>>> ds = dask_cudf.from_cudf(s, 2)
>>> ds.struct.field(0).compute()
0 1
1 3
dtype: int64
>>> ds.struct.field('a').compute()
0 1
1 3
dtype: int64
"""
try:
NV-jpt marked this conversation as resolved.
Show resolved Hide resolved
typ = self.d_series._meta.dtype.fields[key]

except KeyError:
try:
if isinstance(key, int):
typ = self.d_series._meta.dtype.fields[
list(self.d_series._meta.dtype.fields)[key]
]
else:
raise KeyError(
f"Field '{key}' is not in the set of existing keys"
)
except TypeError:
raise IndexError(
f"Index '{key}' is greater than the number of fields"
)

return self.d_series.map_partitions(
lambda s: s.struct.field(key),
meta=self.d_series._meta._constructor([], dtype=typ),
)


class ListMethods:
def __init__(self, d_series):
self.d_series = d_series
Expand Down
6 changes: 5 additions & 1 deletion python/dask_cudf/dask_cudf/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from cudf import _lib as libcudf

from dask_cudf import sorting
from dask_cudf.accessors import ListMethods
from dask_cudf.accessors import ListMethods, StructMethods

DASK_VERSION = LooseVersion(dask.__version__)

Expand Down Expand Up @@ -414,6 +414,10 @@ def groupby(self, *args, **kwargs):
def list(self):
return ListMethods(self)

@property
def struct(self):
return StructMethods(self)


class Index(Series, dd.core.Index):
_partition_type = cudf.Index
Expand Down
85 changes: 85 additions & 0 deletions python/dask_cudf/dask_cudf/tests/test_accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,88 @@ def test_sorting(data, ascending, na_position, ignore_index):
.reset_index(drop=True)
)
assert_eq(expect, got)

rjzamora marked this conversation as resolved.
Show resolved Hide resolved

#############################################################################
# Struct Accessor #
#############################################################################


@pytest.mark.parametrize(
"data",
[
[{"a": 5, "b": 10}, {"a": 3, "b": 7}, {"a": -3, "b": 11}],
[{"a": None, "b": 1}, {"a": None, "b": 0}, {"a": -3, "b": None}],
[{"a": 1, "b": 2}],
[{"b": 3, "c": 4}],
],
NV-jpt marked this conversation as resolved.
Show resolved Hide resolved
)
def test_create_struct_series(data):
expect = pd.Series(data)
ds_got = dgd.from_cudf(Series(data), 2)
assert_eq(expect, ds_got.compute())


@pytest.mark.parametrize(
"data",
[
[{"a": 5, "b": 10}, {"a": 3, "b": 7}, {"a": -3, "b": 11}],
[{"a": None, "b": 1}, {"a": None, "b": 0}, {"a": -3, "b": None}],
[{"a": 1, "b": 2}],
],
)
def test_struct_field_str(data):
for test_key in ["a", "b"]:
expect = Series(data).struct.field(test_key)
ds_got = dgd.from_cudf(Series(data), 2).struct.field(test_key)
assert_eq(expect, ds_got.compute())

rjzamora marked this conversation as resolved.
Show resolved Hide resolved

@pytest.mark.parametrize(
"data",
[
[{"a": 5, "b": 10}, {"a": 3, "b": 7}, {"a": -3, "b": 11}],
[{"a": None, "b": 1}, {"a": None, "b": 0}, {"a": -3, "b": None}],
[{"a": 1, "b": 2}],
[{"b": 3, "c": 4}],
],
)
def test_struct_field_integer(data):
for test_key in [0, 1]:
expect = Series(data).struct.field(test_key)
ds_got = dgd.from_cudf(Series(data), 2).struct.field(test_key)
assert_eq(expect, ds_got.compute())


@pytest.mark.parametrize(
"data",
[
[{"a": 5, "b": 10}, {"a": 3, "b": 7}, {"a": -3, "b": 11}],
[{"a": None, "b": 1}, {"a": None, "b": 0}, {"a": -3, "b": None}],
[{"a": 1, "b": 2}],
[{"b": 3, "c": 4}],
],
)
def test_dask_struct_field_Key_Error(data):
got = dgd.from_cudf(Series(data), 2)

# import pdb; pdb.set_trace()
with pytest.raises(KeyError):
got.struct.field("notakey").compute()


@pytest.mark.parametrize(
"data",
[
[{"a": 5, "b": 10}, {"a": 3, "b": 7}, {"a": -3, "b": 11}],
[{"a": None, "b": 1}, {"a": None, "b": 0}, {"a": -3, "b": None}],
[{"a": 1, "b": 2}],
[{"b": 3, "c": 4}],
],
)
def test_dask_struct_field_Int_Error(data):
# breakpoint()
got = dgd.from_cudf(Series(data), 2)

with pytest.raises(IndexError):
got.struct.field(1000).compute()