rNov
rNov

Reputation: 61

Python: Comparing all elements of two arrays and modifying 2nd array

New to Python, and have been learning about arrays. I am stuck with a simple enough problem and need a solution. I have two arrays:

a = [2.0, 5.1, 6.2, 7.9, 23.0]     # always increasing
b = [5.1, 5.5, 5.7, 6.2, 00.0]     # also always increasing

and I want the resultant array to be:

c = [0.0, 5.1, 6.2, 0.0, 0.0]      # 5.5, 5.7, 00.0 from 'b' were dropped and rearranged such that position of equivalent elements as in 'a' are maintained

I have compared both 'a' & 'b' using Numpy as in:

y = np.isclose(a, b)
print y
# [False False False False False]

(Alternately,) I also tried something like this, which isn't the right way (I think):

c = np.zeros(len(a))
for i in range (len(a)):
    for j in range (len(a)):
        err = abs(a[i]-b[j])
        if err == 0.0 or err < abs(1):
            print (err, a[i], b[j], i, j)
        else:
            print (err, a[i], b[j], i, j)

How do I proceed from here towards obtaining 'c'?

Upvotes: 3

Views: 3159

Answers (5)

hruske
hruske

Reputation: 2253

These solutions work even when the arrays are of different size.

Simple version

c = []

for i in a:
    if any(np.isclose(i, b)):
        c.append(i)
    else:
        c.append(0.0)

Numpy version

aa = np.tile(a, (len(b), 1))
bb = np.tile(b, (len(a), 1))
cc = np.isclose(aa, bb.T)
np.any(cc, 0)
c = np.zeros(shape=a.shape)
result = np.where(np.any(cc, 0), a, c)

Explained:

I will be doing matrix comparison here. First you expand the arrays into matrices. Lengths are exchanged, which creates matrices having equal size of one dimension:

aa = np.tile(a, (len(b), 1))
bb = np.tile(b, (len(a), 1))

They look like this:

# aa
array([[  2. ,   5.1,   6.2,   7.9,  23. ],
       [  2. ,   5.1,   6.2,   7.9,  23. ],
       [  2. ,   5.1,   6.2,   7.9,  23. ],
       [  2. ,   5.1,   6.2,   7.9,  23. ],
       [  2. ,   5.1,   6.2,   7.9,  23. ]])

# bb
array([[ 5.1,  5.5,  5.7,  6.2,  0. ],
       [ 5.1,  5.5,  5.7,  6.2,  0. ],
       [ 5.1,  5.5,  5.7,  6.2,  0. ],
       [ 5.1,  5.5,  5.7,  6.2,  0. ],
       [ 5.1,  5.5,  5.7,  6.2,  0. ]])

Then compare them. Note that bb is transposed:

cc = np.isclose(aa, bb.T)

And you get:

array([[False,  True, False, False, False],
       [False, False, False, False, False],
       [False, False, False, False, False],
       [False, False,  True, False, False],
       [False, False, False, False, False]], dtype=bool)

You can aggregate this by axis 0:

np.any(cc, 0)

which returns

array([False,  True,  True, False, False], dtype=bool)

Now create array c:

c = np.zeros(shape=a.shape)

And select appropriate value, either from a or c:

np.where(np.any(cc, 0), a, c)

And the result:

array([ 0. ,  5.1,  6.2,  0. ,  0. ])

Upvotes: 4

B. M.
B. M.

Reputation: 18658

It seems that you want to keep elements of a that are also in b.

A pure linear time python solution :

c=zeros_like(a)

j=0
n=len(c)
for i in range(n):
    while j<n and b[j]<a[i]-.1 : j+=1
    if j==n : break
    if abs(a[i]-b[j])<.1 : c[i]=a[i]

And a numpy solution for exact matching:

a*in1d(a,b).

in1d(a,b) indicates the places of elements of a that are in b : in1d(a,b) is [False, True, True, False, False]

Since True is 1 and False is 0 , a*in1d(a,b) is [ 0., 5.1, 6.2, 0. , 0. ] . Since in1d sorts a and b, it is a n ln n complexity algorithm, but generally faster. if approximative equality is required, a solution can be rounding the arrays first (np.round(a,1))

Upvotes: 1

rNov
rNov

Reputation: 61

This is the alternate way I was able to obtain the required arrangement for c.

import numpy as np

a = [2.0, 5.1, 6.2, 7.9, 23.0]  # always increasing
b = [5.1, 5.5, 5.7, 6.2, 00.0]  # also always increasing
c = np.zeros(len(a))

for i in range (len(a)):
    for j in range (len(a)):
        err = abs(a[i]-b[j])
        if err == 0.0 or err < abs(0.1):
            c[i] = b[j]

print c
#[ 0.   5.1  6.2  0.   0. ]

Upvotes: 0

Sci Prog
Sci Prog

Reputation: 2691

Try to better explain what your program should do in a more general way. Only giving arrays a, b and c does not tell what it should do. It is as if someone said "If A=5 and B=7, write a program so that C=20".

From what you tried, I'd guess that the task is "each element of c should be equal to the corresponding element of a if its value is near (difference of 0.5 or less) to the corresponding value in b. It should be zero if not."

Also, do you really need to use numpy? Try using only loops and list methods. You may also have a look at "Generator expressions and list comprehensions"

Finally, your title says "(...) and modifying 2nd array". There should not be a third array named c. The result should appear in a modified version of array b.


Edited: if the specification was really this, then the code could be

a = [2.0, 5.1, 6.2, 7.9, 23.0]
b = [5.1, 5.5, 5.7, 6.2, 0.0]
c = []
for x,y in zip(a,b): c.append( x if abs(x-y)<=0.5 else 0.0 )
print c

Which gives the following answer

[0.0, 5.1, 6.2, 0.0, 0.0]

BTW, if this is for a course, you could still get a bad grade for not following the specification ("...and modifying the 2nd array").

Upvotes: 1

MSeifert
MSeifert

Reputation: 152725

With np.isclose you already create an array where the "closest" elements are True. So you can use this result to set all other elements to zero.

import numpy as np
a = np.array([2.0, 5.1, 6.2, 7.9, 23.0])     # always increasing
b = np.array([5.1, 5.5, 5.7, 6.2, 00.0])     # also always increasing
a[~np.isclose(a,b, atol=0.5)] = 0
a

this returns array([ 0. , 5.1, 6.2, 0. , 0. ]).

But notice you want to set all elements that are not close, so you need to invert (~) the result.

Upvotes: 1

Related Questions