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)