2015-04-02 38 views
8

我想查找并替换一维数组/列表中的多个值。在python中查找并替换多个值

在例如用于列表

a=[2, 3, 2, 5, 4, 4, 1, 2] 

我想更换

val_old=[1, 2, 3, 4, 5] 

val_new=[2, 3, 4, 5, 1] 

因此新的数组是:

a_new=[3, 4, 3, 1, 5, 5, 2, 3] 

这样做的最快方法是什么(对于非常大的列表,即50000个值来查找和替换)?

评论anwsers

感谢所有为快速响应!我检查了提出的解决方案具有如下:

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 6.89 s

+1

这里是几乎相同的问题:http://stackoverflow.com/questions/3403973/fast-replacement-of-values-in-a-numpy-array 如果需要一个通用的非整数解决方案,它是真正有趣的是,对于大量的替代* Shashank *的解决方案是最快的。对于少数替代品,在相关问题中接受答案的numpy解决方案是最好的。 python字典和列表解析速度有多快是很棒的。 – knedlsepp 2015-04-02 19:57:25

回答

2
>>> 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]) 
+1

也是我的第一次尝试...如果'a'包含负数,会有点棘手。 – swenzel 2015-04-02 09:20:58

+0

对于负数计算额外的偏移量:'offset = max(-a.min(),0); arr = np.empty(a.max()+ 1 + offset,dtype = val_new.dtype); arr [val_old + offset] = val_new; a_new = arr [a + offset]' – 2017-06-23 06:22:30

3

在香草Python中,没有numpypandas的速度,这也是一个办法:

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

+0

有人会认为有更快的numpy解决方案作为这个通用的... – knedlsepp 2015-04-02 19:17:16

0

要使用两个其他列表替换列表中的值作为键:值对,有几种方法。他们都使用“列表压缩”。

使用list.index():

a=[2, 3, 2, 5, 4, 4, 1, 2] 
val_old=[1, 2, 3, 4, 5] 
val_new=[2, 3, 4, 5, 1] 
a_new=[val_new[val_old.index(x)] for x in a] 

使用你的特殊情况:

a=[2, 3, 2, 5, 4, 4, 1, 2] 
a_new=[x % 5 + 1 for x in a] 
+1

'索引'方法可以工作,但对于可排列项目它会比'dict'方法慢。 – TheBlackCat 2015-04-02 09:19:27

0

我想是这样的:

>>> 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] 
0

试试这个你预期的输出,作品即使elements不在value_old

>>>[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] 
0

在熊猫我将从2所列出创建的字典,然后调用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 

这是一个向量化的方法,将规模比任何基于迭代的方法要好得多

+0

这种方法不是矢量化的,'map'使用迭代。对于长列表,执行'map'要快一些,但构建'Series'所需的时间意味着基于迭代的方法总体上更快。 – TheBlackCat 2015-04-02 09:28:16

2

假设您的val_old阵列已排序(这里是这种情况,但如果后面不是,那么不要忘记将val_new与它一起排序!),则可以使用numpy.searchsorted,然后访问val_new并显示结果。
如果一个数字没有映射,这不起作用,那么在这种情况下您将不得不提供1to1映射。

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 

50k数字?没问题!

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 

500k?你不会注意到其中的差异!

In [26]: a = np.random.choice(old_val, 500000) 

In [27]: timed() 
0.019248008728 Seconds 
0

对于numpy arrays,这可能是一种方法 -

%// 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 
1

numpy_indexed包(声明:我其作者)提供了一个优雅和有效的量化解决这种类型的问题:

import numpy_indexed as npi 
remapped_a = npi.remap(a, val_old, val_new) 

实现的方法是基于类似swenzel的搜索,应该有类似的良好表现,但更一般。例如,数组的项不需要是整数,但可以是任何类型,即使是nd子数组本身。

如果'a'中的所有值预计存在于'val_old'中,那么可以将可选的'missing'kwarg设置为'raise'(默认为'ignore')。性能会稍微好一点,如果这个假设不被满足,你会得到一个KeyError。