Python 返回numpy中类元素的向量

Python 返回numpy中类元素的向量,python,numpy,Python,Numpy,我可以使用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): se

我可以使用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)
现在,如果我想从
数组中检索(比如)包含
速度
的向量,我可以使用普通的Python列表

    v = [p.velocity for p in points]

但是有没有一种方法可以做到这一点呢?在大型阵列上,这会比使用列表理解更有效吗?

创建点的直接列表理解方法:

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一起使用)

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]: 
简言之,一维对象数组与列表相比没有什么优势;只有当您需要以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)


探索存储/访问对象属性的一些替代方法。

创建点的直接列表理解方法:

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一起使用)

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]: 
简言之,一维对象数组与列表相比没有什么优势;只有当您需要以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)


探索一些存储/访问对象属性的替代方法。

因此,我建议您不要将
numpy
数组与
对象
数据类型一起使用。然而,这里的基本上是一个结构,因此您可以使用。因此,首先,创建一个
dtype

>>> import numpy as np
>>> bodytype = np.dtype([('position', np.complex), ('mass', np.float), ('velocity', np.complex)])
然后,初始化body数组:

>>> 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')])
现在您有了一系列“实体”,它们可以充分利用
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])
>>>

因此,我建议您不要将
numpy
数组与
对象一起使用。然而,这里的基本上是一个结构,因此您可以使用。因此,首先,创建一个
dtype

>>> import numpy as np
>>> bodytype = np.dtype([('position', np.complex), ('mass', np.float), ('velocity', np.complex)])
然后,初始化body数组:

>>> 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')])
现在您有了一系列“实体”,它们可以充分利用
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])
>>>

numpythonic实现这一点的方法是不使用numpy。或使用。但从您描述的内容来看,您只需要一个普通的
列表
。因为您所做的是创建
object
dtype数组,而这些数组基本上是性能较差的python列表,没有numpy数组的任何优点。这不是一个好主意,但是:
v=points[0]。velocity.base
会让您恢复原来的
velocities
数组,通常,numpythonic的方法是不使用numpy。或使用。但从您描述的内容来看,您只需要一个普通的
列表
。因为您所做的是创建
object
dtype数组,而这些数组基本上是性能较差的python列表,没有numpy数组的任何优点。这不是一个好主意,但是:
v=points[0]。velocity.base
会让您恢复原来的
velocities
数组, usually@TimGJ:这有点误用了
complex
-您可以使用
np.dtype([('position',np.float,2),…])
来存储2个向量。显然,这比使用
complex
@Eric更容易扩展到3D。啊,是的,我几乎没有使用​ 好好想想。谢谢,@juanpa.arrivillaga。我尝试使用类的原因是为了能够访问所有其他底层OO特性。但结构似乎就是我想要的。非常感谢。@TimGJ老实说,我认为您真正想要的是使用常规的旧Python列表。@TimGJ
numpy
数据结构本身可以作为自定义类的强大主干。不幸的是,它们不能很好地充当任意Python对象的容器,并且是专门为使用数字而设计的,没有Python数值类型的所有开销。它们本质上是围绕C数组的包装器,通过机器代码编译的例程,可以为启动时抛出的数值计算提供一些强大的马力。@TimGJ:这有点误用了
complex
-您可以使用
np.dtype([('position',np.float,2),…])
来存储2个向量。显然,这比使用
complex
@Eric更容易扩展到3D。啊,是的,我几乎没有使用​ 好好想想。谢谢,@juanpa.arrivillaga。我尝试使用类的原因是为了能够访问所有其他底层OO特性。但结构似乎就是我想要的。非常感谢。@TimGJ老实说,我认为您真正想要的是使用常规的旧Python列表。@TimGJ
numpy
数据结构本身可以作为自定义类的强大主干。不幸的是,它们确实如此