在python中查找和替换多个值
我想查找一维数组/列表中的多个值,并将其替换为新值 例如,列表在python中查找和替换多个值,python,numpy,pandas,Python,Numpy,Pandas,我想查找一维数组/列表中的多个值,并将其替换为新值 例如,列表 a=[2, 3, 2, 5, 4, 4, 1, 2] 我想换一个 val_old=[1, 2, 3, 4, 5] 与 因此,新阵列是: a_new=[3, 4, 3, 1, 5, 5, 2, 3] 最快的方法是什么(对于非常大的列表,即需要查找和替换50000个值) 评论 谢谢大家的快速回复!我用以下内容检查了提议的解决方案: N = 10**4 N_val = 0.5*N a = np.random.randint(0,
a=[2, 3, 2, 5, 4, 4, 1, 2]
我想换一个
val_old=[1, 2, 3, 4, 5]
与
因此,新阵列是:
a_new=[3, 4, 3, 1, 5, 5, 2, 3]
最快的方法是什么(对于非常大的列表,即需要查找和替换50000个值)
评论
谢谢大家的快速回复!我用以下内容检查了提议的解决方案:
N = 10**4
N_val = 0.5*N
a = np.random.randint(0, N_val, size=N)
val_old = np.arange(N_val, dtype=np.int)
val_new = np.arange(N_val, dtype=np.int)
np.random.shuffle(val_new)
a1 = list(a)
val_old1 = list(val_old)
val_new1 = list(val_new)
def Ashwini_Chaudhary(a, val_old, val_new):
arr = np.empty(a.max()+1, dtype=val_new.dtype)
arr[val_old] = val_new
return arr[a]
def EdChum(a, val_old, val_new):
df = pd.Series(a, dtype=val_new.dtype)
d = dict(zip(val_old, val_new))
return df.map(d).values
def xxyzzy(a, val_old, val_new):
return [val_new[val_old.index(x)] for x in a]
def Shashank_and_Hackaholic(a, val_old, val_new):
d = dict(zip(val_old, val_new))
return [d.get(e, e) for e in a]
def itzmeontv(a, val_old, val_new):
return [val_new[val_old.index(i)] if i in val_old else i for i in a]
def swenzel(a, val_old, val_new):
return val_new[np.searchsorted(val_old,a)]
def Divakar(a, val_old, val_new):
C,R = np.where(a[:,np.newaxis] == val_old[np.newaxis,:])
a[C] = val_new[R]
return a
结果是:
%timeit -n100 Ashwini_Chaudhary(a, val_old, val_new)
100 loops, best of 3: 77.6 µs per loop
%timeit -n100 swenzel(a, val_old, val_new)
100 loops, best of 3: 703 µs per loop
%timeit -n100 Shashank_and_Hackaholic(a1, val_old1, val_new1)
100 loops, best of 3: 1.7 ms per loop
%timeit -n100 EdChum(a, val_old, val_new)
100 loops, best of 3: 17.6 ms per loop
%timeit -n10 Divakar(a, val_old, val_new)
10 loops, best of 3: 209 ms per loop
%timeit -n10 xxyzzy(a1, val_old1, val_new1)
10 loops, best of 3: 429 ms per loop
%timeit -n10 itzmeontv(a1, val_old1, val_new1)
10 loops, best of 3: 847 ms per loop
性能的相对差异随着
N
的增大而增大,即如果N=10**7
,那么Ashwini_Chaudhary的结果需要207 ms
,而swenzel的结果在香草Python中是6.89 s
,没有numpy
或pandas
的速度,这是一种方式:
a = [2, 3, 2, 5, 4, 4, 1, 2]
val_old = [1, 2, 3, 4, 5]
val_new = [2, 3, 4, 5, 1]
expected_a_new = [3, 4, 3, 1, 5, 5, 2, 3]
d = dict(zip(val_old, val_new))
a_new = [d.get(e, e) for e in a]
print a_new # [3, 4, 3, 1, 5, 5, 2, 3]
print a_new == expected_a_new # True
此算法的平均时间复杂度为
O(M+N)
,其中M
是“翻译列表”的长度,N
是列表的长度a
使用其他两个列表作为键:值对替换列表中的值有几种方法。它们都使用“列表压缩”
>>> arr = np.empty(a.max() + 1, dtype=val_new.dtype)
>>> arr[val_old] = val_new
>>> arr[a]
array([3, 4, 3, 1, 5, 5, 2, 3])
使用list.index():
使用您的特殊情况:
a=[2, 3, 2, 5, 4, 4, 1, 2]
a_new=[x % 5 + 1 for x in a]
我试着这样做:
>>> val_old=[1, 2, 3, 4, 5]
>>> val_new=[2, 3, 4, 5, 1]
>>> a=[2, 3, 2, 5, 4, 4, 1, 2]
>>> my_dict = dict(zip(val_old, val_new))
>>> [my_dict.get(x,x) for x in a]
[3, 4, 3, 1, 5, 5, 2, 3]
为您的预期输出尝试此方法,即使
元素不在值中
也有效
>>>[val_new[val_old.index(i)] if i in val_old else i for i in a]
[3, 4, 3, 1, 5, 5, 2, 3]
在pandas中,我将从2个列表中创建一个dict,然后调用map
,它将执行查找并替换值:
In [6]:
df = pd.Series([2, 3, 2, 5, 4, 4, 1, 2])
df
Out[6]:
0 2
1 3
2 2
3 5
4 4
5 4
6 1
7 2
dtype: int64
In [7]:
val_old=[1, 2, 3, 4, 5]
val_new=[2, 3, 4, 5, 1]
d = dict(zip(val_old,val_new ))
d
Out[7]:
{1: 2, 2: 3, 3: 4, 4: 5, 5: 1}
In [9]:
df.map(d)
Out[9]:
0 3
1 4
2 3
3 1
4 5
5 5
6 2
7 3
dtype: int64
对于80000元件系列,这需要3.4ms:
In [14]:
%timeit df.map(d)
100 loops, best of 3: 3.4 ms per loop
这是一种矢量化方法,比任何基于迭代的方法伸缩性都要好得多假设您的val\u old
数组已排序(这里是这种情况,但如果以后不是这样,请不要忘记将val\u new
与之一起排序!),您可以使用numpy.searchsorted
,然后使用结果访问val\u new
。
如果一个数字没有映射,则这不起作用,在这种情况下,您必须提供1对1映射。
In [1]: import numpy as np
In [2]: a = np.array([2, 3, 2, 5, 4, 4, 1, 2])
In [3]: old_val = np.array([1, 2, 3, 4, 5])
In [4]: new_val = np.array([2, 3, 4, 5, 1])
In [5]: a_new = np.array([3, 4, 3, 1, 5, 5, 2, 3])
In [6]: i = np.searchsorted(old_val,a)
In [7]: a_replaced = new_val[i]
In [8]: all(a_replaced == a_new)
Out[8]: True
五万个?没问题
In [23]: def timed():
t0 = time.time()
i = np.searchsorted(old_val, a)
a_replaced = new_val[i]
t1 = time.time()
print('%s Seconds'%(t1-t0))
....:
In [24]: a = np.random.choice(old_val, 50000)
In [25]: timed()
0.00288081169128 Seconds
500公里?你不会注意到区别的
In [26]: a = np.random.choice(old_val, 500000)
In [27]: timed()
0.019248008728 Seconds
对于numpy数组
,这可能是一种方法-
%// Find row and column IDs for matches between "a" and "val_old"
C,R = np.where(a[:,np.newaxis] == val_old[np.newaxis,:])
%// Index into "a" with the column indices and
%// set those to "val_new" elements indexed by "R"
a[C] = val_new[R]
样本运行和计时
对于输入:
a = np.random.randint(10000,size=(100000))
val_old = np.random.randint(10000,size=(1000))
val_new = np.random.randint(10000,size=(1000))
每个代码行的运行时都是-
%timeit C,R = np.where(a[:,np.newaxis] == val_old[np.newaxis,:])
1 loops, best of 3: 292 ms per loop
%timeit a[C] = val_new[R]
10000 loops, best of 3: 43 µs per loop
该软件包(免责声明:我是其作者)为此类问题提供了一个优雅而高效的矢量化解决方案:
import numpy_indexed as npi
remapped_a = npi.remap(a, val_old, val_new)
实现的方法基于类似swenzel的searchsorted,应该具有类似的良好性能,但更通用。例如,数组的项不需要是int,但可以是任何类型,甚至可以是nd子数组本身
如果“a”中的所有值都应出现在“val_old”中,则可以将可选的“missing”kwarg设置为“raise”(默认值为“ignore”)。性能会稍微好一点,如果不满足该假设,您将得到一个关键错误。对于散列项,“索引”方法将起作用,但它将比dict方法慢。这也是我第一次尝试。。。如果a
包含负数,则会有点棘手。对于负数,请计算附加偏移量:offset=max(-a.min(),0);arr=np.empty(a.max()+1+offset,dtype=val\u new.dtype);arr[val_old+offset]=val_new;a_new=arr[a+offset]
这种方法不是矢量化的,map
使用迭代。对于长列表,绘制映射要快一些,但是构建系列所需的时间意味着基于迭代的方法最终会更快。这里有一个几乎相同的问题:如果一个人需要一个通用的非整数解,非常有趣的是,对于大量替换,Shashank的解是最快的。对于替换数量较少的问题,链接问题中可接受答案的numpy解决方案是最好的。python字典和列表理解的速度真是太快了。
list(map(lambda x:val_new[val_old.index(x)], a))
import numpy_indexed as npi
remapped_a = npi.remap(a, val_old, val_new)
list(map(lambda x:val_new[val_old.index(x)], a))