I have a structured numpy array, I want to use the recfunctions library http://pyopengl.sourceforge.net/pydoc/numpy.lib.recfunctions.html function append_fields() or rec_append_fields() to append a field with some shape to it. However, I get an error:
ValueError: operands could not be broadcast together with shapes (10) (10,3)
where 10
is the length of my existing array, and (3,)
is the shape of the field I want to append.
For example:
import numpy as np
from numpy.lib.recfunctions import append_fields
my_structured_array = np.array(
zip([0,1,2,3],[[4.3,3.2],[1.4,5.6],[6.,2.5],[4.5,5.4]]),
dtype=[('id','int8'),('pos','2float16')]
)
my_new_field = np.ones(
len(my_structured_array),
dtype='2int8'
)
my_appended_array = append_fields(
my_structured_array,
'new',
data=my_new_field
)
ValueError: operands could not be broadcast together with shapes (4) (4,2)
Any ideas? I tried making my_new_field
a list of tuples and putting a dtype
argument with the proper shape into the append_fields():
my_new_field = len(my_structured_array)*[(1,1)]
my_appended_array = append_fields(
my_structured_array,
'new',
data=my_new_field,
dtype='2int8'
)
but that seems to end up the same once it gets converted to a numpy array.
None of this seems to change when I use rec_append_fields() instead of simply append_fields()
EDIT: In light of the fact that my new field doesn't have the same shape as my array, I suppose that my desired append is impossible, suggested by @radicalbiscuit.
In : my_new_field.shape
Out: (4, 2)
In : my_structured_array.shape
Out: (4,)
But, I included one of the original fields in the array with shape different from the original array to make my point, which is that a field does not have to have the same shape as the structured array. How can I append a field like this?
In : my_structured_array['pos'].shape
Out: (4, 2)
In : my_new_field.shape
Out: (4, 2)
I should note that for my application, I can append an empty field as long as it's possible to somehow change the shape later. Thanks!
append_fields()
does indeed require that the two arrays be the same shape. That being said, as you realized in my_structured_array
, numpy does support subarrays (that is, a field can itself be an array with a shape).
In your case, I think you probably want my_new_field
not to be a two dimensional array, but instead be a one dimensional array (of shape shape(my_structured_array)
) with elements of dtype, e.g., dtype([('myfield', '<i8', (2,))])
. For example,
import numpy as np
from numpy.lib.recfunctions import append_fields
my_structured_array = np.array(
zip([0,1,2,3],[[4.3,3.2],[1.4,5.6],[6.,2.5],[4.5,5.4]]),
dtype=[('id','int8'),('pos','2float16')]
)
my_new_field = np.ones(
len(my_structured_array),
dtype=[('myfield', 'i8', 2)]
)
my_appended_array = append_fields(
my_structured_array,
'new',
data=my_new_field
)
Will yield,
>>> my_appended_array[0]
(0, [4.30078125, 3.19921875], ([1, 1],))
Although the datatype is slightly inconvenient as myfield
is nested within new
,
>>> my_appended_array.dtype
dtype([('id', '|i1'), ('pos', '<f2', (2,)), ('new', [('myfield', '<i8', (2,))])])
This, however, is coerced away fairly easily,
>>> np.asarray(my_appended_array, dtype=[('id', '|i1'), ('pos', '<f2', (2,)), ('myfield', '<i8', (2,))])
array([(0, [4.30078125, 3.19921875], [0, 0]),
(1, [1.400390625, 5.6015625], [0, 0]), (2, [6.0, 2.5], [0, 0]),
(3, [4.5, 5.3984375], [0, 0])],
dtype=[('id', '|i1'), ('pos', '<f2', (2,)), ('myfield', '<i8', (2,))])
Still, it's a bit unfortunate that we've had to repeat the dtype of my_structured_array
here. While at first glance it appears that numpy.lib.recfunctions.flatten_descr
could do the dirty work of flattening the dtype, it unfortunately gives a tuple and not a list as required by np.dtype
. Coercing its output to a list, however, works around this issue,
>>> np.dtype(list(np.lib.recfunctions.flatten_descr(my_appended_array.dtype)))
dtype([('id', '|i1'), ('pos', '<f2', (2,)), ('myfield', '<i8', (2,))])
This can be passed as the dtype to np.asarray
, making things slightly more robust against changes in my_structured_array.dtype
.
Indeed, minor inconsistencies such as this make working with record arrays messy business. One gets the feeling that things could fit together a bit more coherently.
Edit:
It turns out that the np.lib.recfunctions.merge_arrays
function is much more amenable to this sort of merging,
>>> my_appended_array = merge_arrays([my_structured_array, my_new_field], flatten=True)
array([(0, [4.30078125, 3.19921875], [1, 1]),
(1, [1.400390625, 5.6015625], [1, 1]), (2, [6.0, 2.5], [1, 1]),
(3, [4.5, 5.3984375], [1, 1])],
dtype=[('id', '|i1'), ('pos', '<f2', (2,)), ('myfield', '<i8', (2,))])
append_fields()
requires that the two arrays are the same shape, which in this case they are not. Printing out the two arrays will help it become obvious:
>>> my_structured_array
array([(0, [4.30078125, 3.19921875]), (1, [1.400390625, 5.6015625]),
(2, [6.0, 2.5]), (3, [4.5, 5.3984375])],
dtype=[('id', '|i1'), ('pos', '<f2', (2,))])
>>> my_new_field
array([[1, 1],
[1, 1],
[1, 1],
[1, 1]], dtype=int8)
As you can see, my_structured_array
is an array of length 4 where each element is a tuple containing two objects, an int and a list of two floats.
my_new_field
, on the other hand, is an array of length 4 where each element is a list of two ints. It's like trying to add apples and oranges.
Make your arrays the same shape and they'll add together.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With