The list of indices indicating which elements you want to replace should be a Python list (or similar type), not a tuple. Different items in the selection tuple indicate that they should be selected from different axis dimensions.
Therefore, a[(2, 4, 6)] is the same as a[2, 4, 6], which is interpreted as the value at index 2 in the first dimension, index 4 in the second dimension, and index 6 in the third dimension.
The following code works correctly:
indexes = [2, 4, 6]
a[indexes] = newvalues
See also the page on Indexing from the numpy documentation, specifically the second 'Note' block in the introduction as well as the first 'Warning' under Advanced Indexing:
In Python,
x[(exp1, exp2, ..., expN)]is equivalent tox[exp1, exp2, ..., expN]; the latter is just syntactic sugar for the former.
Answer from luuk on Stack OverflowThe definition of advanced indexing means that
x[(1,2,3),]is fundamentally different thanx[(1,2,3)]. The latter is equivalent tox[1,2,3]which will trigger basic selection while the former will trigger advanced indexing. Be sure to understand why this occurs.
python - Getting and replace values from numpy array based on list of index - Stack Overflow
python - Replace values of a numpy index array with values of a list - Stack Overflow
How can I replace values in a Python NumPy array with the index of those values found in another array? - Stack Overflow
python - Replace an element in a numpy array at specific index - Stack Overflow
You should use array[idx] = new_values. This approach is much faster than native python loops. But you will have to convert 'idx' and 'new_values' to numpy arrays as well.
import numpy as np
n = 100000
array = np.random.random(n)
idx = np.random.randint(0, n, n//10)
new_values = np.random.random(n//10)
%time array[idx] = new_values
Wall time: 257 µs
def f():
for i, v in zip(idx, new_values):
array[i] = v
%time f()
Wall time: 5.93 ms
Use np.r_:
larr = np.array([0, 1, 2, 8, 4, 9, 1, 2])
larr[np.r_[3, 5]]
Output
array([8, 9])
As @MadPhysicist suggest, using larr[np.array([3, 5])] will work also, and is faster.
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
If the minimal/maximal elements of a, b form a small range (or at least small enough to fit into RAM), this can be done very quickly using a lookup table:
a = np.array([[51, 30, 20, 10],
[10, 32, 65, 77],
[15, 20, 77, 30]])
b = np.array([10, 15, 20, 30, 32, 51, 65, 77])
lo = min(a.min(), b.min())
hi = max(a.max(), b.max())
lut = np.zeros(hi - lo + 1, dtype=np.int64)
lut[b - lo] = np.arange(len(b))
Then:
>>> a_indices = lut[a - lo]
>>> a_indices
array([[5, 3, 2, 0],
[0, 4, 6, 7],
[1, 2, 7, 3]])
This is posted as an answer only because it is too long for a comment. It supports orlp's solution posted above. Numpy's vectorize avoids an explicit loop, but it is clearly not the best approach. Note that Numpy's searchsorted can only be applied as shown when b is sorted.
import timeit
import numpy as np
a = np.random.randint(1,100,(1000,1000))
b = np.arange(0,1000,1)
def o1():
lo = min(a.min(), b.min())
hi = max(a.max(), b.max())
lut = np.zeros(hi - lo + 1, dtype=np.int64)
lut[b - lo] = np.arange(len(b))
a2 = lut[a - lo]
return a2
def o2():
a2 = a.copy()
fu = np.vectorize(lambda i: np.place(a2, a2==b[i], i))
fu(np.arange(0,len(b),1))
print(timeit.timeit("np.searchsorted(b, a)", globals=globals(), number=2))
print(timeit.timeit("o1()", globals=globals(), number=2))
print(timeit.timeit("o2()", globals=globals(), number=2))
prints
0.061956800000189105
0.012765400000716909
2.220097600000372
Convert to object dtype which would support mixed dtype data and then assign -
A = A.astype(object)
A[2] = words+"_"+tags
Sample run -
In [253]: A = np.array([0,1,2,3,4,5,6])
In [254]: A.dtype
Out[254]: dtype('int64')
In [255]: A = A.astype(object)
In [256]: A[2] = words+"_"+tags
In [257]: A
Out[257]: array([0, 1, 'dan_np', 3, 4, 5, 6], dtype=object)
The error message, and comments, tell you that you can't put a string into an integer array.
You can how ever put a string into a list:
In [53]: Al = A.tolist()
In [54]: Al[2] = words+"_"+tags
In [55]: Al
Out[55]: [0, 1, 'dan_np', 3, 4, 5, 6]
And you can turn that list back into an array
In [56]: A = np.array(Al)
In [57]: A
Out[57]:
array(['0', '1', 'dan_np', '3', '4', '5', '6'],
dtype='<U11')
Because of the mix of numbers and string np.array uses their common format - string. You could also specify object,and get the same result as @Divakar.
In [58]: A = np.array(Al, dtype=object)
In [59]: A
Out[59]: array([0, 1, 'dan_np', 3, 4, 5, 6], dtype=object)
Such an object array is similar to a list, containing pointers to elements elsewhere in memory.
If A should remain numeric, then you need to assign compatible numeric values.
I have a function that converts angular values (degrees) into cardinal directions (N, NNW, ect). Here is what I have so far:
import numpy as np
def to_cardinal(values: np.ndarray):
CARD_DIR = [
"N",
"NNE",
"NE",
"ENE",
"E",
"ESE",
"SE",
"SSE",
"S",
"SSW",
"SW",
"WSW",
"W",
"WNW",
"NW",
"NNW",
"N",
]
# If values over 360, scale down
angle = np.remainder(vals_array, 360.0)
# Divide by 22.5 and convert to int
# On another note, not sure if np.around is necessary?
index_arr = np.around((angle / 22.5)).astype(np.int16, copy=False)
return index_arr
I return index_arr here because I'm not sure how to continue. Here is a sample input/output:
input = np.array((10, 350, 190, 100, 270, 30)) output = to_cardinal(input) print(output) >>> (0, 16, 8, 4, 12, 1)
These values are the indices of the CARD_DIR list that the degree values corresponds to. In words, I need to construct an array (or replace from index_arr) where each element is now the string from CARD_DIR instead of the integer that represents the index. I can imagine doing this in a loop, but how could this be vectorized?
In psuedo-code:
new_array = np.empty(size = index_arr.size)
for i, value in enumerate(index_arr):
new_array[i] = CARD_DIR[value]Also, not sure if np.around is necessary when casting a float to an int? Does the int type conversion round correctly already?
Edit: Figured it out, numpy is magical. See comment below! Still interested in type-casting from floats to ints, though.
There is a numpy function for this, numpy.full or numpy.full_like:
>>> x = np.array(([1,2,2], [1,4,3]))
>>> np.full(x.shape, 10)
array([[10, 10, 10],
[10, 10, 10]])
# OR,
>>> np.full_like(x, 10)
array([[10, 10, 10],
[10, 10, 10]])
If you want to iterate you can either use itertools.product:
>>> from itertools import product
>>> def Iter_Replace(x):
indices = product(*map(range, x.shape))
for index in indices:
x[tuple(index)] = 10
return x
>>> x = np.array([[1,2,2], [1,4,3]])
>>> Iter_Replace(x)
array([[10, 10, 10],
[10, 10, 10]])
Or, use np.nditer
>>> x = np.array([[1,2,2], [1,4,3]])
>>> for index in np.ndindex(x.shape):
x[index] = 10
>>> x
array([[10, 10, 10],
[10, 10, 10]])
You have two errors. There are missing parenthesis in the first line of main:
x = np.array(([1,2,2], [1,4,3]))
And you must replace range(x) by range(len(x)) in the Iter_Replace function.