11
11
from awkward ._namedaxis import _get_named_axis , _unify_named_axis
12
12
from awkward ._nplikes .numpy_like import NumpyMetadata
13
13
14
- __all__ = ("unsafe_zip " ,)
14
+ __all__ = ("zip_no_broadcast " ,)
15
15
16
16
np = NumpyMetadata .instance ()
17
17
18
18
19
19
@high_level_function ()
20
- def unsafe_zip (
20
+ def zip_no_broadcast (
21
21
arrays ,
22
22
* ,
23
23
parameters = None ,
@@ -46,7 +46,7 @@ def unsafe_zip(
46
46
of records or the slots of a collection of tuples.
47
47
48
48
Caution: unlike #ak.zip this function will _not_ broadcast the arrays together.
49
- It assumes that the given arrays have already the same layouts and lengths.
49
+ During typetracing, it assumes that the given arrays have already the same layouts and lengths.
50
50
51
51
This operation may be thought of as the opposite of projection in
52
52
#ak.Array.__getitem__, which extracts fields one at a time, or
@@ -60,7 +60,7 @@ def unsafe_zip(
60
60
Zipping them together using a dict creates a collection of records with
61
61
the same nesting structure as `one` and `two`.
62
62
63
- >>> ak.unsafe_zip ({"x": one, "y": two}).show()
63
+ >>> ak.zip_no_broadcast ({"x": one, "y": two}).show()
64
64
[[{x: 1.1, y: 'a'}, {x: 2.2, y: 'b'}, {x: 3.3, y: 'c'}],
65
65
[],
66
66
[{x: 4.4, y: 'd'}],
@@ -153,7 +153,6 @@ def _impl(
153
153
parameters ["__record__" ] = with_name
154
154
155
155
# only allow all NumpyArrays and ListOffsetArrays
156
- # maybe this could be done recursively, but for now just check the top level. This is also how ak.zip works.
157
156
if all (isinstance (layout , ak .contents .NumpyArray ) for layout in layouts ):
158
157
length = _check_equal_lengths (layouts )
159
158
out = ak .contents .RecordArray (
@@ -162,14 +161,35 @@ def _impl(
162
161
elif all (isinstance (layout , ak .contents .ListOffsetArray ) for layout in layouts ):
163
162
contents = []
164
163
for layout in layouts :
164
+ # get the content of the ListOffsetArray
165
165
if not isinstance (layout .content , ak .contents .NumpyArray ):
166
166
raise ValueError (
167
167
"can not (unsafe) zip ListOffsetArrays with non-NumpyArray contents"
168
168
)
169
169
contents .append (layout .content )
170
- # just get from the first one
171
- offsets = layouts [0 ].offsets
172
- length = _check_equal_lengths ([layout .content for layout in layouts ])
170
+
171
+ if backend .name == "typetracer" :
172
+ # just get from the first one
173
+ # we're in typetracer mode, so we can't check the offsets (see else branch)
174
+ offsets = layouts [0 ].offsets
175
+ else :
176
+ # this is at 'runtime' with actual data, that means we can check the offsets,
177
+ # but only those that have actual data, i.e. no PlaceholderArrays
178
+ # so first, let's filter out any PlaceholderArrays
179
+ comparable_offsets = filter (
180
+ lambda o : not isinstance (o , ak ._nplikes .placeholder .PlaceholderArray ),
181
+ (layout .offsets for layout in layouts ),
182
+ )
183
+ # check that offsets are the same
184
+ first = next (comparable_offsets )
185
+ if not all (
186
+ first .nplike .all (offsets .data == first .data )
187
+ for offsets in comparable_offsets
188
+ ):
189
+ raise ValueError ("all ListOffsetArrays must have the same offsets" )
190
+ offsets = first
191
+
192
+ length = _check_equal_lengths (contents )
173
193
out = ak .contents .ListOffsetArray (
174
194
offsets = offsets ,
175
195
content = ak .contents .RecordArray (
@@ -193,10 +213,10 @@ def _impl(
193
213
194
214
195
215
def _check_equal_lengths (
196
- layouts : ak .contents .Content ,
216
+ contents : ak .contents .Content ,
197
217
) -> int | ak ._nplikes .shape .UnknownLength :
198
- length = layouts [0 ].length
199
- for layout in layouts :
218
+ length = contents [0 ].length
219
+ for layout in contents :
200
220
if layout .length != length :
201
221
raise ValueError ("all arrays must have the same length" )
202
222
return length
0 commit comments