numpy Structured array adding record

2020-05-06 20:03发布

问题:

I have an Structured array like this:

a = np.array([(0. , 1. , 2.) , (10. , 11. , 12. )] ,
             dtype=[('PositionX', '<f8'), ('PositionY', '<f8'), ('PositionZ', '<f8')])

Now, I want add record 0 (a[0]) and record 1 (a[1]), to get something like : (10. , 12. , 14. )

When I write something like :

a[0] + a[1]

I got error which tell me you cant add two dtype object or something like that.

So, I think maybe I can turn a[0] to be a regular vector, then perform adding.

But numpy.array(a[0]) have same dtype as a[0], and numpy.array(a[0],dtype=np.float64) does not work too.

So, can anyone tell me how to convert a[0] to regular vector? please don't tell me to covert structured array to regular array. because I just want take few of my array record and do adding. Besides, I really want to know how to turn an object like a[0] to an regular vector.

回答1:

You're getting an error just because the a[i] are tuples, you can't add directly tuple. You have to access them, a more pythonic way to achieve this would be:

map(sum, zip(*a))

the zip function do exactly what you're looking for, after that you have to process each entry according to what you need, in your case sum , you can also try this:

result = []
for elem in zip(*a):
    result.append(sum(elem))


回答2:

In [206]: a
Out[206]: 
array([(  0.,   1.,   2.), ( 10.,  11.,  12.)], 
      dtype=[('PositionX', '<f8'), ('PositionY', '<f8'), ('PositionZ', '<f8')])

A record is a compound numpy dtype object, that is displayed as a tuple.

In [207]: type(a[0])
Out[207]: numpy.void
In [208]: a[0].dtype
Out[208]: dtype([('PositionX', '<f8'), ('PositionY', '<f8'), ('PositionZ', '<f8')])

Fields ('columns') of the array are arrays, and do the normal array math.

In [209]: a['PositionX']
Out[209]: array([  0.,  10.])
In [210]: a['PositionX']+a['PositionY']
Out[210]: array([  1.,  21.])

But math has not been defined for the compound dtype:

In [211]: a[0]+a[1]
TypeError: ufunc 'add' did not contain a loop with signature matching types dtype([('PositionX', '<f8'), ('PositionY', '<f8'), ('PositionZ', '<f8')]) ....

If you allow me to convert the whole array to 2d, I can add rows:

In [213]: a1=np.array(a.tolist())
In [214]: a1
Out[214]: 
array([[  0.,   1.,   2.],
       [ 10.,  11.,  12.]])
In [215]: a1[0]+a1[1]
Out[215]: array([ 10.,  12.,  14.])

There are other ways of converting a structured array to 2d (with view or astype) but this tolist() is easiest to use and most consistent. More on this at https://stackoverflow.com/a/43380941/901925

But to do math with individual records you have to convert them to arrays or treat them like the displayed tuples.

In [218]: np.array(a[0].tolist())
Out[218]: array([ 0.,  1.,  2.])
In [219]: np.array(a[0].tolist())+np.array(a[1].tolist())
Out[219]: array([ 10.,  12.,  14.])

But are you happy with this array, or do you want that back in the a.dtype?

In [234]: np.array(tuple(asum), a.dtype)
Out[234]: 
array(( 10.,  12.,  14.), 
      dtype=[('PositionX', '<f8'), ('PositionY', '<f8'), ('PositionZ', '<f8')])

The data to a structured array must be in tuples or list of tuples.

You have to do the same dtype conversion if you use the zipped approach that @Mohamed Lakhal showed

In [236]: [i+j for i,j in zip(a[0],a[1])]
Out[236]: [10.0, 12.0, 14.0]
In [237]: np.array(tuple([i+j for i,j in zip(a[0],a[1])]), a.dtype)

While a view approach as Divakar commented converts the whole array:

In [227]: a.view('<f8')
Out[227]: array([  0.,   1.,   2.,  10.,  11.,  12.])
In [228]: a.view('<f8').reshape(-1,3)
Out[228]: 
array([[  0.,   1.,   2.],
       [ 10.,  11.,  12.]])

it does not work with a record:

In [229]: a[0].view('<f8')
 ....
ValueError: new type not compatible with array.

This is a better converter to 2d array:

In [239]: a.view('3f8')
Out[239]: 
array([[  0.,   1.,   2.],
       [ 10.,  11.,  12.]])
In [240]: a[0].view('3f8')
Out[240]: array([ 0.,  1.,  2.])
In [241]: a[[0,1]].view('3f8')
Out[241]: 
array([[  0.,   1.,   2.],
       [ 10.,  11.,  12.]])
In [242]: a[[0,1]].view('3f8').sum(axis=0)
Out[242]: array([ 10.,  12.,  14.])