2015-04-02 147 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。