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
.
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])
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)
où M
est la longueur de votre "liste de traduction" et N
est la longueur de la liste a
.
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
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]
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.
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]
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]
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
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