Return difference of two 2D arrays - python

I have 2 2d arrays and I would like to return all values that are differing in the second array while keeping the existing dimensions.
I've done something like diff = arr2[np.nonzero(arr2-arr1)] works to give me the differing elements but how do I keep the dimensions and relative position of the elements?
Example Input:
arr1 = [[0 1 2] arr2 = [[0 1 2]
[3 4 5] [3 5 5]
[6 7 8]] [6 7 8]]
Expected output:
diff = [[0 0 0]
[0 5 0]
[0 0 0]]

How about the following:
import numpy as np
arr1 = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
arr2 = np.array([[0, 1, 2], [3, 5, 5], [6, 7, 8]])
diff = arr2 * ((arr2 - arr1) != 0)
print(diff)
# [[0 0 0]
# [0 5 0]
# [0 0 0]]
EDIT: Surprisingly to me, the following first version of my answer (corrected by OP) might be faster:
diff = arr2 * np.abs(np.sign(arr2 - arr1))

If they are numpy arrays, you could do
ans = ar1 * 0
ans[ar1 != ar2] = ar2[ar1 != ar2]
ans
# array([[0, 0, 0],
# [0, 5, 0],
# [0, 0, 0]])
Without numpy, you can use map
list(map(lambda a, b: list(map(lambda x, y: y if x != y else 0, a, b)), arr1, arr2))
# [[0, 0, 0], [0, 5, 0], [0, 0, 0]]
Data
import numpy as np
arr1 = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
arr2 = [[0, 1, 2], [3, 5, 5], [6, 7, 8]]
ar1 = np.array(arr1)
ar2 = np.array(arr2)

I am surprised no one proposed the numpy.where method:
diff = np.where(arr1!=arr2, arr2, 0)
Literally, where arr1 and arr2 are different take the values of arr2, else take 0.
Output:
array([[0, 0, 0],
[0, 5, 0],
[0, 0, 0]])

np.copyto
You can check for inequality between the two arrays then use np.copyto with np.zeros/ np.zeros_like.
out = np.zeros(arr2.shape) # or np.zeros_like(arr2)
np.copyto(out, arr2, where=arr1!=arr2)
print(out)
# [[0 0 0]
# [0 5 0]
# [0 0 0]]
np.where
You can use np.where and specify x, y args.
out = np.where(arr1!=arr2, arr2, 0)
# [[0 0 0]
# [0 5 0]
# [0 0 0]]

Related

Reorder a square array using a sorted 1D array

Let's say I have a symmetric n-by-n array A and a 1D array x of length n, where the rows/columns of A correspond to the entries of x, and x is ordered. Now assume both A and x are randomly rearranged, so that the rows/columns still correspond but they're no longer in order. How can I manipulate A to recover the correct order?
As an example: x = array([1, 3, 2, 0]) and
A = array([[1, 3, 2, 0],
[3, 9, 6, 0],
[2, 6, 4, 0],
[0, 0, 0, 0]])
so the mapping from x to A in this example is A[i][j] = x[i]*x[j]. x should be sorted like array([0, 1, 2, 3]) and I want to arrive at
A = array([[0, 0, 0, 0],
[0, 1, 2, 3],
[0, 2, 4, 6],
[0, 3, 6, 9]])
I guess that OP is looking for a flexible way to use indices that sorts both rows and columns of his mapping at once. What is more, OP might be interested in doing it in reverse, i.e. find and initial view of mapping if it's lost.
def mapping(x, my_map, return_index=True, return_inverse=True):
idx = np.argsort(x)
out = my_map(x[idx], x[idx])
inv = np.empty_like(idx)
inv[idx] = np.arange(len(idx))
return out, idx, inv
x = np.array([1, 3, 2, 0])
a, idx, inv = mapping(x, np.multiply.outer) #sorted mapping
b = np.multiply.outer(x, x) #straight mapping
print(b)
>>> [[1 3 2 0]
[3 9 6 0]
[2 6 4 0]
[0 0 0 0]]
print(a)
>>> [[0 0 0 0]
[0 1 2 3]
[0 2 4 6]
[0 3 6 9]]
np.array_equal(b, a[np.ix_(inv, inv)]) #sorted to straight
>>> True
np.array_equal(a, b[np.ix_(idx, idx)]) #straight to sorted
>>> True
A simple implementation would be
idx = np.argsort(x)
A = A[idx, :]
A = A[:, idx]
Another possibility would be (all credit to #mathfux):
A[np.ix_(idx, idx)]
You can use argsort and fancy indexing:
idx = np.argsort(x)
A2 = A[idx[None], idx[:,None]]
output:
array([[0, 0, 0, 0],
[0, 1, 2, 3],
[0, 2, 4, 6],
[0, 3, 6, 9]])

Transform an array to 1s and 0s using another array

I have two arrays
arr1 = np.array([[4, 1, 3, 2, 5], [5, 2, 4, 1, 3]])
arr2 = np.array([[2], [1]])
I want to transform array 1 to a binary array using the elements of the array 2 in the following way
For row 1 of array 1, I want to use the row 1 of array 2 i.e. 2 - to make the top 2 values of array 1 as 1s and the rest as 0s
Similarly for row 2 of array 1, I want to use the row 2 of array 2 i.e. 1 - to make the top 1 value of array 1 as 1s and the rest as 0s
So arr1 would get transformed as follows
arr1_transformed = np.array([[1, 0, 0, 0, 1], [1, 0, 0, 0, 0]])
Here is what I tried.
arr1_sorted_indices = np.argosrt(-arr1)
This gave me the indices of the sorted array
array([[1, 3, 2, 0, 4],
[3, 1, 4, 2, 0]])
Now I think I need to mask this array with the help of arr2 to get the desired output and I'm not sure how to do it.
this should do the job in the mentioned case:
def trasform_arr(arr1,arr2):
for i in range(0,len(arr1)):
if i >= len(arr2):
arr1[i] = [0 for x in arr1[i]]
else:
sorted_arr = sorted(arr1[i])[-arr2[i][0]:]
arr1[i] = [1 if x in sorted_arr else 0 for x in arr1[i]]
arr1 = [[4, 1, 3, 2, 5], [5, 2, 4, 1, 3]]
arr2 = [[2], [1]]
trasform_arr(arr1,arr2)
print(arr1)
You can try the following:
import numpy as np
arr1 = np.array([[4, 1, 3, 2, 5], [5, 2, 4, 1, 3]])
arr2 = np.array([[2],[1]])
r, c = arr1.shape
s = np.argsort(np.argsort(-arr1))
out = (np.arange(c) < arr2)[np.c_[0:r], s] * 1
print(out)
It gives:
[[1 0 0 0 1]
[1 0 0 0 0]]

How to obtain bottom diagonals of a matrix according to an index?

I would like to know how it is possible to obtain the diagonals that point downwards (left and right) with respect to a specific index of the matrix.
To be more graphic I will give the following example of an expected output:
matrix = np.array([[2, 0, 0, 2],
[3, 9, 8, 3],
[3, 0, 0, 2],
[0, 0, 0, 0]])
Expected output results for position: matrix[1][2]
matrix[1][2]
8
diag_right = [2]
diag_left = [0, 0]
Same example but using the matrix in matrix[1][2]
matrix = np.array([[x, x, x, x],
[x, x, 8, x],
[x, 0, x, 2],
[0, x, x, x]])
An easy way using numpy would be:
(i've changed the matrix to make more clear some results in test_ij)
import numpy as np
def get_right_left_diags(matrix,i,j):
n = len(matrix)
left_diag = np.diag(matrix[i+1:n,j+1:n])
right_diag = np.diag( matrix[i+1:n, j-1::-1])
return right_diag,left_diag
%lets check some results
matrix = np.array([[2, 0, 0, 2],
[3, 9, 8, 3],
[3, 0, 0, 2],
[1, 2, 3, 4]])
n = len(matrix)
cases = [[0,0],[0,3],[1,1],[1,2],[2,1]]
for i,j in cases:
right_diag,left_diag = get_right_left_diags(matrix,i,j)
print(f"i={i}, j={j}, left_diag: {left_diag} \t right_diag: {right_diag}")
this will output:
#i=0, j=0, left_diag: [9 0 4] right_diag: [3 0 2]
#i=0, j=3, left_diag: [] right_diag: [8 0 1]
#i=1, j=1, left_diag: [0 4] right_diag: [3]
#i=1, j=2, left_diag: [2] right_diag: [0 1]
#i=2, j=1, left_diag: [3] right_diag: [1]
for me it has total sense.
matrix = np.array([[2, 0, 0, 2],
[3, 9, 8, 3],
[3, 0, 0, 2],
[0, 0, 0, 0]])
i = 1; j = 2
diag_right = []; diag_left = []
for k in range(1, len(matrix) - i):
if(j+k < len(matrix[0])):
diag_right.append(matrix[i+k][j+k])
if(j-k >= 0):
diag_left.append(matrix[i+k][j-k])
Is this what you're looking for?

Pad elements in ndarray using unique padding for each element

I am quite new to python and have read lots of SO questions on this topic however none of them answers my needs.
I end up with an ndarray:
[[1, 2, 3]
[4, 5, 6]]
Now I want to pad each element (e.g. [1, 2, 3]) with a tailored padding just for that element. Of course I could do it in a for loop and append each result to a new ndarray but isn't there a faster and cleaner way I could apply this over the whole ndarray at once?
I imagined it could work like:
myArray = [[1, 2, 3]
[4, 5, 6]]
paddings = [(1, 2),
(2, 1)]
myArray = np.pad(myArray, paddings, 'constant')
But of course this just outputs:
[[0 0 0 0 0 0 0 0 0]
[0 0 1 2 3 0 0 0 0]
[0 0 3 4 5 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]]
Which is not what i need. The target result would be:
[[0 1 2 3 0 0]
[0 0 4 5 6 0]]
How can I achieve this using numpy?
Here is a loop based solution but with creating a zeros array as per the dimensions of input array and paddings. Explanation in comments:
In [192]: myArray
Out[192]:
array([[1, 2, 3],
[4, 5, 6]])
In [193]: paddings
Out[193]:
array([[1, 2],
[2, 1]])
# calculate desired shape; needed for initializing `padded_arr`
In [194]: target_shape = (myArray.shape[0], myArray.shape[1] + paddings.shape[1] + 1)
In [195]: padded_arr = np.zeros(target_shape, dtype=np.int32)
In [196]: padded_arr
Out[196]:
array([[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]], dtype=int32)
After this, we can use a for loop to slot fill the sequences from myArray, based on the values from paddings:
In [199]: for idx in range(paddings.shape[0]):
...: padded_arr[idx, paddings[idx, 0]:-paddings[idx, 1]] = myArray[idx]
...:
In [200]: padded_arr
Out[200]:
array([[0, 1, 2, 3, 0, 0],
[0, 0, 4, 5, 6, 0]], dtype=int32)
The reason we've to resort to a loop based solution is because numpy.lib.pad() doesn't yet support this sort of padding, even with all available additional modes and keyword arguments that it already provides.

Python: how to replace values in masked array according to a condition

I have an array. I want to replace the values > 5 with 1, and the values <= 5 with 0.
I also must to take into account the invalid values (999).
1) My array:
>>> import numpy
>>> a = numpy.array([ [[2, 5, 999],[0, 12, 1]], [[999, 8, 7],[7, 11, 6]] ])
>>> a
array([[[ 2, 5, 999],
[ 0, 12, 1]],
[[999, 8, 7],
[ 7, 11, 6]]])
2) I mask the invalid values:
>>> mask_a = (a==999)
>>> a_masked = numpy.ma.masked_array(a, mask = mask_a)
>>> print a_masked
[[[2 5 --]
[0 12 1]]
[[-- 8 7]
[7 11 6]]]
3) I replace the values <= 5 with zeros:
>>> a_masked[a_masked<=5]=0
>>> print a_masked
[[[0 0 --]
[0 12 0]]
[[-- 8 7]
[7 11 6]]]
4) I want to replace now the values > 5 with ones:
>>> a_masked[a_masked>5]=1
>>> print a_masked
[[[0 0 1]
[0 1 0]]
[[1 1 1]
[1 1 1]]]
Why doesn't it take into account the values=999 which were already masked???
I want to get the following result:
[[[0 0 --]
[0 1 0]]
[[-- 1 1]
[1 1 1]]]
How about simply:
>>> a[a != 999] = (a[a != 999] > 5)
>>> a
array([[[ 0, 0, 999],
[ 0, 1, 0]],
[[999, 1, 1],
[ 1, 1, 1]]])
a = np.piecewise(a, [a < 5, numpy.logical_and(a > 5,a <999) ,a >= 999], [0, 1,999])
I think would do what you want with one line ...

Categories

Resources