Представьте себе такой класс.
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)
Кто-нибудь, пожалуйста, подтолкните меня в правильном направлении, прежде чем я сойду с ума.
numpy.fromiter
и итератор, возвращающий Foo
экземпляры, может работать. Он создаст одномерный массив, но впоследствии вы сможете изменить его форму. Однако в любом случае это может быть немного медленным и использовать много памяти. Вы уверены, что вам действительно нужно 40 000 экземпляров класса?
@juanpa.arrivillaga О, чувак, хотел бы я знать, почему я этого хочу. Мое мышление предназначено для хранения дополнительной информации о пикселе, и у меня есть массив изображений, а затем это. Так что я могу легко получить объект через индекс от щелчков мыши и координат X, Y. Это длинная история, конечно, есть лучший способ, но мой мозг поджаривается.
@LewisMorris конечно, почему бы не просто список?
просто потому, что я использую numpy и знаю, как работает np.zeros. Я думаю, что я собираюсь просто добавить плоский список и написать функцию для преобразования индекса в координаты. что-то вроде (int(index/height), index%height) должно работать, я думаю.
Наткнулся на этот хорошо документированный сайт 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 Я должен был сравнить, спасибо, что указали на это! +1 к вашему ответу, ура.
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)
«Хотите заполнить его полным классом, но с входными параметрами в качестве индекса массива». Почему вы хотите это сделать? Если вы собираетесь это сделать, вы можете просто использовать вложенный цикл Python — использование массива объектного типа сводит на нет преимущества использования
numpy.ndarray
. Если ваш класс на самом деле что-то вроде этого, то есть базовый контейнер атрибутов, то, возможно, вы могли бы вместо этого использовать структурированный массив и сохранить преимущества numpy