Skip to content

Commit b034854

Browse files
authored
feat: add 'how=' to ak.unzip (#3655)
1 parent 0415602 commit b034854

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed

src/awkward/operations/ak_unzip.py

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@
1313

1414

1515
@high_level_function()
16-
def unzip(array, *, highlevel=True, behavior=None, attrs=None):
16+
def unzip(
17+
array,
18+
*,
19+
how=tuple,
20+
highlevel=True,
21+
behavior=None,
22+
attrs=None,
23+
):
1724
"""
1825
Args:
1926
array: Array-like data (anything #ak.to_layout recognizes).
27+
how (type): The type of the returned output. This can be `tuple` or `dict`.
2028
highlevel (bool): If True, return an #ak.Array; otherwise, return
2129
a low-level #ak.contents.Content subclass.
2230
behavior (None or dict): Custom #ak.behavior for the output array, if
@@ -25,10 +33,10 @@ def unzip(array, *, highlevel=True, behavior=None, attrs=None):
2533
high-level.
2634
2735
If the `array` contains tuples or records, this operation splits them
28-
into a Python tuple of arrays, one for each field.
36+
into a Python tuple (or dict) of arrays, one for each field.
2937
3038
If the `array` does not contain tuples or records, the single `array`
31-
is placed in a length 1 Python tuple.
39+
is placed in a length 1 Python tuple (or dict).
3240
3341
For example,
3442
@@ -40,15 +48,32 @@ def unzip(array, *, highlevel=True, behavior=None, attrs=None):
4048
<Array [1.1, 2.2, 3.3] type='3 * float64'>
4149
>>> y
4250
<Array [[1], [2, 2], [3, 3, 3]] type='3 * var * int64'>
51+
52+
The `how` argument determines the structure of the output. Using `how=dict`
53+
returns a dictionary of arrays instead of a tuple, and let's you round-trip
54+
through `ak.zip`:
55+
56+
>>> array = ak.Array([{"x": 1.1, "y": [1]},
57+
... {"x": 2.2, "y": [2, 2]},
58+
... {"x": 3.3, "y": [3, 3, 3]}])
59+
>>> x = ak.unzip(array, how=dict)
60+
>>> x
61+
{'x': <Array [1.1, 2.2, 3.3] type='3 * float64'>,
62+
'y': <Array [[1], [2, 2], [3, 3, 3]] type='3 * var * int64'>}
63+
>>> assert ak.zip(ak.unzip(array, how=dict), depth_limit=1).to_list() == array.to_list() # True
64+
4365
"""
4466
# Dispatch
4567
yield (array,)
4668

4769
# Implementation
48-
return _impl(array, highlevel, behavior, attrs)
70+
return _impl(array, how, highlevel, behavior, attrs)
71+
4972

73+
def _impl(array, how, highlevel, behavior, attrs):
74+
if how not in {tuple, dict}:
75+
raise ValueError(f"`how` must be `tuple` or `dict`, not {how!r}")
5076

51-
def _impl(array, highlevel, behavior, attrs):
5277
with HighLevelContext(behavior=behavior, attrs=attrs) as ctx:
5378
layout = ctx.unwrap(array, allow_record=True, primitive_policy="error")
5479

@@ -68,13 +93,10 @@ def check_for_union(layout, **kwargs):
6893
ak._do.recursively_apply(layout, check_for_union, return_array=False)
6994

7095
if len(fields) == 0:
71-
return (ctx.wrap(layout, highlevel=highlevel, allow_other=True),)
96+
items = (ctx.wrap(layout, highlevel=highlevel, allow_other=True),)
97+
fields = ["0"]
7298
else:
73-
return tuple(
74-
ctx.wrap(
75-
layout[n],
76-
highlevel=highlevel,
77-
allow_other=True,
78-
)
79-
for n in fields
99+
items = (
100+
ctx.wrap(layout[n], highlevel=highlevel, allow_other=True) for n in fields
80101
)
102+
return tuple(items) if how is tuple else dict(zip(fields, items))

0 commit comments

Comments
 (0)