Here's one way, provided you have a small dictionary/min and max values, this may be more efficient, you work around the negative index by adding the array min:
In [11]: indexer = np.array([d.get(i, -1) for i in range(a.min(), a.max() + 1)])
In [12]: indexer[(a - a.min())]
Out[12]:
array([[4, 1, 3, 0],
[2, 0, 2, 3],
[1, 4, 3, 3]])
Note: This moves the for loop to the lookup table, but if this is significantly smaller than the actual array this could be a lot faster.
Answer from Andy Hayden on Stack Overflowpython - Numpy: Replacing values in a 2D array efficiently using a dictionary as a map - Stack Overflow
python - Find and replace specific values within 2D array - Stack Overflow
python - Replacing specific values in numpy 2D Array - Stack Overflow
python - How do I replace values in 2D numpy array using a dictionary of {value:(row#,column#)} pairs - Stack Overflow
Here's one way, provided you have a small dictionary/min and max values, this may be more efficient, you work around the negative index by adding the array min:
In [11]: indexer = np.array([d.get(i, -1) for i in range(a.min(), a.max() + 1)])
In [12]: indexer[(a - a.min())]
Out[12]:
array([[4, 1, 3, 0],
[2, 0, 2, 3],
[1, 4, 3, 3]])
Note: This moves the for loop to the lookup table, but if this is significantly smaller than the actual array this could be a lot faster.
Make a copy of the array then iterate over the dictionary items then use boolean indexing to assign the new values to the copy.
import numpy as np
b = np.copy(a)
for old, new in d.items():
b[a == old] = new
This can be done with list comprehension in a simple one-liner.
Say you have a list of lists:
a = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
and you want to replace all occurrences of 2 with 4:
[[_el if _el != 2 else 4 for _el in _ar] for _ar in a]
Another option is to use numpy's where function. From the docstring:
where(condition, [x, y])
Return elements, either from
xory, depending oncondition.
So, in your case (say you'd like again to replace all 2 with 4):
import numpy as np
a = np.array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])
np.where(a==2, 4, a)
If you want to replace several values in one go, you could do something like this:
Say you'd like to replace 1 with 3 and 3 with 5:
ix=np.isin(array, [1,3])
vc=np.vectorize(lambda x: 3 if x == 1 else 5)
np.where(ix, vc(array), array)
If you have more than 2 values to replace, say you want to map the list [1,3,5] to [3, 5, -3], then you can define a simple function like:
old_vals = [1,3,5]
new_vals = [3, 5, -3]
def switch_val(x):
return new_vals[old_vals.index(x)] if x in old_vals else x
and so:
vc=np.vectorize(switch_val)
vc(array)
where we vectorized the function.
Hope that helped and happy coding!
for i in range(len(array)):
for j in range(len(array[i])):
if array[i][j] == value_you_are_looking_for:
array[i][j] = new_value
This should work
I hope I understood your question correctly
array = np.zeros((10,10))
data = {72: (3, 4), 11: (1, 5), 10: (2, 4), 43: (2, 3), 22: (24,35)}
for i in data.keys():
try:
array[data[i][0],data[i][1]] = float(i)
except IndexError:
pass
print array
I changed the indices such that it fits into your 10 x 10 array (I assume you work with a bigger array in your real example)
I iterate over all keys in the dictionary (the values). The program then tries to set this value in the array at the given coordinates. I pass IndexErrors for the case some coordinates are outside the array (like the last in this example.
EDIT
This solution only works if your keys are unique. If they are not I would recommend the solution of @Osssan.
We need to invert mapping from values=>coordinates to co-ordinates=>values before replacement in the array. I have edited the dictionary entries for demo purpose and as pointed in comments, dictionary co-ordinate entries should be less than dimensions of array
import numpy as np
arrObj = np.zeros((10,10))
arrObj
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
#copy of array for replacement
replaceArrObj=arrObj
#ensure co-ordinates in the dictionary could be indexed in array
#current mapping: values => co-ordinates
dictObj = {1.0:(0.0,0.0),2.0:(1.0,1.0),3.0: (2.0, 2.0), 4.0: (3.0, 3.0),5.0:(4.0,4.0), 6.0: (5.0, 5.0), 7.0: (6.0, 6.0), 8.0: (7.0,7.0), 9.0: (8.0,8.0),
10.0: (9.0,9.0)}
dictObj
#{1.0: (0.0, 0.0),
# 2.0: (1.0, 1.0),
# 3.0: (2.0, 2.0),
# 4.0: (3.0, 3.0),
# 5.0: (4.0, 4.0),
# 6.0: (5.0, 5.0),
# 7.0: (6.0, 6.0),
# 8.0: (7.0, 7.0),
# 9.0: (8.0, 8.0),
# 10.0: (9.0, 9.0)}
Invert Mapping:
#invert mapping of dictionary: co-ordinates => values
inv_dictObj = {v: k for k, v in dictObj.items()}
inv_dictObj
#{(0.0, 0.0): 1.0,
# (1.0, 1.0): 2.0,
# (2.0, 2.0): 3.0,
# (3.0, 3.0): 4.0,
# (4.0, 4.0): 5.0,
# (5.0, 5.0): 6.0,
# (6.0, 6.0): 7.0,
# (7.0, 7.0): 8.0,
# (8.0, 8.0): 9.0,
# (9.0, 9.0): 10.0}
Replacement:
#Replace values from dictionary at correponding co-ordiantes
for i,j in inv_dictObj.keys():
replaceArrObj[i,j]=inv_dictObj[(i,j)]
replaceArrObj
#array([[ 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
# [ 0., 2., 0., 0., 0., 0., 0., 0., 0., 0.],
# [ 0., 0., 3., 0., 0., 0., 0., 0., 0., 0.],
# [ 0., 0., 0., 4., 0., 0., 0., 0., 0., 0.],
# [ 0., 0., 0., 0., 5., 0., 0., 0., 0., 0.],
# [ 0., 0., 0., 0., 0., 6., 0., 0., 0., 0.],
# [ 0., 0., 0., 0., 0., 0., 7., 0., 0., 0.],
# [ 0., 0., 0., 0., 0., 0., 0., 8., 0., 0.],
# [ 0., 0., 0., 0., 0., 0., 0., 0., 9., 0.],
# [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 10.]])
Type Conversion:
You should not face any errors/warnings as long as both array co-ordinates and dictionary entries have same type. You can additionally enforce specific type conversion if you prefer int/float
#float to int conversion in array
replaceArrObj.astype(int)
#array([[ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
# [ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0],
# [ 0, 0, 3, 0, 0, 0, 0, 0, 0, 0],
# [ 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
# [ 0, 0, 0, 0, 5, 0, 0, 0, 0, 0],
# [ 0, 0, 0, 0, 0, 6, 0, 0, 0, 0],
# [ 0, 0, 0, 0, 0, 0, 7, 0, 0, 0],
# [ 0, 0, 0, 0, 0, 0, 0, 8, 0, 0],
# [ 0, 0, 0, 0, 0, 0, 0, 0, 9, 0],
# [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 10]])
#float to int conversion in dictionary, where k referes to key items and v to value items
int_dictObj = { (int(k[0]),int(k[1])):int(v) for k,v in inv_dictObj.items()}
int_dictObj
#{(0, 0): 1,
# (1, 1): 2,
# (2, 2): 3,
# (3, 3): 4,
# (4, 4): 5,
# (5, 5): 6,
# (6, 6): 7,
# (7, 7): 8,
# (8, 8): 9,
# (9, 9): 10}
Approach #1 : One vectorized approach using np.searchsorted and np.in1d would be as listed below -
# Store country_codes and continent_codes column data for further usage
oldval = np.array(df['country_codes'])
newval = np.array(df['continent_codes'])
# Mask of elements to be changed
mask = np.in1d(arr,oldval)
# Indices for each match from oldval in arr
idx = np.searchsorted(oldval,arr.ravel()[mask])
# Using the mask put selective elements from continent_codes column into arr
arr.ravel()[mask] = newval[idx]
Sample run -
>>> arr # Original 2D array
array([[23, 4, 23, 5, 8],
[ 3, 6, 8, 5, 11],
[16, 24, 15, 4, 10],
[ 4, 16, 10, 8, 1]])
>>> df
country_codes continent_codes
0 4 4
1 8 3
2 12 5
3 16 6
4 24 5
>>> oldval = np.array(df['country_codes'])
>>> newval = np.array(df['continent_codes'])
>>> mask = np.in1d(arr,oldval)
>>> idx = np.searchsorted(oldval,arr.ravel()[mask])
>>> arr.ravel()[mask] = newval[idx]
>>> mask.reshape(arr.shape) # Mask array depiciting which elements were updated
array([[False, True, False, False, True],
[False, False, True, False, False],
[ True, True, False, True, False],
[ True, True, False, True, False]], dtype=bool)
>>> arr # Updated 2D array
array([[23, 4, 23, 5, 3],
[ 3, 6, 3, 5, 11],
[ 6, 5, 15, 4, 10],
[ 4, 6, 10, 3, 1]])
Approach #2 : As a variant, you can also create the mask with a comparison between np.searchsorted(oldval,arr,'left') and np.searchsorted(oldval,arr,'right') as discussed in the solutions for this question and re-use np.searchsorted(oldval,arr,'left') again later on while putting values into arr for a more efficient solution, like so -
# Store country_codes and continent_codes column data for further usage
oldval = np.array(df['country_codes'])
newval = np.array(df['continent_codes'])
# Left and right indices for each match from oldval in arr
left_idx = np.searchsorted(oldval,arr,'left')
right_idx = np.searchsorted(oldval,arr,'right')
# Mask of elements to be changed
mask = left_idx!=right_idx
# Using the mask put selective elements from continent_codes column into arr
arr[mask] = newval[left_idx[mask]]
Runtime tests and verify outputs
Function definitions -
def original_app(arr,df):
for index, row in df.iterrows():
arr[arr == row['country_codes']] = row['continent_codes']
def vectorized_app1(arr,df):
oldval = np.array(df['country_codes'])
newval = np.array(df['continent_codes'])
mask = np.in1d(arr,oldval)
idx = np.searchsorted(oldval,arr.ravel()[mask])
arr.ravel()[mask] = newval[idx]
def vectorized_app2(arr,df):
oldval = np.array(df['country_codes'])
newval = np.array(df['continent_codes'])
left_idx = np.searchsorted(oldval,arr,'left')
right_idx = np.searchsorted(oldval,arr,'right')
mask = left_idx!=right_idx
arr[mask] = newval[left_idx[mask]]
Verify outputs -
In [195]: # Input array
...: arr = np.random.randint(0,100000,(1000,1000))
...:
...: # Setup input dataframe
...: N = 1000
...: oldvals = np.unique(np.random.randint(0,100000,N))
...: newvals = np.random.randint(0,9,(oldvals.size))
...: df=pd.DataFrame({'country_codes':oldvals,'continent_codes':newvals})
...: df = df.reindex_axis(sorted(df.columns)[::-1], axis=1)
...:
...: # Make copies for input array for funcs to update them
...: arrc1 = arr.copy()
...: arrc2 = arr.copy()
...: arrc3 = arr.copy()
...:
In [196]: # Verify outputs
...: original_app(arrc1,df)
...: vectorized_app1(arrc2,df)
...: vectorized_app2(arrc3,df)
...:
In [197]: np.allclose(arrc1,arrc2)
Out[197]: True
In [198]: np.allclose(arrc1,arrc3)
Out[198]: True
Timings -
In [199]: # Make copies for input array for funcs to update them
...: arrc1 = arr.copy()
...: arrc2 = arr.copy()
...: arrc3 = arr.copy()
...:
In [200]: %timeit original_app(arrc1,df)
1 loops, best of 3: 2.79 s per loop
In [201]: %timeit vectorized_app1(arrc2,df)
1 loops, best of 3: 360 ms per loop
In [202]: %timeit vectorized_app2(arrc3,df)
1 loops, best of 3: 213 ms per loop
with this data as exemple, with at most N countries,
N=10**5
values=np.random.randint(0,N,(1000,1000))
exemple={'country':np.arange(N//2),'continent':randint(1,5,N//2)}
df=pd.DataFrame.from_dict(exemple)
You can just do :
v=np.arange(N)
v[df['country']]=df['continent']
v.take(values,out=values)
probably not optimal, but efficient (20ms).
I think both the fastest and most concise way to do this is to use NumPy's built-in Fancy indexing. If you have an ndarray named arr, you can replace all elements >255 with a value x as follows:
arr[arr > 255] = x
I ran this on my machine with a 500 x 500 random matrix, replacing all values >0.5 with 5, and it took an average of 7.59ms.
In [1]: import numpy as np
In [2]: A = np.random.rand(500, 500)
In [3]: timeit A[A > 0.5] = 5
100 loops, best of 3: 7.59 ms per loop
If you want a new array result containing a copy of arr whenever arr < 255, and 255 otherwise:
result = np.minimum(arr, 255)
More generally, for a lower and/or upper bound:
result = np.clip(arr, 0, 255)
If you just want to access the values over 255, or something more complicated, @mtitan8's answer is more general, but np.clip and np.minimum (or np.maximum) are nicer and much faster for your case:
In [292]: timeit np.minimum(a, 255)
100000 loops, best of 3: 19.6 µs per loop
In [293]: %%timeit
.....: c = np.copy(a)
.....: c[a>255] = 255
.....:
10000 loops, best of 3: 86.6 µs per loop
If you want to do it in-place (i.e., modify arr instead of creating result) you can use the out parameter of np.minimum:
np.minimum(arr, 255, out=arr)
or
np.clip(arr, 0, 255, arr)
(the out= name is optional since the arguments in the same order as the function's definition.)
For in-place modification, the boolean indexing speeds up a lot (without having to make and then modify the copy separately), but is still not as fast as minimum:
In [328]: %%timeit
.....: a = np.random.randint(0, 300, (100,100))
.....: np.minimum(a, 255, a)
.....:
100000 loops, best of 3: 303 µs per loop
In [329]: %%timeit
.....: a = np.random.randint(0, 300, (100,100))
.....: a[a>255] = 255
.....:
100000 loops, best of 3: 356 µs per loop
For comparison, if you wanted to restrict your values with a minimum as well as a maximum, without clip you would have to do this twice, with something like
np.minimum(a, 255, a)
np.maximum(a, 0, a)
or,
a[a>255] = 255
a[a<0] = 0
Just do boolean masking:
mask = (array_a == -999)
new_array = np.copy(array_a)
new_array[mask] = array_b[mask]
all you need to do is
array_a[array_a==-999]=array_b[array_a==-999]
we are putting boolean condition on array elements to update should have value -999
import numpy as np
array_a =np.array([[0.564,-999,-999],
[0.234,-999,0.898],
[-999,0.124,0.687],
[0.478,0.786,-999]])
array_b =np.array([[0.324,0.254,0.204],
[0.469,0.381,0.292],
[0.550,0.453,0.349],
[0.605,0.582,0.551]])
array_a[array_a==-999]=array_b[array_a==-999]
run this snippet


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.