我可以使用numpy的vectorize
函数来创建一些任意类的对象数组:
import numpy as np
class Body:
"""
Simple class to represent a point mass in 2D space, more to
play with numpy than anything else...
"""
def __init__(self, position, mass, velocity):
self.position = position
self.mass = mass
self.velocity = velocity
def __repr__(self):
return "m = {} p = {} v = {}".format(self.mass,
self.position, self.velocity)
if __name__ == '__main__':
positions = np.array([0 + 0j, 1 + 1j, 2 + 0j])
masses = np.array([2, 5, 1])
velocities = np.array([0 + 0j, 0 + 1j, 1 + 0j])
vBody = np.vectorize(Body)
points = vBody(positions, masses, velocities)
现在,如果我想从velocities
数组中检索包含(例如)points
的向量,我可以使用普通的Python列表推导
v = [p.velocity for p in points]
但是有numpy
- thonic方式吗?在大型数组上,这比使用列表推导更有效吗?
答案 0 :(得分:3)
因此,我建议您不要将numpy
数组与object
dtype一起使用。但是,你在这里拥有的本质上是一个结构,所以你可以使用structured array使用numpy
。首先,创建一个dtype
:
>>> import numpy as np
>>> bodytype = np.dtype([('position', np.complex), ('mass', np.float), ('velocity', np.complex)])
然后,初始化你的身体阵列:
>>> bodyarray = np.zeros((len(positions),), dtype=bodytype)
>>> bodyarray
array([(0j, 0.0, 0j), (0j, 0.0, 0j), (0j, 0.0, 0j)],
dtype=[('position', '<c16'), ('mass', '<f8'), ('velocity', '<c16')])
现在,您可以轻松设置值:
>>> positions = np.array([0 + 0j, 1 + 1j, 2 + 0j])
>>> masses = np.array([2, 5, 1])
>>> velocities = np.array([0 + 0j, 0 + 1j, 1 + 0j])
>>> bodyarray['position'] = positions
>>> bodyarray['mass'] = masses
>>> bodyarray['velocity'] = velocities
现在你有了一系列“身体”,可以充分利用numpy
,并让你像这样访问“属性”:
>>> bodyarray
array([(0j, 2.0, 0j), ((1+1j), 5.0, 1j), ((2+0j), 1.0, (1+0j))],
dtype=[('position', '<c16'), ('mass', '<f8'), ('velocity', '<c16')])
>>> bodyarray['mass']
array([ 2., 5., 1.])
>>> bodyarray['velocity']
array([ 0.+0.j, 0.+1.j, 1.+0.j])
>>> bodyarray['position']
array([ 0.+0.j, 1.+1.j, 2.+0.j])
>>>
请注意,
>>> bodyarray.shape
(3,)
答案 1 :(得分:0)
创建点的直接列表理解方法:
In [285]: [Body(p,m,v) for p,m,v in zip(positions, masses,velocities)]
Out[285]: [m = 2 p = 0j v = 0j, m = 5 p = (1+1j) v = 1j, m = 1 p = (2+0j) v = (1+0j)]
In [286]: timeit [Body(p,m,v) for p,m,v in zip(positions, masses,velocities)]
100000 loops, best of 3: 6.74 µs per loop
为此,创建一个对象数组,frompyfunc
比np.vectorize
快(尽管你应该使用otypes
和vectorize)。
In [287]: vBody = np.frompyfunc(Body,3,1)
In [288]: vBody(positions, masses, velocities)
Out[288]:
array([m = 2 p = 0j v = 0j, m = 5 p = (1+1j) v = 1j,
m = 1 p = (2+0j) v = (1+0j)], dtype=object)
vectorize
比理解慢,但这个frompyfunc
版本具有竞争力
In [289]: timeit vBody(positions, masses, velocities)
The slowest run took 12.26 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 8.56 µs per loop
vectorize/frompyfunc
在广播中添加了一些有用的功能。例如,通过使用ix_
,我可以生成3个输入的笛卡尔积和3d积分,而不仅仅是3个:
In [290]: points = vBody(*np.ix_(positions, masses, velocities))
In [291]: points.shape
Out[291]: (3, 3, 3)
In [292]: points
Out[292]:
array([[[m = 2 p = 0j v = 0j, m = 2 p = 0j v = 1j, m = 2 p = 0j v = (1+0j)],
....
[m = 1 p = (2+0j) v = 0j, m = 1 p = (2+0j) v = 1j,
m = 1 p = (2+0j) v = (1+0j)]]], dtype=object)
In [293]:
简而言之,与列表相比,1d对象阵列的优点很少;只有当你需要组织2个或更多维度的对象时,这些数组才有优势。
至于访问属性,您可以使用列表推导或等效的vectorize
操作。
[x.position for x in points.ravel()]
Out[294]:
[0j,
0j,
0j,
...
(2+0j),
(2+0j)]
In [295]: vpos = np.frompyfunc(lambda x:x.position,1,1)
In [296]: vpos(points)
Out[296]:
array([[[0j, 0j, 0j],
[0j, 0j, 0j],
...
[(2+0j), (2+0j), (2+0j)],
[(2+0j), (2+0j), (2+0j)]]], dtype=object)
在Tracking Python 2.7.x object attributes at class level to quickly construct numpy array
中探讨了存储/访问对象属性的一些替代方法。