0

Imagine a class like this.

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

Then I have an array

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

I want to fill it with full of the class but with the input parameters as the index of the array.

I've toyed with the idea of np.where but cant see how I can add the parameters.

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

Currently I'm doing this disgusting nested loop. 1) i know its not the right way 2) it takes forever.

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

Someone please push me in the right direction before I go crazy.

5
  • " want to fill it with full of the class but with the input parameters as the index of the array." Why would you want to do that? If you are going to do that, you might as well just use the nested Python loop - using an object-type array negates the advantages of using a numpy.ndarray. If your class is actually something like this, i.e. basic container of attributes, then maybe you could use a structured array instead and keep the benefits of numpy Commented Dec 11, 2020 at 20:23
  • numpy.fromiter and an iterator returning Foo instances might work. It will make an 1-D array but you can reshape it afterwards. However, it might be a bit slow anyways, and use a lot of memory. Are you sure that you actually need 40,000 instances of a class? Commented Dec 11, 2020 at 20:26
  • @juanpa.arrivillaga Oh man, I wish I knew why I want it this way. My thinking is meant to hold more information about a pixel and I've got an image array and then this one. Its so I can easily get the object via the index from mouse clicks and the X,Y coords. Its a long story, sure there is probably a better way but my brain is getting fried. Commented Dec 11, 2020 at 20:26
  • @LewisMorris sure, by why not just a list? Commented Dec 11, 2020 at 20:33
  • just purely because i'm using numpy and I know how the np.zeros works. I think i'm going to just put in a flat list and write a function to convert the index to coords. something like (int(index/height), index%height) should work i think. Commented Dec 11, 2020 at 20:37

2 Answers 2

2
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)

The proposed nditer solution:

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)

The nested loop:

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)

my favorite, 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)

timings:

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)

And for comparison, a nested list:

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)
Sign up to request clarification or add additional context in comments.

Comments

0

Came across this well documented Iterating Over Arrays site-

This should be able to solve your problem, I've tried it for 200x200 as well and it hardly takes any time.

Note: Make sure the numpy version is >=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>]]

2 Comments

This is slower than the nested loop. np.nditer is, in my experience, only helpful as a step toward use in compiled code. The python interface is slow.
@hpaulj I should've compared, thanks for pointing it out! +1 to your answer, cheers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.