Add value only if value is greater than zero in Python multidimensional array

I have an array like this: tmp.shape = (128, 64, 64)

I calculate all zeros along axis 128 as follows:

 nonzeros = np.count_nonzero(tmp, axis=0) // shape = (64, 64) 

I have an array c.shape = (64, 64)

Now I want to add the values ​​of c to tmp along the 128 axis, but only if the tmp values ​​are> 0:

 for i in range(tmp.shape[0]): axis1 = tmp[i,:,:] tmp[i, :, :] += (c / nonzeros) // only if tmp[i, :, :] > 0 

Can this be done briefly? Or do I need to use multiple loops? I hope anyone can offer a solution without another cycle

Something like this does not work:

 tmp[i, tmp > 0.0, tmp > 0.0] += (c / nonzeros) 

IndexError: Too many indexes for an array

LONG TERM

 for i in range(tmp.shape[0]): for x in range(tmp.shape[1]): for y in range(tmp.shape[2]): pixel = tmp[i, x, y] if pixel != 0: pixel += (c[x,y] / nonzeros[x,y]) 
+5
source share
2 answers

Well, you basically add c/nonzeros to the tmp array in the broadcast, except where the tmp element is zero. Thus, one way would be to save the 0s upfront mask, add to c/nonzeros and finally use the mask for the reset tmp elements.

Therefore, the implementation will be -

 mask = tmp==0 tmp+= c/nonzeros tmp[mask] = 0 

Runtime test

Approaches -

 # @DSM soln def fast(tmp, c, nonzeros): return tmp + np.where(tmp > 0, c/nonzeros, 0) # Proposed in this post def fast2(tmp, c, nonzeros): mask = tmp==0 tmp+= c/nonzeros tmp[mask] = 0 return tmp 

Dates -

 In [341]: # Setup inputs ...: M,N = 128,64 ...: tmp = np.random.randint(0,10,(M,N,N)).astype(float) ...: c = np.random.rand(N,N)*100 ...: nonzeros = np.count_nonzero(tmp, axis=0) ...: ...: # Make copies for testing as input would be edited with the approaches ...: tmp1 = tmp.copy() ...: tmp2 = tmp.copy() ...: In [342]: %timeit fast(tmp1, c, nonzeros) 100 loops, best of 3: 2.22 ms per loop In [343]: %timeit fast2(tmp2, c, nonzeros) 1000 loops, best of 3: 1.61 ms per loop 

Shorter alternative

If you are looking for compact code, here is another, using a non-0s mask to do a translation using elementary multiplication with c/nonzeros and add to tmp and therefore have a one-line solution, like

 tmp += (tmp!=0)*(c/nonzeros) 

Note. . To avoid dividing by 0 , we could edit nonzeros by 0s with anything other than 0 , say 1 , and then use the published approaches, as well -

 nonzeros = np.where(nonzeros > 0, nonzeros, 1) 
+1
source

You can use np.where and broadcast. After correcting the code of your example (adding to the pixel will not change tmp),

 def fast(tmp, c, nonzeros): return tmp + np.where(tmp > 0, c/nonzeros, 0) 

gives me

 In [6]: tmp = np.random.randint(0, 5, (128, 64, 64)).astype(float) ...: c = np.random.randint(10, 15, (64, 64)).astype(float) ...: nonzeros = np.count_nonzero(tmp, axis=0) ...: In [7]: %time slow_result = slow(tmp, c, nonzeros) CPU times: user 488 ms, sys: 16 ms, total: 504 ms Wall time: 553 ms In [8]: %time fast_result = fast(tmp, c, nonzeros) CPU times: user 4 ms, sys: 4 ms, total: 8 ms Wall time: 16.4 ms In [9]: np.allclose(slow_result, fast_result) Out[9]: True 

Alternatively, you can often replace np.where with multiplication, something like tmp + (tmp > 0) * (c/nonzeros) .

Changing the code to protect against a situation in which the element of nonzero values ​​is zero remains as an exercise for the reader .; -)

+1
source

Source: https://habr.com/ru/post/1269114/


All Articles