2012-06-27 52 views
9

在做一些生物信息学工作时,我一直在思考存储对象实例在Numpy数组而不是Python列表中的影响,但是在所有测试中我在各种情况下都表现得更糟。我正在使用CPython。有谁知道原因?将Python对象存储在Python列表与固定长度的Numpy数组中

具体来说:

  • 什么是使用固定长度的阵列numpy.ndarray(dtype=object)与常规Python列表的性能影响?我执行的初始测试表明,访问Numpy数组元素比通过Python列表迭代要慢,特别是在使用对象方法时。
  • 为什么使用列表理解(例如[ X() for i in range(n) ]而不是numpy.empty(size=n, dtype=object))实例化对象更快?
  • 每个内存开销是多少?我无法测试这个。我的课程广泛使用__slots__,如果有任何影响。

回答

16

不要在numpy中使用像这样的对象数组。

它们击败了numpy数组的基本目的,尽管它们在极少数情况下很有用,但它们几乎总是一个糟糕的选择。

是的,访问python中的numpy数组的单个元素或在python中遍历numpy数组比使用list的等效操作要慢。 (这就是为什么你不应该做这样的事情y = [item * 2 for item in x]x是numpy的阵列。)

numpy的对象数组将有小幅走低的内存开销比名单,但如果你存储许多个人Python对象,你首先会遇到其他内存问题。

Numpy首先是一个用于统一数字数据的内存高效的多维数组容器。如果你想在一个numpy数组中保存任意对象,你可能需要一个列表。


我的观点是,如果你想有效地使用numpy的,你可能需要重新思考你是如何构建的东西。

代替在一个numpy的阵列中存储每个对象实例,您的数值数据存储在numpy的阵列,并且如果需要单独的对象的每个行/列/不管,索引存储到在每一种情况下该数组。

这样就可以快速操作数值数组(即使用numpy代替列表解析)。

作为一个简单的例子就是我说一下,这里是不使用numpy的一个简单的例子:

from random import random 

class PointSet(object): 
    def __init__(self, numpoints): 
     self.points = [Point(random(), random()) for _ in xrange(numpoints)] 

    def update(self): 
     for point in self.points: 
      point.x += random() - 0.5 
      point.y += random() - 0.5 

class Point(object): 
    def __init__(self, x, y): 
     self.x = x 
     self.y = y 

points = PointSet(100000) 
point = points.points[10] 

for _ in xrange(1000): 
    points.update() 
    print 'Position of one point out of 100000:', point.x, point.y 

而一个类似的例子使用numpy的数组:

import numpy as np 

class PointSet(object): 
    def __init__(self, numpoints): 
     self.coords = np.random.random((numpoints, 2)) 
     self.points = [Point(i, self.coords) for i in xrange(numpoints)] 

    def update(self): 
     """Update along a random walk.""" 
     # The "+=" is crucial here... We have to update "coords" in-place, in 
     # this case. 
     self.coords += np.random.random(self.coords.shape) - 0.5 

class Point(object): 
    def __init__(self, i, coords): 
     self.i = i 
     self.coords = coords 

    @property 
    def x(self): 
     return self.coords[self.i,0] 

    @property 
    def y(self): 
     return self.coords[self.i,1] 


points = PointSet(100000) 
point = points.points[10] 

for _ in xrange(1000): 
    points.update() 
    print 'Position of one point out of 100000:', point.x, point.y 

还有其他的方法要做到这一点(例如,您可能希望避免在每个point中存储对特定 numpy数组的引用),但我希望这是一个有用的示例。

请注意它们运行速度的差异。在我的机器上,numpy版本的差异是5秒,纯python版本的差异是60秒。

相关问题