The biggest problem with your code is that it's unreadable. Python code rule number one, if it's not readable, no one's gonna look at it for long enough to get any useful information out of it. Always use descriptive variable names. Almost didn't catch the bug in your code, let's see it again with good names, slow-motion replay style:
to_modify = [5,4,3,2,1,0]
indexes = [0,1,3,5]
replacements = [0,0,0,0]
for index in indexes:
to_modify[indexes[index]] = replacements[index]
# to_modify[indexes[index]]
# indexes[index]
# Yo dawg, I heard you liked indexes, so I put an index inside your indexes
# so you can go out of bounds while you go out of bounds.
As is obvious when you use descriptive variable names, you're indexing the list of indexes with values from itself, which doesn't make sense in this case.
Also when iterating through 2 lists in parallel I like to use the zip function (or izip if you're worried about memory consumption, but I'm not one of those iteration purists). So try this instead.
for (index, replacement) in zip(indexes, replacements):
to_modify[index] = replacement
If your problem is only working with lists of numbers then I'd say that @steabert has the answer you were looking for with that numpy stuff. However you can't use sequences or other variable-sized data types as elements of numpy arrays, so if your variable to_modify has anything like that in it, you're probably best off doing it with a for loop.
The biggest problem with your code is that it's unreadable. Python code rule number one, if it's not readable, no one's gonna look at it for long enough to get any useful information out of it. Always use descriptive variable names. Almost didn't catch the bug in your code, let's see it again with good names, slow-motion replay style:
to_modify = [5,4,3,2,1,0]
indexes = [0,1,3,5]
replacements = [0,0,0,0]
for index in indexes:
to_modify[indexes[index]] = replacements[index]
# to_modify[indexes[index]]
# indexes[index]
# Yo dawg, I heard you liked indexes, so I put an index inside your indexes
# so you can go out of bounds while you go out of bounds.
As is obvious when you use descriptive variable names, you're indexing the list of indexes with values from itself, which doesn't make sense in this case.
Also when iterating through 2 lists in parallel I like to use the zip function (or izip if you're worried about memory consumption, but I'm not one of those iteration purists). So try this instead.
for (index, replacement) in zip(indexes, replacements):
to_modify[index] = replacement
If your problem is only working with lists of numbers then I'd say that @steabert has the answer you were looking for with that numpy stuff. However you can't use sequences or other variable-sized data types as elements of numpy arrays, so if your variable to_modify has anything like that in it, you're probably best off doing it with a for loop.
numpy has arrays that allow you to use other lists/arrays as indices:
import numpy
S=numpy.array(s)
S[a]=m
python - Replace value in an array by its index in a list - Stack Overflow
python - Replace values of an array based on a vector of indices - 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 - How to replace certain elements of a NumPy array via an index array - Stack Overflow
How do I replace one item in a Python list?
How do I replace every matching item in a list?
Can I replace list items with map()?
take is your friend.
a = np.array([[0, 1, 2], [2, 1, 3], [0, 1, 3]])
chg = np.array([3, 0, 2, 1])
inverse_chg=chg.take(chg)
print(inverse_chg.take(a))
gives :
[[1 3 2]
[2 3 0]
[1 3 0]]
or more directly with fancy indexing: chg[chg][a], but inverse_chg.take(a) is three times faster.
You can convert chg to a 3D array by adding two new axes at the end of it and then perform the matching comparison with a, which would bring in NumPy's broadcasting to give us a 3D mask. Next up, get the argmax on the mask along the first axis to simulate "b[a == old] = new". Finally, replace the ones that had no matches along that axis with the corresponding values in a. The implementation would look something like this -
mask = a == chg[:,None,None]
out = mask.argmax(0)
invalid_pos = ~mask.max(0)
out[invalid_pos] = a[invalid_pos]
You can just index your value array with the indices array as they are integers (required for indexing):
vals[ind]
which produces:
array([0.2, 0.2, 0.3, 0.2])
as desired.
Not sure if this is the NumPy best practice way, but you can do:
[vals[i] for i in ind]
new_vals = np.array([vals[i] for i in ind])
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
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.
Use loc:
df.loc[0:15,'A'] = 16
print (df)
A B
0 16 45
1 16 5
2 16 97
3 16 58
4 16 26
5 16 87
6 16 51
7 16 17
8 16 39
9 16 73
10 16 94
11 16 69
12 16 57
13 16 24
14 16 43
15 16 77
16 41 0
17 3 21
18 0 98
19 45 39
20 66 62
21 8 53
22 69 47
23 48 53
Solution with ix is deprecated.
In addition to the other answers, here is what you can do if you have a list of individual indices:
indices = [0,1,3,6,10,15]
df.loc[indices,'A'] = 16
print(df.head(16))
Output:
A B
0 16 4
1 16 4
2 4 3
3 16 4
4 1 1
5 3 0
6 16 4
7 2 1
8 4 4
9 3 4
10 16 0
11 3 1
12 4 2
13 2 2
14 2 1
15 16 1
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
you can slice your list accordingly!
That is, with
aandbbeing your initial list and the one that you want to replace from indexs,a[:s]will get all elements before from 0 to s that is ([1,2]).a[s+len(b):]will get all items from indexstolen(b), that is ([6,7,8])
so when you concatenate the first result along with b and then the second result you can get the desired output!
a[:s]+b+a[s+len(b):]
So,
>>> a = [1,2,3,4,5,6,7,8]
>>> b = ['a','b','c']
>>> replace=lambda a,b,s:a[:s]+b+a[s+len(b):]
>>> c = replace(a,b,2)
>>> print c
[1, 2, 'a', 'b', 'c', 6, 7, 8]
Hope this helps!
Okay, here is another simple way to do this: (Edit: forgot slicing a to copy it into c)
a = [1,2,3,4,5,6,7,8]
b = ['a','b','c']
c=a[:]
c[2:5]=b
print(c)
Or, when you want to replace in place:
a = [1,2,3,4,5,6,7,8]
b = ['a','b','c']
a[2:5]=b
print(a)
Hope I helped!