Replace Values of a Numpy Index Array with Values of a List
Suppose You Have a Numpy Array and a List: >>> a = Np. Array([1,2,2,1]). Reshape(2,2) >>> a Array([[1, 2], [2, 1]]) >>> B = [0, 10] I'd Like to Replace Values...
Suppose you have a numpy array and a list:
>>> a = np.array([1,2,2,1]).reshape(2,2)
>>> a
array([[1, 2],
[2, 1]])
>>> b = [0, 10]
I'd like to replace values in an array, so that 1 is replaced by 0, and 2 by 10.
I found a similar problem here -
But using this solution:
for x in np.nditer(a):
if x==1:
x[...]=x=0
elif x==2:
x[...]=x=10
Throws me an error:
ValueError: assignment destination is read-only
I guess that's because I can't really write into a numpy array.
P.S. The actual size of the numpy array is 514 by 504 and of the list is 8.
6 Answers
Well, I suppose what you need is
a[a==2] = 10 #replace all 2's with 10's
Instead of replacing the values one by one, it is possible to remap the entire array like this:
import numpy as np
a = np.array([1,2,2,1]).reshape(2,2)
# palette must be given in sorted order
palette = [1, 2]
# key gives the new values you wish palette to be mapped to.
key = np.array([0, 10])
index = np.digitize(a.ravel(), palette, right=True)
print(key[index].reshape(a.shape))
yields
[[ 0 10]
[10 0]]
Credit for the above idea goes to @JoshAdel. It is significantly faster than my original answer:
import numpy as np
import random
palette = np.arange(8)
key = palette**2
a = np.array([random.choice(palette) for i in range(514*504)]).reshape(514,504)
def using_unique():
palette, index = np.unique(a, return_inverse=True)
return key[index].reshape(a.shape)
def using_digitize():
index = np.digitize(a.ravel(), palette, right=True)
return key[index].reshape(a.shape)
if __name__ == '__main__':
assert np.allclose(using_unique(), using_digitize())
I benchmarked the two versions this way:
In [107]: %timeit using_unique()
10 loops, best of 3: 35.6 ms per loop
In [112]: %timeit using_digitize()
100 loops, best of 3: 5.14 ms per loop
Read-only array in numpy can be made writable:
nArray.flags.writeable = True
This will then allow assignment operations like this one:
nArray[nArray == 10] = 9999 # replace all 10's with 9999's
The real problem was not assignment itself but the writable flag.
I found another solution with the numpy function place. (Documentation here)
Using it on your example:
>>> a = np.array([1,2,2,1]).reshape(2,2)
>>> a
array([[1, 2],
[2, 1]])
>>> np.place(a, a==1, 0)
>>> np.place(a, a==2, 10)
>>> a
array([[ 0, 10],
[10, 0]])
You can also use np.choose(idx, vals), where idx is an array of indices that indicate which value of vals should be put in their place. The indices must be 0-based, though. Also make sure that idx has an integer datatype. So you would only need to do:
np.choose(a.astype(np.int32) - 1, b)
I was unable to set the flags, or use a mask to modify the value. In the end I just made a copy of the array.
a2 = np.copy(a)