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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.