'Change 1's to 0 and 0's to 1 in numpy array without looping

Let's say I have a numpy array where I would like to swap all the 1's to 0 and all the 0's to 1 (the array will have other values, and there is nothing special about the 0's and 1's). Of course, I can loop through the array and change the values one by one.

Is there an efficient method you can recommend using? Does the np.where() method have an option for this operation?



Solution 1:[1]

A very simple way which does not require the use of any special method such as np.where() is to get the indices for the conditions of the variables in your numpy array, and accordingly assign the required value (in your case 0 for 1s and 1 for 0s) to the respective positional items in the array. This works for values other than 0s and 1s too. Also, you don't require any temporary variable to swap the values.

import numpy as np
arr = np.array([1, 0, 2, 3, 6, 1, 0])
indices_one = arr == 1
indices_zero = arr == 0
arr[indices_one] = 0 # replacing 1s with 0s
arr[indices_zero] = 1 # replacing 0s with 1s

Output: array([0, 1, 2, 3, 6, 0, 1])

Solution 2:[2]

Here's one way using np.where, and taking the bitwise XOR of a given value when it is either 0 or 1:

np.where((a==0)|(a==1), a^1, a)

For example:

a = np.array([[0,1,2,1], [1,2,0,3]])
print(a)
array([[0, 1, 2, 1],
       [1, 2, 0, 3]])

np.where((a==0)|(a==1), a^1, a)

array([[1, 0, 2, 0],
       [0, 2, 1, 3]])

Solution 3:[3]

This is a less clever option with np.where, just using it for indexing:

where_0 = np.where(arr == 0)
where_1 = np.where(arr == 1)

arr[where_0] = 1
arr[where_1] = 0

If you know more about the other values (e.g. they're all small numbers) there may be more options, but this is simplest.

Solution 4:[4]

a^(a&1==a)

for example

a = np.arange(-3, 4)
a^(a&1==a)
# array([-3, -2, -1,  1,  0,  2,  3])

Solution 5:[5]

iverted = ~arr + 2 should do the work

Solution 6:[6]

The trickiest part is the array will have other values. In case only 0 and 1 (no other value), arr = ~arr + 2 is the fastest way. If the array will have other values needs to be considered, use arr^(arr&1==arr). Here is the benchmark.

%%timeit

np.random.seed(0)
arr = np.random.randint(0,2,100)

arr = ~arr + 2
38.8 µs ± 12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit

np.random.seed(0)
arr = np.random.randint(0,2,100)

where_1 = arr == 1
where_0 = arr == 0

arr[where_1] = 0 # replacing 1s with 0s
arr[where_0] = 1 # replacing 0s with 1s
45.2 µs ± 7.02 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit

np.random.seed(0)
arr = np.random.randint(0,2,100)

arr = arr^(arr&1==arr)
40.3 µs ± 7.8 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit

np.random.seed(0)
arr = np.random.randint(0,2,100)

where_1 = np.where(arr == 1)
where_0 = np.where(arr == 0)

arr[where_0] = 1
arr[where_1] = 0
49.1 µs ± 13.4 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit

np.random.seed(0)
arr = np.random.randint(0,2,100)

arr = np.where((arr==0)|(arr==1), arr^1, arr)
52.3 µs ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Solution 7:[7]

inverted = ~arr + 2, did the trick for me as my array was of 8 bits

The '~' operator flips all the bits of the integer in the array from 0 to 1 and vice versa. For example, if you have the integer 0 represented by eight bits (one byte) 0000 0000, the tilde operation ~0000 0000 results in the value 1111 1111 which is the integer value -1, reference

so if a = 0, ~a gives -1 and (~a+2) gives 1 and if a = 1, ~a gives -2 and (~a+2) gives 0

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 JChat
Solution 2
Solution 3 RemcoGerlich
Solution 4 Paul Panzer
Solution 5 ?????????? ???????
Solution 6 Muhammad Yasirroni
Solution 7 Sarvesh Pathak