Reputation: 61
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
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
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
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
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
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