Самый эффективный способ установить атрибуты для объектов в массиве - PullRequest
0 голосов
/ 28 октября 2018

Я работаю с большими массивами, представляющими сетку, каждый элемент является объектом Cell с атрибутами x, y.

Я не уверен, что наиболее эффективный способ инициализации массивов, моя базовая реализация:

# 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 голосов
/ 30 октября 2018

Определить простой класс:

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 и настраиваетсяd-тип возврата.frompyfunc всегда возвращает объект dtype.Более новые версии vectorize имеют параметр signature, что позволяет нам передавать массивы (в отличие от скаляров) в функцию и возвращать массивы.Но эта обработка еще медленнее.

Определение таких массивов объектов может сделать ваш код более чистым и лучше организованным, но они никогда не смогут соответствовать числовым массивам с точки зрения скорости.


Учитывая определение 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 объекта требует специальных приемов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...