3

I have 2 numpy arrays such as:

arr1 = np.array([[0,5,5,0],[0,5,5,0]])
arr2 = np.array([[7,7,0,0],[7,7,0,0]])

I'd like to copy non zero elements in arr2 into corresponding position in arr1 resulting the following array:

arr1 = np.array([[7,7,5,0],[7,7,5,0]])

2 Answers 2

9

You can use three alternatives:

arr1[arr2 > 0] = arr2[arr2 > 0]
arr1[np.where(arr2>0)] = arr2[np.where(arr2>0)]
arr1[arr2.nonzero()] = arr2[arr2.nonzero()]

But a winner is np.copyto(arr1, arr2, where=arr2 != 0), thanks @Mark Meyer.

Every of these 4 methods changes arr1 into

array([[7, 7, 5, 0],
       [7, 7, 5, 0]])

If you don't want side effects in arr1, use arr = arr1.copy() instead and ten replace it in analogous way.

Update

Let's look at results of perfplot

import perfplot

def simple(arr):
    arr1, arr2 = arr
    arr1[arr2 != 0] = arr2[arr2 != 0]
    return arr1
def where(arr):
    arr1, arr2 = arr
    arr1[np.where(arr2 != 0)] = arr2[np.where(arr2 != 0)]
    return arr1
def nonzero(arr):
    arr1, arr2 = arr
    arr1[arr2.nonzero()] = arr2[arr2.nonzero()]
    return arr1
def simple_improve(arr):
    arr1, arr2 = arr
    idx = arr2 != 0
    arr1[idx] = arr2[idx]
    return arr1
def where_improve(arr):
    arr1, arr2 = arr
    idx = np.where(arr2 != 0)
    arr1[idx] = arr2[idx]
    return arr1
def nonzero_improve(arr):
    arr1, arr2 = arr
    idx = arr2.nonzero()
    arr1[idx] = arr2[idx]
    return arr1
def copyto(arr): #thanks @Mark Meyer
    arr1, arr2 = arr
    np.copyto(arr1, arr2, where=arr2 != 0)
    return arr1
import numexpr as ne
def copyto_numexpr(arr):
    #some magic boost
    arr1, arr2 = arr
    np.copyto(arr1, arr2, where=ne.evaluate('arr2 != 0'))
    return arr1

perfplot.show(
    setup=lambda n: (np.tile(np.array([[0, 5, 5, 0], [0, 5, 5, 0]]), (n, n)),
                     np.tile(np.array([[7, 7, 0, 0], [7, 7, 0, 0]]), (n, n))),
    # setup=lambda n: [list(range(n))] * 10,
    kernels=[simple, where, nonzero,
             simple_improve, where_improve, nonzero_improve, 
             copyto, copyto_numexpr],
    n_range=[2 ** k for k in range(12)],
    xlabel="n*n copies of array of shape (2,4)")

enter image description here

Sign up to request clarification or add additional context in comments.

8 Comments

In performance wise which would be faster? I'm dealing with 20k*30k arrays here. @mathfux
I'm not sure, I guess that nonzero. Although, all the cases use repetition of the same indexes so this is double loss. Going to update an answer to measure it soon :)
So maybe declaring a = arr2.nonzero() and then using arr1[a] = arr2[a] could be a bit faster? @mathfux
Wait, oops! There is a new winner :(
Nice analysis @mathfux!
|
3

Since you want to mutate arr1, you can just assign with boolean indexing:

import numpy as np

arr1 = np.array([[0,5,5,0],[0,5,5,0]])
arr2 = np.array([[7,7,0,0],[7,7,0,0]])

arr1[arr2 != 0] = arr2[arr2 != 0]

print(arr1)

# [[7 7 5 0]
#  [7 7 5 0]]

You can pick up a little performance on large arrays using copyto():

np.copyto(arr1, arr2, where=arr2 != 0)

Comments

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.