Vectorise np logic: zeroing out in z axis above point - python

I have an array:
rp = np.array(
[
[[True, False], [True, True]],
[[True, True], [False, True]],
[[True, True], [True, True]],
[[True, True], [True, False]],
[[True, True], [True, True]],
]
)
I want to zero it out in the z axis above the first False point, i.e. set subsequent z values to False. Thus I have a test:
def test_f():
desired = np.array(
[
[[True, False], [True, True]],
[[True, False], [False, True]],
[[True, False], [False, True]],
[[True, False], [False, False]],
[[True, False], [False, False]],
]
)
assert (f(rp) == desired).all()
And I have some non vectorised code which passes the test:
def f(a: np.ndarray) -> np.ndarray:
lowest = np.argmin(a, axis=0)
for x, row in enumerate(lowest):
for y, z in enumerate(row):
if not a[z, x, y]: # catch cases with no False at all
a[z:, x, y] = False
return a
How do I vectorise this function?

What about this?
def f(a: np.ndarray) -> np.ndarray:
return a.cumprod(axis=0, dtype=np.bool)

Related

How to intersect boolean subarrays for True values?

I know that Numpy provides logical_and() which allows us to intersect two boolean arrays for True values only (True and True would yield True while True and False would yield False). For example,
a = np.array([True, False, False, True, False], dtype=bool)
b = np.array([False, True, True, True, False], dtype=bool)
np.logical_and(a, b)
> array([False, False, False, True, False], dtype=bool)
However, I'm wondering how I can apply this to two subarrays in an overall array? For example, consider the array:
[[[ True, True], [ True, False]], [[ True, False], [False, True]]]
The two subarrays I'm looking to intersect are:
[[ True, True], [ True, False]]
and
[[ True, False], [False, True]]
which should yield:
[[ True, False], [False, False]]
Is there a way to specify that I want to apply logical_and() to the outermost subarrays to combine the two?
You can use .reduce() along the first axis:
>>> a = np.array([[[ True, True], [ True, False]], [[ True, False], [False, True]]])
>>> np.logical_and.reduce(a, axis=0)
array([[ True, False],
[False, False]])
This works even when you have more than two "sub-arrays" in your outer array. I prefer this over the unpacking approach because it allows you to apply your function (np.logical_and) over any axis of your array.
If I understand your question correctly, you are looking to do:
import numpy as np
output = np.logical_and(a[:, 0], a[:, 1])
This simply slices your arrays so that you can use logical_and the way your results suggest.

Appending to a multidimensional array Python

I am filtering the arrays a and b for likewise values and then I want to append them to a new array difference howveer I get the error: ValueError: all the input array dimensions for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 0 and the array at index 1 has size 2. How would I be able to fix this?
import numpy as np
a = np.array([[0,12],[1,40],[0,55],[1,23],[0,123.5],[1,4]])
b = np.array([[0,3],[1,10],[0,55],[1,34],[1,122],[0,123]])
difference= np.array([[]])
for i in a:
for j in b:
if np.allclose(i, j, atol=0.5):
difference = np.concatenate((difference,[i]))
Expected Output:
[[ 0. 55.],[ 0. 123.5]]
The problem is that you are trying to concatenate an array where the elements has size 0
difference= np.array([[]]) # Specifically [ [<no elements>] ]
To an array where the elements has size 2
np.concatenate((difference,[i])) # Specifically [i] which is [ [ 0., 55.] ]
Instead of initializing it with an empty array which has the size of 0, you could try just calling .reshape() on the difference.
# difference= np.array([[]]) # Old code
difference= np.array([]).reshape(0, 2) # Updated code
Output
[[ 0. 55. ]
[ 0. 123.5]]
np.array([[]]) shape is (1, 0). To make it work, it should be (0, 2):
difference= np.zeros((0, *a.shape[1:]))
In [22]: a = np.array([[0,12],[1,40],[0,55],[1,23],[0,123.5],[1,4]])
...: b = np.array([[0,3],[1,10],[0,55],[1,34],[1,122],[0,123]])
Using a straight forward list comprehension:
In [23]: [i for i in a for j in b if np.allclose(i,j,atol=0.5)]
Out[23]: [array([ 0., 55.]), array([ 0. , 123.5])]
But as for your concatenate. Look at the shape of the arrays:
In [24]: np.array([[]]).shape
Out[24]: (1, 0)
In [25]: np.array([i]).shape
Out[25]: (1, 1)
Those can only be joined on axis 1; default is 0, giving you the error. Like wrote in the comment, you have to understand arrays shapes to use concatenate.
In [26]: difference= np.array([[]])
...: for i in a:
...: for j in b:
...: if np.allclose(i, j, atol=0.5):
...: difference = np.concatenate((difference,[i]), axis=1)
...:
In [27]: difference
Out[27]: array([[ 0. , 55. , 0. , 123.5]])
vectorized
A whole-array approach:
broadcase a against b, producing a (5,5,2) closeness array:
In [37]: np.isclose(a[:,None,:],b[None,:,:], atol=0.5)
Out[37]:
array([[[ True, False],
[False, False],
[ True, False],
[False, False],
[False, False],
[ True, False]],
[[False, False],
[ True, False],
[False, False],
[ True, False],
[ True, False],
[False, False]],
[[ True, False],
[False, False],
[ True, True],
[False, False],
[False, False],
[ True, False]],
[[False, False],
[ True, False],
[False, False],
[ True, False],
[ True, False],
[False, False]],
[[ True, False],
[False, False],
[ True, False],
[False, False],
[False, False],
[ True, True]],
[[False, False],
[ True, False],
[False, False],
[ True, False],
[ True, False],
[False, False]]])
Find where both columns are true, and where at least one "row" is:
In [38]: _.all(axis=2)
Out[38]:
array([[False, False, False, False, False, False],
[False, False, False, False, False, False],
[False, False, True, False, False, False],
[False, False, False, False, False, False],
[False, False, False, False, False, True],
[False, False, False, False, False, False]])
In [39]: _.any(axis=1)
Out[39]: array([False, False, True, False, True, False])
In [40]: a[_]
Out[40]:
array([[ 0. , 55. ],
[ 0. , 123.5]])

Split cells in two and and form np.array

list(itertools.product([[True,False],[False, True]], repeat=2))
The command above produces the output below.
[((True, False), (True, False)),
((True, False), (False, True)),
((False, True), (True, False)),
((False, True), (False, True))]
I want it however to look like this:
array([[ True, False, True, False],
[ True, False, False, True],
[False, True, True, False],
[False, True, False, True]])
Anything will help!
First generate your product:
pr = list(itertools.product([[True, False], [False, True]], repeat=2))
Then convert it to a Numpy array, with proper reshaping:
a = np.array(pr).reshape((len(pr), -1))
The result is:
array([[ True, False, True, False],
[ True, False, False, True],
[False, True, True, False],
[False, True, False, True]])
This code works also for other values of repeat (check e.g. for
repeat=3).
And a remark about other solutions: All of them, except for mathfux,
generate plain pythonic lists, not Numpy arrays (as you
specified).
The values of the product list are tuples.
You can loop over the tuples of the product list,
each time concatenate each list's tuples to a new list:
e.g.
([True, False], [True, False]) -> [True, False, True, False]
Option 1
import itertools
x = list(itertools.product([[True,False],[False, True]], repeat=2))
x = [list(v[0] + v[1]) for v in x]
# [[True, False, True, False], [True, False, False, True], [False, True, True, False], [False, True, False, True]]
print(x)
Option 2: One liner
import itertools
x = [list(v[0] + v[1]) for v in list(itertools.product([[True,False],[False, True]], repeat=2))]
# [[True, False, True, False], [True, False, False, True], [False, True, True, False], [False, True, False, True]]
print(x)
In numpy terms, you need to change its shape:
X = [((True, False), (True, False)),
((True, False), (False, True)),
((False, True), (True, False)),
((False, True), (False, True))]
np.array(X).reshape(4,4)
[[ True False True False]
[ True False False True]
[False True True False]
[False True False True]]
out = [[*a, *b] for a, b in itertools.product([[True,False],[False, True]], repeat=2)]
print(out)
Prints:
[[True, False, True, False],
[True, False, False, True],
[False, True, True, False],
[False, True, False, True]]

Combine mask across all channels

I have some condition tested in all 3 channels of an image, so I have something like:
import numpy as np
check = np.array([[[True, True], [True, False]], [[True, False], [False, False]], [[True, True], [True, True]]])
where dimensions are: channel (RGB), height, width.
I want to get 2D array, that shows that all corresponding pixels of different channels are true, so I want to get
result = np.array([[True, False], [False, False]])
Currently, I'm doing it this ways:
result = np.logical_and(check[0, :, :], check[1, :, :], check[2, :, :])
But I'm sure there is a more elegant way to do this
You can use numpy.all along the axis of interest:
import numpy as np
check = np.array([[[True, True],
[True, False]],
[[True, False],
[False, False]],
[[True, True],
[True, True]]])
np.all(check, axis=0)
array([[ True, False],
[False, False]])
Alternatively you can use a list comprehension on check just because you are comparing along the first axis:
np.logical_and(*[c for c in check])
array([[ True, False],
[False, False]])

How to change the values of a 2d tensor in certain rows and columns

Suppose I have an all-zero mask tensor like this:
mask = torch.zeros(5,3, dtype=torch.bool)
Now I want to set the value of mask at the intersection of the following rows and cols indices to True:
rows = torch.tensor([0,2,4])
cols = torch.tensor([1,2])
I would like to produce the following result:
tensor([[False, True, True ],
[False, False, False],
[False, True, True ],
[False, False, False],
[False, True, True ]])
When I try the following code, I receive an error:
mask[rows, cols] = True
IndexError: shape mismatch: indexing tensors could not be broadcast together with shapes [3], [2]
How can I do that efficiently in PyTorch?
You need proper shape for that you can use torch.unsqueeze
mask = torch.zeros(5,3, dtype=torch.bool)
mask[rows, cols.unsqueeze(1)] = True
mask
tensor([[False, True, True],
[False, False, False],
[False, True, True],
[False, False, False],
[False, True, True]])
or torch.reshape
mask[rows, cols.reshape(-1,1)] = True
mask
tensor([[False, True, True],
[False, False, False],
[False, True, True],
[False, False, False],
[False, True, True]])

Categories