Python: Check if a numpy array contains an object with specific attribute
<p>I want to check if there is any object with a specific attribute in my numpy array:</p>

<pre><code>class Test:
def __init__(self, name):
    self.name = name

l = numpy.empty( (2,2), dtype=object)
l[0][0] = Test("A")
l[0][1] = Test("B")
l[1][0] = Test("C")
l[1][1] = Test("D")
</code></pre>

<p>I know that following line of code working for a list, but what is the alternative process for a numpy array?</p>

<pre><code>print numpy.any(l[:,0].name == "A")
</code></pre>

<p>There are a number SO questions about object dtype arrays, and even some about getting or testing the attributes of the elements of such arrays.  The general point is that such an array behaves much like a list.  For the most part you have to iterate over the array as though it were a list.  Most of the cool things that you can do with numeric arrays like <code>add</code>, <code>multiply</code> etc do not apply.  Or your objects have to implement specific methods for the actions to propagate down to the objects.</p>

<pre><code>In [15]: class Test:
    ...:     def __init__(self,name):
    ...:         self.name=name
    ...:     def __repr__(self):   # added for nice display
    ...:         return 'Test:%s'%self.name
    ...:     
In [16]: A = np.empty((2,2),dtype=object)
</code></pre>

<p>I can assign all the values at once using <code>flat[:]</code> and list:</p>

<pre><code>In [17]: A.flat[:]=[Test('A'),Test('B'),Test('C'),Test('D')]

In [18]: A      # try this display without the `repr`
Out[18]: 
array([[Test:A, Test:B],
       [Test:C, Test:D]], dtype=object)
</code></pre>

<p>this returns False because I did not define a <code>cmp</code> for the class; that is <code>Test('A')==Test('A')</code> is also False.</p>

<pre><code>In [19]: Test('A') in A
Out[19]: False


In [20]: A[0,1].name
Out[20]: 'B'
</code></pre>

<p>This is true because it is an identity test </p>

<pre><code>In [21]: A[0,1] in A
Out[21]: True
</code></pre>

<p>Since <code>A</code> is 2d, a simple list comprehension on it does not work, at least not for testing attributes.  <code>a</code> in this case is a row of <code>A</code>, a 1d object array:</p>

<pre><code>In [23]: [a.name for a in A]
...
AttributeError: 'numpy.ndarray' object has no attribute 'name'
</code></pre>

<p>To get the names I have to iterated on <code>A.flat</code>; I can apply the <code>in</code> test to the resulting list:</p>

<pre><code>In [24]: [a.name for a in A.flat]
Out[24]: ['A', 'B', 'C', 'D']
In [25]: 'B' in [a.name for a in A.flat]
Out[25]: True
</code></pre>

<p><code>np.vectorize</code> is a way of writing functions that operate on arrays of various shapes.  It uses <code>np.frompyfunc</code>, which in this case works just as well, if not better.</p>

<pre><code>In [27]: foo = np.frompyfunc(lambda a: a.name, 1,1)
In [28]: foo(A)
Out[28]: 
array([['A', 'B'],
       ['C', 'D']], dtype=object)
In [29]: 'C' in foo(A)
Out[29]: True
</code></pre>

<p>Or I could define a version that does the <code>name</code> equality test.  Notice this takes 2 inputs</p>

<pre><code>In [30]: bar = np.frompyfunc(lambda a,b: b == a.name, 2, 1)

In [32]: bar(A,'C')
Out[32]: 
array([[False, False],
       [True, False]], dtype=object)
</code></pre>

<p>I can even test 2 arrays against each other with broadcasting:</p>

<pre><code>In [37]: bar(A,np.array(['A','D'])[:,None,None])
Out[37]: 
array([[[True, False],
        [False, False]],

       [[False, False],
        [False, True]]], dtype=object)
</code></pre>

<p><code>frompyfunc</code> iterates as the <code>[a for a in A.flat]</code> does, but is somewhat faster, and throws all the power of numpy broadcasting at the task.</p>

python python-2.7 numpy