Best way to create a new deeply nested array with shape and depth matching an existing array? #3101
-
To start, a bit of context to avoid the X-Y problem: I have a deeply nested input array which I need to use for a calculation in numba. The final output of this calculation should be of the same shape + depth as the input array. I do the numba calculation and return the output in a flattened np array. I'm now left with converting that output into the same shape + depth as the input array. However, unflattening is where I run into trouble. For one level of depth, unflatten + As a concrete example: In [115]: input_array
Out[115]: <Array [[[[0, 1, -4], [...], ..., [2]]], ...] type='2 * var * var * var * i...'>
In [116]: input_array.to_list()
Out[116]:
[[[[0, 1, -4], [2, 3], [1, -4], [0], [1], [-4], [3], [2]]],
[[[0, 1, -4], [2, 3], [1, -4], [0], [1], [-4], [3], [2]]]]
In [117]: input_array.type.show()
2 * var * var * var * int64
In [118]: output_array
Out[118]:
array([0, 1, 2, 3, 4, 1, 2, 0, 1, 2, 4, 3, 0, 1, 2, 3, 4, 1, 2, 0, 1, 2,
4, 3])
In [119]: output_array_same_shape = ?? I've tried every combination of unflatten, num, and sum that I can think of but I'm unable to find the right way to get In [1] output_array_2 = ak.unflatten(output_array, ak.sum(ak.sum(ak.num(input_array, axis=-1), axis=1), axis=1))
In [2] output_array_3 = ak.unflatten(output_array_2, ak.flatten(ak.sum(ak.num(input_array, axis=-1), axis=1), axis=1))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[97], line 1
----> 1 res3 = ak.unflatten(res2, ak.flatten(ak.sum(ak.num(constituents_user_index, axis=-1), axis=1), axis=1))
File ~/software/dev/mammoth/.venv-3.11/lib/python3.11/site-packages/awkward/_dispatch.py:62, in named_high_level_function.<locals>.dispatch(*args, **kwargs)
60 # Failed to find a custom overload, so resume the original function
61 try:
---> 62 next(gen_or_result)
63 except StopIteration as err:
64 return err.value
File ~/software/dev/mammoth/.venv-3.11/lib/python3.11/site-packages/awkward/operations/ak_unflatten.py:90, in unflatten(array, counts, axis, highlevel, behavior, attrs)
87 yield (array,)
89 # Implementation
---> 90 return _impl(array, counts, axis, highlevel, behavior, attrs)
File ~/software/dev/mammoth/.venv-3.11/lib/python3.11/site-packages/awkward/operations/ak_unflatten.py:207, in _impl(array, counts, axis, highlevel, behavior, attrs)
204 return out
206 if axis == 0 or maybe_posaxis(layout, axis, 1) == 0:
--> 207 out = unflatten_this_layout(layout)
209 else:
211 def recursively_apply_to_content(
212 action, layout, depth, depth_context, lateral_context, options, **kwargs
213 ):
File ~/software/dev/mammoth/.venv-3.11/lib/python3.11/site-packages/awkward/operations/ak_unflatten.py:186, in _impl.<locals>.unflatten_this_layout(layout)
167 position = (
168 index_nplike.searchsorted(
169 current_offsets,
(...)
175 - 1
176 )
177 if (
178 current_offsets.size is not unknown_length
179 and layout.length is not unknown_length
(...)
184 )
185 ):
--> 186 raise ValueError(
187 "structure imposed by 'counts' does not fit in the array or partition "
188 f"at axis={axis}"
189 )
191 offsets = current_offsets[: position + 1]
192 current_offsets = current_offsets[
193 position:
194 ] - index_nplike.shape_item_as_index(layout.length)
ValueError: structure imposed by 'counts' does not fit in the array or partition at axis=0
This error occurred while calling
ak.unflatten(
<Array [[0, 1, 2, 3, 4, ..., 1, 2, 4, 3], ...] type='2 * var * int64'>
<Array [3, 2, 2, 1, 1, 1, 1, ..., 2, 1, 1, 1, 1, 1] type='16 * int64'>
) Even if this worked and I could come up with a recursive function, it still seems far to complicated for something that is conceptually so simple. I've considered something like Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
As a brief follow up, I did finally find a formulation that worked here (copied below for completeness), but my question remains: conceptually, this seems like I would expect to be a 1-2 liner rather than something that I need to carefully think through each time I end up in a similar -- but not exactly the same -- situation. Thanks! In [157]: counts = ak.sum(ak.num(input_array, axis=-1), axis=1)
In [160]: output_array_2 = ak.unflatten(output_array, ak.sum(counts, axis=1))
In [158]: output_array_3 = ak.unflatten(output_array_2, ak.flatten(counts), axis=1)
In [159]: output_array_4 = ak.unflatten(output_array_3, ak.num(input_array, axis=1))
# output_array_4 is now in the same shape as input_array! |
Beta Was this translation helpful? Give feedback.
-
Applying import awkward as ak
import numpy as np
def kernel(inputs, output):
for i, input_ in enumerate(inputs):
output[i] = np.prod(input_)
def transform(layout, depth, **kwargs):
if depth == 2 and layout.is_list: # Act at list of outer lists
kernel(ak.Array(layout), output)
return ak.to_content(output)
result = ak.transform(transform, array) |
Beta Was this translation helpful? Give feedback.
What you're proposing as
ak.shape_like
is essentially ak.unflatten, if it worked on multiple levels at once. And if it took an input array's structure directly, instead of asking forcounts
(which could be taken from ak.num).Actually,