Reputation: 620
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?
Upvotes: 14
Views: 30030
Reputation: 3378
I'm really surprised that no one thought of this before. The easiest and most efficient way to flip the values in a numpy array (arr
) consisting of 0s and 1s is simply by subtracting the numpy array from 1. In other word, performing the operation 1 - arr
. Here's a quick example:
arr = np.array([1, 0, 1, 1, 0])
1 - arr
array([0, 1, 0, 0, 1])
Upvotes: 0
Reputation: 2167
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)
Upvotes: 1
Reputation: 1
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
Upvotes: 0
Reputation: 53089
a^(a&1==a)
for example
a = np.arange(-3, 4)
a^(a&1==a)
# array([-3, -2, -1, 1, 0, 2, 3])
Upvotes: 9
Reputation: 814
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])
Upvotes: 3
Reputation: 88305
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]])
Upvotes: 26
Reputation: 31270
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.
Upvotes: 9