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_likeis 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,