Rechercher et remplacer plusieurs valeurs en python

je veux trouver et remplacer plusieurs valeurs dans un tableau / liste 1D par de nouvelles.

par exemple pour une liste

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

je voudrais remplacer

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

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

donc le nouveau tableau est:

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

Quelle est la façon la plus rapide de faire ceci (pour les listes très grandes, c.-à-d. avec 50000 valeurs à trouver et remplacer)?

anwsers

Merci à tous pour une réponse rapide! J'ai vérifié les solutions proposées avec ce qui suit:

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

résultat:

%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

la différence relative dans la performance augmente avec plus grand N , c'est à dire si N=10**7, puis le résultat par Ashwini_Chaudhary prend 207 ms et le résultat par swenzel 6.89 s.

8
demandé sur blaz 2015-04-02 11:04:51

10 réponses

>>> 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])
3
répondu Ashwini Chaudhary 2015-04-02 08:11:42

en Python vanille, sans la vitesse de numpy ou pandas, de cette manière:

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

moyenne la complexité temporelle de cet algorithme est O(M + N)M est la longueur de votre "liste de traduction" et N est la longueur de la liste a.

3
répondu Shashank 2015-04-02 17:12:03

en Supposant que votre val_old tableau est trié (ce qui est le cas ici, mais si plus tard, il ne l'est pas, alors n'oubliez pas de trier val_new avec!), vous pouvez utiliser numpy.searchsorted puis accédez à val_new avec les résultats.

Cela ne fonctionne pas si un nombre n'a pas de cartographie, vous devrez fournir 1to1 mappages dans ce cas.

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 numéros? Pas de problème!

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? Vous ne remarquerez pas la différence!

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

In [27]: timed()
0.019248008728 Seconds
2
répondu swenzel 2015-04-02 09:06:41

essayez ceci pour votre résultat attendu, fonctionne même si elements pas 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]
1
répondu itzMEonTV 2015-04-02 08:26:23

numpy_indexed package (disclaimer: je suis son auteur) fournit un cadre élégant et efficace vectorisé solution à ce type de problème:

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

la méthode mise en œuvre est basée sur des résultats de recherche similaires à ceux de swenzel et devrait avoir des performances similaires, mais plus générales. Par exemple, les éléments du tableau n'ont pas besoin d'être des ints, mais peuvent être n'importe quel type, même les nd-subarrays eux-mêmes.

si toutes les valeurs de "a" doivent être présentes dans 'val_old', vous pouvez définir l'option "disparus" kwarg de "relance" (par défaut, 'ignore'). La Performance sera légèrement meilleure, et vous obtiendrez une erreur clé si cette hypothèse n'est pas satisfaite.

1
répondu Eelco Hoogendoorn 2016-07-26 18:41:05

pour remplacer les valeurs dans une liste en utilisant deux autres listes comme clé:les paires de VALEURs il y a plusieurs approches. Tous utilisent la "compression de liste".

Utilisation de la liste.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]

à l'Aide de votre cas particulier:

a=[2, 3, 2, 5, 4, 4, 1, 2]
a_new=[x % 5 + 1 for x in a]
0
répondu xxyzzy 2015-04-02 08:21:46

j'ai essayé comme ceci:

>>> 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
répondu Hackaholic 2015-04-02 08:22:08

dans pandas je créerais un dict à partir des 2 listes et ensuite j'appellerais map qui va effectuer une recherche et remplacer les valeurs:

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

pour une série de 80000 éléments cela prend 3,4 ms:

In [14]:

%timeit df.map(d)

100 loops, best of 3: 3.4 ms per loop

il s'agit d'une approche vectorisée qui sera beaucoup plus efficace que n'importe quelle méthode basée sur l'itération

0
répondu EdChum 2015-04-02 08:30:48

numpy arrays, cela pourrait être une approche -

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

echantillonnage et calendrier

Pour les entrées:

a = np.random.randint(10000,size=(100000))
val_old = np.random.randint(10000,size=(1000))
val_new = np.random.randint(10000,size=(1000))

les temps D'exécution à chaque ligne de code étaient -

%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
0
répondu Divakar 2015-04-02 11:27:02
list(map(lambda x:val_new[val_old.index(x)], a))
0
répondu Andyk 2018-04-29 16:36:08