Заполнение массива 2d numpy объектом, который принимает индекс в качестве параметра

Представьте себе такой класс.

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

Тогда у меня есть массив

obj_arr = np.zeros((200,200), dtype=object)

Я хочу заполнить его полным классом, но с входными параметрами в качестве индекса массива.

Я играл с идеей np.where, но не понимаю, как добавить параметры.

np.where(obj_arr == None, obj_arr, Foo()) 

В настоящее время я делаю этот отвратительный вложенный цикл. 1) я знаю, что это неправильный путь 2) это занимает вечность.

for y in range(obj_arr.shape[0]):
    for x in range(obj_arr.shape[1]):
        obj_arr[y,x] == Foo(y,x)

Кто-нибудь, пожалуйста, подтолкните меня в правильном направлении, прежде чем я сойду с ума.

«Хотите заполнить его полным классом, но с входными параметрами в качестве индекса массива». Почему вы хотите это сделать? Если вы собираетесь это сделать, вы можете просто использовать вложенный цикл Python — использование массива объектного типа сводит на нет преимущества использования numpy.ndarray. Если ваш класс на самом деле что-то вроде этого, то есть базовый контейнер атрибутов, то, возможно, вы могли бы вместо этого использовать структурированный массив и сохранить преимущества numpy

juanpa.arrivillaga 11.12.2020 21:23
numpy.fromiter и итератор, возвращающий Foo экземпляры, может работать. Он создаст одномерный массив, но впоследствии вы сможете изменить его форму. Однако в любом случае это может быть немного медленным и использовать много памяти. Вы уверены, что вам действительно нужно 40 000 экземпляров класса?
Jussi Nurminen 11.12.2020 21:26

@juanpa.arrivillaga О, чувак, хотел бы я знать, почему я этого хочу. Мое мышление предназначено для хранения дополнительной информации о пикселе, и у меня есть массив изображений, а затем это. Так что я могу легко получить объект через индекс от щелчков мыши и координат X, Y. Это длинная история, конечно, есть лучший способ, но мой мозг поджаривается.

Lewis Morris 11.12.2020 21:26

@LewisMorris конечно, почему бы не просто список?

juanpa.arrivillaga 11.12.2020 21:33

просто потому, что я использую numpy и знаю, как работает np.zeros. Я думаю, что я собираюсь просто добавить плоский список и написать функцию для преобразования индекса в координаты. что-то вроде (int(index/height), index%height) должно работать, я думаю.

Lewis Morris 11.12.2020 21:37
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
5
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Наткнулся на этот хорошо документированный сайт Iterating Over Arrays.

Это должно решить вашу проблему, я пробовал это и для 200x200, и это почти не заняло много времени.

Примечание. Убедитесь, что версия numpy> = 1.15.

import numpy as np

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


a = np.empty((2, 2), dtype=object)

with np.nditer(a, flags=['multi_index', 'refs_ok'], op_flags=['writeonly']) as it:
    for x in it:
        x[...] = Foo(it.multi_index[1], it.multi_index[0])

print(a)
[[<__main__.Foo object at 0x7f91e2613370>
  <__main__.Foo object at 0x7f91e25b2070>]
 [<__main__.Foo object at 0x7f91e11ef220>
  <__main__.Foo object at 0x7f91e11ef160>]]

Это медленнее, чем вложенный цикл. np.nditer, по моему опыту, полезен только как шаг к использованию в скомпилированном коде. Интерфейс Python медленный.

hpaulj 12.12.2020 06:44

@hpaulj Я должен был сравнить, спасибо, что указали на это! +1 к вашему ответу, ура.

sai 12.12.2020 07:38
Ответ принят как подходящий
class Foo:
    def __init__(self, y, x):
        self.y = y
        self.x = x
    def __repr__(self):
        return f'Foo({self.y}, {self.x})'

In [69]: Foo(1,2)
Out[69]: Foo(1, 2)

Предлагаемое nditer решение:

def foo1(n):
    a = np.empty((n,n), dtype=object)
    with np.nditer(a, flags=['multi_index', 'refs_ok'], op_flags=['writeonly']) as it:
        for x in it:
            x[...] = Foo(it.multi_index[1], it.multi_index[0])
    return a

In [70]: foo1(2)
Out[70]: 
array([[Foo(0, 0), Foo(1, 0)],
       [Foo(0, 1), Foo(1, 1)]], dtype=object)

Вложенный цикл:

def foo2(n):
    a = np.empty((n,n), dtype=object)
    for i in range(n):
        for j in range(n):
            a[i,j] = Foo(i,j)
    return a

In [71]: foo2(2)
Out[71]: 
array([[Foo(0, 0), Foo(0, 1)],
       [Foo(1, 0), Foo(1, 1)]], dtype=object)

мой любимый, frompyfunc:

def foo3(n):
    f = np.frompyfunc(Foo, 2, 1)
    I,J = np.meshgrid(np.arange(n),np.arange(n), sparse=True)
    return f(I,J)

In [72]: foo3(2)
Out[72]: 
array([[Foo(0, 0), Foo(1, 0)],
       [Foo(0, 1), Foo(1, 1)]], dtype=object)

тайминги:

In [73]: timeit foo1(200)
144 ms ± 305 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [74]: timeit foo2(200)
25.7 ms ± 958 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [75]: timeit foo3(200)
17.7 ms ± 40.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

И для сравнения вложенный список:

def foo4(n):
    alist = []
    for i in range(n):
        blist = []
        for j in range(n):
            blist.append(Foo(i,j))
        alist.append(blist)
    return alist

In [77]: foo4(2)
Out[77]: [[Foo(0, 0), Foo(0, 1)], [Foo(1, 0), Foo(1, 1)]]
In [78]: timeit foo4(200)
18.6 ms ± 149 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Другие вопросы по теме