Change all values equal to x to y - python

I have a very simple task that I cannot figure out how to do in numpy. I have a 3 channel array and wherever the array value does not equal (1,1,1) I want to convert that array value to (0,0,0).
So the following:
[[0,1,1],
[1,1,1],
[1,0,1]]
Should change to:
[[0,0,0],
[1,1,1],
[0,0,0]]
How can I achieve this in numpy? The following is not achieving the desired results:
# my_arr.dtype = uint8
my_arr[my_arr != (1,1,1)] = 0
my_arr = np.where(my_arr == (1,1,1), my_arr, (0,0,0))

Use numpy.array.all(1) to filter and assign 0:
import numpy as np
arr = np.array([[0,1,1],
[1,1,1],
[1,0,1]])
arr[~(arr == 1).all(1)] = 0
Output:
array([[0, 0, 0],
[1, 1, 1],
[0, 0, 0]])
Explain:
arr==1: returns array of bools that satisfy the condition (here it's 1)
all(axis=1): returns array of bools if each row has all True (i.e. all rows that are 1`
~(arr==1).all(1): selects rows that are not all 1

This is just comparing the two lists.
x = [[0,1,1],
[1,1,1],
[1,0,1]]
for i in range(len(x)):
if x[i] != [1,1,1]:
x[i] = [0,0,0]

Related

comparing numpy arrays such that they equal each other if they both fall within the same range of values

Suppose that I have a 3d numpy array where at each canvas[row, col], there is another numpy array in the format of [R, G, B, A].I want to check if the numpy array at canvas[row, col] is equal to another numpy array [0, 0, 0, 240~255], where the last element is a range of values that will be accepted as "equal". For example, both [0,0,0, 242] and [0,0,0, 255] will pass this check. Below, I have it so that it only accepts the latter case.
(canvas[row,col] == np.array([0,0,0,255])).all()
How might I write this condition so it does as I described previously?
You can compare slices:
(
(canvas[row, col, :3] == 0).all() # checking that color is [0, 0, 0]
and
(canvas[row, col, 3] >= 240) # checking that alpha >= 240
)
Also, if you need to check this on a lot of values, you can optimize it with vectorization, producing a 2D array of boolean values:
np.logical_and(
(canvas[..., :3] == 0).all(axis=-1), # checking that color is [0, 0, 0]
(canvas[..., 3] >= 240) # checking that alpha >= 240
)

Replacing array at i`th dimension

Let's say I have a two-dimensional array
import numpy as np
a = np.array([[1, 1, 1], [2,2,2], [3,3,3]])
and I would like to replace the third vector (in the second dimension) with zeros. I would do
a[:, 2] = np.array([0, 0, 0])
But what if I would like to be able to do that programmatically? I mean, let's say that variable x = 1 contained the dimension on which I wanted to do the replacing. How would the function replace(arr, dimension, value, arr_to_be_replaced) have to look if I wanted to call it as replace(a, x, 2, np.array([0, 0, 0])?
numpy has a similar function, insert. However, it doesn't replace at dimension i, it returns a copy with an additional vector.
All solutions are welcome, but I do prefer a solution that doesn't recreate the array as to save memory.
arr[:, 1]
is basically shorthand for
arr[(slice(None), 1)]
that is, a tuple with slice elements and integers.
Knowing that, you can construct a tuple of slice objects manually, adjust the values depending on an axis parameter and use that as your index. So for
import numpy as np
arr = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
axis = 1
idx = 2
arr[:, idx] = np.array([0, 0, 0])
# ^- axis position
you can use
slices = [slice(None)] * arr.ndim
slices[axis] = idx
arr[tuple(slices)] = np.array([0, 0, 0])

Generate random matrix in numpy without rows of all 1's

I am generating a random matrix with
np.random.randint(2, size=(5, 3))
that outputs something like
[0,1,0],
[1,0,0],
[1,1,1],
[1,0,1],
[0,0,0]
How do I create the random matrix with the condition that each row cannot contain all 1's? That is, each row can be [1,0,0] or [0,0,0] or [1,1,0] or [1,0,1] or [0,0,1] or [0,1,0] or [0,1,1] but cannot be [1,1,1].
Thanks for your answers
Here's an interesting approach:
rows = np.random.randint(7, size=(6, 1), dtype=np.uint8)
np.unpackbits(rows, axis=1)[:, -3:]
Essentially, you are choosing integers 0-6 for each row, ie 000-110 as binary. 7 would be 111 (all 1's). You just need to extract binary digits as columns and take the last 3 digits (your 3 columns) since the output of unpackbits is 8 digits.
Output:
array([[1, 0, 1],
[1, 0, 0],
[1, 0, 0],
[1, 0, 0],
[0, 1, 1],
[0, 0, 0]], dtype=uint8)
If you always have 3 columns, one approach is to explicitly list the possible rows and then choose randomly among them until you have enough rows:
import numpy as np
# every acceptable row
choices = np.array([
[1,0,0],
[0,0,0],
[1,1,0],
[1,0,1],
[0,0,1],
[0,1,0],
[0,1,1]
])
n_rows = 5
# randomly pick which type of row to use for each row needed
idx = np.random.choice(range(len(choices)), size=n_rows)
# make an array by using the chosen rows
array = choices[idx]
If this needs to generalize to a large number of columns, it won't be practical to explicitly list all choices (even if you create the choices programmatically, the memory is still an issue; the number of possible rows grows exponentially in the number of columns). Instead, you can create an initial matrix and then just resample any unacceptable rows until there are none left. I'm assuming that a row is unacceptable if it consists only of 1s; it would be easy to adapt this to the case where the threshold is any number of 1s, though.
n_rows = 5
n_cols = 4
array = np.random.randint(2, size=(n_rows, n_cols))
all_1s_idx = array.sum(axis=-1) == n_cols
while all_1s_idx.any():
array[all_1s_idx] = np.random.randint(2, size=(all_1s_idx.sum(), n_cols))
all_1s_idx = array.sum(axis=-1) == n_cols
Here we just keep resampling all unacceptable rows until there are none left. Because all of the necessary rows are resampled at once, this should be quite efficient. Additionally, as the number of columns grows larger, the probability of a row having all 1s decreases exponentially, so efficiency shouldn't be a problem.
#busybear beat me to it but I'll post it anyway, as it is a bit more general:
def not_all(m, k):
if k>64 or sys.byteorder != 'little':
raise NotImplementedError
sample = np.random.randint(0, 2**k-1, (m,), dtype='u8').view('u1').reshape(m, -1)
sample[:, k//8] <<= -k%8
return np.unpackbits(sample).reshape(m, -1)[:, :k]
For example:
>>> sample = not_all(1000000, 11)
# sanity checks
>>> unq, cnt = np.unique(sample, axis=0, return_counts=True)
>>> len(unq) == 2**11-1
True
>>> unq.sum(1).max()
10
>>> cnt.min(), cnt.max()
(403, 568)
And while I'm at hijacking other people's answers here is a streamlined version of #Nathan's acceptance-rejection method.
def accrej(m, k):
sample = np.random.randint(0, 2, (m, k), bool)
all_ones, = np.where(sample.all(1))
while all_ones.size:
resample = np.random.randint(0, 2, (all_ones.size, k), bool)
sample[all_ones] = resample
all_ones = all_ones[resample.all(1)]
return sample.view('u1')
Try this solution using sum():
import numpy as np
array = np.random.randint(2, size=(5, 3))
for i, entry in enumerate(array):
if entry.sum() == 3:
while True:
new = np.random.randint(2, size=(1, 3))
if new.sum() == 3:
continue
break
array[i] = new
print(array)
Good luck my friend!

Comparing Numpy Arrays

So I have two 2D numpy arrays of equal size, both obtained using the pygame.surfarray.array3d method on two different surfaces.
Each value in the array is also an array in the form [a, b, c] (so I basically have a 2D array with 1D elements).
I'd essentially like to compare the two based on the condition:
if any(val1 != val2) and all(val1 != [0, 0, 0]):
# can't be equal and val1 cant be [0, 0, 0]
Is there any more efficient way of doing this without simply iterating through either array as shown below?
for y in range(len(array1)):
for x in range(len(array1[y])):
val1 = array1[y,x]; val2 = array[y,x]
if any(val1 != val2) and all(val1 != [0, 0, 0]):
# do something
import numpy as np
if np.any(array1 != array2) and not np.any(np.all(a == 0, axis=-1))
np.any(array1 != array2) is comparing each element of the "big" 3D array. This is, however, equivalent to comparing val1 to val2 for every x and y.
The other condition, np.any(np.all(a == 0, axis=-1)) is a little bit more complicated. The innermost np.all(a == 0, axis=-1) creates a 2D array of boolean values. Each value is set to True or False depending if all values in the last dimension are 0. The outer condition checks if any of the values in the 2D array are True which would mean that there was an element of array1[y, x] that was equal to [0, 0, 0].

numpy.where() returns inconsisten dimensions

I pass an array of size (734,814,3) to a function but numpy.where() gives one dimensional result instead of the two-dimensional one, which it should for a 2D array
def hsi2rgb(img):
img_rgb = np.empty_like(img)
h = img[:,:,0] #(734,814)
s = img[:,:,1] #(734,814)
i = img[:,:,2] #(734,814)
l1 = 0.00
l2 = 2*3.14/3
l3 = 4*3.14/3
l4 = 3.14
r1 = np.where(np.logical_and(h>=l1, h<l2)) #(99048,)
r2 = np.where(np.logical_and(h>=l2, h<l3))
r3 = np.where(np.logical_and(h>=l3, h<l4))
hs = h[r1]
return img_rgb
r1 is shown to be a tupple, and r1[0],r1[1] are of the size 99048, which shouldn't be the case. r1 should have row indices and column indices of those values which satisfy the condition. I tried it without the logical and, using just one condition, but the problem persists.
I followed your code, and np.where returned the expected result: a tuple with two 1D arrays containing the indexes where the condition is met:
import numpy as np
h = np.random.uniform(size=(734, 814))
r1 = np.where(np.logical_and(h >= 0.1, h < 0.9))
print(r1[0].shape, r1[1].shape) # (478129,) (478129,)
This means that 478129 elements met the condition. For each of them, r1[0] will have its row index, and r11 will have its column index. Namely, if r1 looks like
(array([ 0, 0, 0, ..., 733, 733, 733]), array([ 0, 1, 2, ..., 808, 809, 811]))
then I know that h[0, 0], h[0, 1], h[0, 2], etc satisfy the conditions: the row index comes from the first array, the column index from the second. This structure may be less readable, but it's usable for indexing the array h.
The transposed form of the output is more readable, being a 2D array with row-column index pairs:
array([[ 0, 0],
[ 0, 1],
[ 0, 2],
...,
[733, 808],
[733, 809],
[733, 811]])
It can be obtained by transposing r1 (if you need the original r1 as well), or directly with np.argwhere:
r1 = np.argwhere(np.logical_and(h >= 0.1, h < 0.9))

Categories