在数组中的对象上设置属性的最有效方法

时间:2018-10-28 17:31:21

标签: python arrays numpy

我正在处理代表网格的大型数组,每个元素都是具有x,y属性的Cell对象。

我不确定初始化数组的最有效方法,我的基本实现是:

# X,Y dimensions of grid:
Gx = 3000
Gy = 4000

    # Array to create
    A = numpy.ndarray(shape=(int(self.Gx),int(self.Gy)),dtype=object)

for y in range(0,int(self.Gy)):
             for x in range (0,int(self.Gx)):       
              c = Cell(1,x,y,1)
              A.itemset((x,y),c)

很显然,这对于大型阵列而言效率不高。我知道如何创建大量对象并使用矢量化一次访问所有对象。我不知道的是如何在不需要迭代整个数组的单个函数中应用索引数组(通过A.indices)。

每个Cell对象确实具有setX和setY函数,我可以通过函数的索引数组来在一行中设置每个单元的y值吗?

1 个答案:

答案 0 :(得分:0)

定义一个简单的类:

class Cell():
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def setX(self,x):
        self.x=x
    def __repr__(self):
        return f'Cell({self.x},{self.y})'

一种创建这些对象的数组的方法:

In [653]: f = np.frompyfunc(Cell, 2, 1)
In [654]: arr = f(np.arange(3)[:,None], np.arange(4))
In [655]: arr
Out[655]: 
array([[Cell(0,0), Cell(0,1), Cell(0,2), Cell(0,3)],
       [Cell(1,0), Cell(1,1), Cell(1,2), Cell(1,3)],
       [Cell(2,0), Cell(2,1), Cell(2,2), Cell(2,3)]], dtype=object)
In [656]: arr.shape
Out[656]: (3, 4)

创建相同对象的列表方式:

In [658]: [[Cell(i,j) for i in range(3)] for j in range(4)]
Out[658]: 
[[Cell(0,0), Cell(1,0), Cell(2,0)],
 [Cell(0,1), Cell(1,1), Cell(2,1)],
 [Cell(0,2), Cell(1,2), Cell(2,2)],
 [Cell(0,3), Cell(1,3), Cell(2,3)]]

一些比较时间:

In [659]: timeit arr = f(np.arange(3)[:,None], np.arange(4))
13.5 µs ± 73.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [660]: timeit [[Cell(i,j) for i in range(3)] for j in range(4)]
8.3 µs ± 115 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [661]: timeit arr = f(np.arange(300)[:,None], np.arange(400))
64.9 ms ± 293 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [662]: timeit [[Cell(i,j) for i in range(300)] for j in range(400)]
78 ms ± 2.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

对于大集合,frompyfunc方法具有适度的速度优势。

从所有单元格中获取值:

In [664]: np.frompyfunc(lambda c: c.x, 1, 1)(arr)
Out[664]: 
array([[0, 0, 0, 0],
       [1, 1, 1, 1],
       [2, 2, 2, 2]], dtype=object)

使用SetX方法:

In [665]: np.frompyfunc(Cell.setX, 2, 1)(arr, np.arange(12).reshape(3,4))
Out[665]: 
array([[None, None, None, None],
       [None, None, None, None],
       [None, None, None, None]], dtype=object)
In [666]: arr
Out[666]: 
array([[Cell(0,0), Cell(1,1), Cell(2,2), Cell(3,3)],
       [Cell(4,0), Cell(5,1), Cell(6,2), Cell(7,3)],
       [Cell(8,0), Cell(9,1), Cell(10,2), Cell(11,3)]], dtype=object)

SetX不返回任何内容,因此函数调用产生的数组均为None。但是它修改了arr的所有元素。像列表理解一样,我们通常不会使用frompyfunc来产生副作用,但是有可能。

np.vectorize以其默认(原始)格式使用frompyfunc,并调整返回值的dtype。 frompyfunc始终返回对象dtype。较新版本的vectorize具有一个signature参数,使我们可以将数组(与标量相反)传递给函数,并返回数组。但是此过程甚至更慢。

像这样定义对象数组可以使您的代码看起来更整洁和组织更好,但是就速度而言,它们永远无法匹配数字numpy数组。


鉴于Cell的定义,我可以将属性设置为数组,例如

Cell(np.arange(3), np.zeros((3,4)))

但是要设置Cell数组的值,我必须首先构造一个对象数组:

In [676]: X = np.zeros(3, object)
In [677]: for i,row in enumerate(np.arange(6).reshape(3,2)): X[i]=row
In [678]: X
Out[678]: array([array([0, 1]), array([2, 3]), array([4, 5])], dtype=object)
In [679]: np.frompyfunc(Cell.setX, 2, 1)(arr, X[:,None])
Out[679]: 
array([[None, None, None, None],
       [None, None, None, None],
       [None, None, None, None]], dtype=object)
In [680]: arr
Out[680]: 
array([[Cell([0 1],0), Cell([0 1],1), Cell([0 1],2), Cell([0 1],3)],
       [Cell([2 3],0), Cell([2 3],1), Cell([2 3],2), Cell([2 3],3)],
       [Cell([4 5],0), Cell([4 5],1), Cell([4 5],2), Cell([4 5],3)]],
      dtype=object)

我无法传递(3,2)数组:

In [681]: np.frompyfunc(Cell.setX, 2, 1)(arr, np.arange(6).reshape(3,2))
ValueError: operands could not be broadcast together with shapes (3,4) (3,2) 

numpy优选用于多维(数字)数组。创建和使用对象dtype数组需要一些特殊技巧。