numpy - return index If value inside one of 3d array
<p>How to do this in Numpy : Thank you!</p>

<p><strong>Input :</strong> </p>

<pre><code>A = np.array([0, 1, 2, 3]) 

B = np.array([[3, 2, 0], [0, 2, 1], [2, 3, 1], [3, 0, 1]]) 
</code></pre>

<hr>

<p><strong>Output :</strong></p>

<pre><code>result = [[0, 1, 3], [1, 2, 3], [0, 1, 2], [0, 2, 3]]
</code></pre>

<hr>

<p>in Python :</p>

<pre><code>A = np.array([0 ,1 ,2 ,3]) 
B = np.array([[3 ,2 ,0], [0 ,2 ,1], [2 ,3 ,1], [3 ,0 ,1]]) 
result = []
for x ,  valA in enumerate (A) :
    inArray = []
    for y , valB in enumerate (B) : 
        if valA in valB:
            inArray.append (y)
    result.append (inArray)
print result

# result = [[0, 1, 3], [1, 2, 3], [0, 1, 2], [0, 2, 3]]
</code></pre>

<p><strong>Approach #1</strong></p>

<p>Here's a NumPy vectorized approach using <a href="http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html" rel="nofollow"><code>broadcasting</code></a> -</p>

<pre><code>R,C = np.where((A[:,None,None] == B).any(-1))
out = np.split(C,np.flatnonzero(R[1:]&gt;R[:-1])+1)
</code></pre>

<p><strong>Approach #2</strong></p>

<p>Assuming <code>A</code> and <code>B</code> to hold positive numbers, we can consider those to represent indices on a <code>2D</code> grid, such that <code>B</code> could be considered to hold the column indices on per row basis. Once that <code>2D</code> grid corresponding to <code>B</code> is in place, we just need to consider only the columns that are intersected by <code>A</code>. Finally, we get the indices of <code>True</code> values in a such a <code>2D</code> grid to give us <code>R</code> and <code>C</code> values. This should be much more memory-efficient. </p>

<p>Thus, the alternative approach would look something like this -</p>

<pre><code>ncols = B.max()+1
nrows = B.shape[0]
mask = np.zeros((nrows,ncols),dtype=bool)
mask[np.arange(nrows)[:,None],B] = 1
mask[:,~np.in1d(np.arange(mask.shape[1]),A)] = 0
R,C = np.where(mask.T)
out = np.split(C,np.flatnonzero(R[1:]&gt;R[:-1])+1)
</code></pre>

<p>Sample run -</p>

<pre><code>In [43]: A
Out[43]: array([0, 1, 2, 3])

In [44]: B
Out[44]: 
array([[3, 2, 0],
       [0, 2, 1],
       [2, 3, 1],
       [3, 0, 1]])

In [45]: out
Out[45]: [array([0, 1, 3]), array([1, 2, 3]), array([0, 1, 2]), array([0, 2, 3])]
</code></pre>

<p><strong>Runtime test</strong></p>

<p>Scaling up the dataset sizes by <code>100x</code>, here's a quick runtime test result -</p>

<pre><code>In [85]: def index_1din2d(A,B):
    ...:     R,C = np.where((A[:,None,None] == B).any(-1))
    ...:     out = np.split(C,np.flatnonzero(R[1:]&gt;R[:-1])+1)
    ...:     return out
    ...: 
    ...: def index_1din2d_initbased(A,B):
    ...:     ncols = B.max()+1
    ...:     nrows = B.shape[0]
    ...:     mask = np.zeros((nrows,ncols),dtype=bool)
    ...:     mask[np.arange(nrows)[:,None],B] = 1
    ...:     mask[:,~np.in1d(np.arange(mask.shape[1]),A)] = 0
    ...:     R,C = np.where(mask.T)
    ...:     out = np.split(C,np.flatnonzero(R[1:]&gt;R[:-1])+1)
    ...:     return out
    ...: 

In [86]: A = np.unique(np.random.randint(0,10000,(400)))
    ...: B = np.random.randint(0,10000,(400,300))
    ...: 

In [87]: %timeit [np.where((B == x).sum(axis = 1))[0] for x in A]
1 loop, best of 3: 161 ms per loop # @Psidom's soln

In [88]: %timeit index_1din2d(A,B)
10 loops, best of 3: 91.5 ms per loop

In [89]: %timeit index_1din2d_initbased(A,B)
10 loops, best of 3: 33.4 ms per loop
</code></pre>

<p><strong>Further performance-boost!</strong></p>

<p>Well, alternatively we can create the <code>2D</code> grid in the second approach in a transposed way. The idea is to avoid the transpose in <code>R,C = np.where(mask.T)</code>, which seemed like the bottleneck. So, a modified version of the second approach and the associated runtimes would look something like this -</p>

<pre><code>In [135]: def index_1din2d_initbased_v2(A,B):
     ...:     nrows = B.max()+1
     ...:     ncols = B.shape[0]
     ...:     mask = np.zeros((nrows,ncols),dtype=bool)
     ...:     mask[B,np.arange(ncols)[:,None]] = 1
     ...:     mask[~np.in1d(np.arange(mask.shape[0]),A)] = 0
     ...:     R,C = np.where(mask)
     ...:     out = np.split(C,np.flatnonzero(R[1:]&gt;R[:-1])+1)
     ...:     return out
     ...: 

In [136]: A = np.unique(np.random.randint(0,10000,(400)))
     ...: B = np.random.randint(0,10000,(400,300))
     ...: 

In [137]: %timeit index_1din2d_initbased(A,B)
10 loops, best of 3: 57.5 ms per loop

In [138]: %timeit index_1din2d_initbased_v2(A,B)
10 loops, best of 3: 25.9 ms per loop
</code></pre>

python arrays numpy indexing collect