J'ai une liste Python, dites a = [0,1,2,3,4,5,6]
. J'ai également une liste d'indices, par exemple b = [0,2,4,5]
. Comment obtenir la liste des éléments de a
avec des indices dans b
?
Vous pouvez utiliser la compréhension de la liste pour obtenir cette liste:
c = [a[index] for index in b]
print c
Cela équivaut à:
c= []
for index in b:
c.append(a[index])
print c
Sortie:
[0,2,4,5]
Remarque:
N'oubliez pas que some_list[index]
est la notation utilisée pour accéder à un élément d'un list
dans un index spécifique.
Quelque chose de différent...
>>> a = range(7)
>>> b = [0,2,4,5]
>>> import operator
>>> operator.itemgetter(*b)(a)
(0, 2, 4, 5)
La fonction itemgetter
prend une ou plusieurs clés comme arguments et retourne une fonction qui renverra les éléments aux clés données dans son argument. Donc, dans ce qui précède, nous créons une fonction qui renverra les éléments à l'index 0, l'index 2, l'index 4 et l'index 5, puis appliquer cette fonction à a
.
Il semble être un peu plus rapide que la compréhension de liste équivalente
In [1]: import operator
In [2]: a = range(7)
In [3]: b = [0,2,4,5]
In [4]: %timeit operator.itemgetter(*b)(a)
1000000 loops, best of 3: 388 ns per loop
In [5]: %timeit [ a[i] for i in b ]
1000000 loops, best of 3: 415 ns per loop
In [6]: f = operator.itemgetter(*b)
In [7]: %timeit f(a)
10000000 loops, best of 3: 183 ns per loop
Quant à savoir pourquoi itemgetter
est plus rapide, la compréhension doit exécuter des codes d'octets supplémentaires Python.
In [3]: def f(a,b): return [a[i] for i in b]
In [4]: def g(a,b): return operator.itemgetter(*b)(a)
In [5]: dis.dis(f)
1 0 BUILD_LIST 0
3 LOAD_FAST 1 (b)
6 GET_ITER
>> 7 FOR_ITER 16 (to 26)
10 STORE_FAST 2 (i)
13 LOAD_FAST 0 (a)
16 LOAD_FAST 2 (i)
19 BINARY_SUBSCR
20 LIST_APPEND 2
23 JUMP_ABSOLUTE 7
>> 26 RETURN_VALUE
Alors que itemgetter
est un seul appel implémenté en C:
In [6]: dis.dis(g)
1 0 LOAD_GLOBAL 0 (operator)
3 LOAD_ATTR 1 (itemgetter)
6 LOAD_FAST 1 (b)
9 CALL_FUNCTION_VAR 0
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 RETURN_VALUE
Si vous êtes fan de programmation fonctionnelle , vous pouvez utiliser map
et list.__getitem__
:
>>> a = [0,1,2,3,4,5,6]
>>> b = [0,2,4,5]
>>> map(a.__getitem__, b)
[0, 2, 4, 5]
>>>
L'approche de compréhension de liste est plus canonique dans Python cependant ...
Un peu de comparaison de vitesse pour toutes les méthodes mentionnées et d'autres de dictionnaire Python: obtenir la liste des valeurs pour la liste des clés :
Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Jan 19 2016, 12:08:31) [MSC v.1500 64 bit (AMD64)] on win32
In[2]: import numpy.random as nprnd
idx = nprnd.randint(1000, size=10000)
l = nprnd.Rand(1000).tolist()
from operator import itemgetter
import operator
f = operator.itemgetter(*idx)
%timeit f(l)
%timeit list(itemgetter(*idx)(l))
%timeit [l[_] for _ in idx] # list comprehension
%timeit map(l.__getitem__, idx)
%timeit list(l[_] for _ in idx) # a generator expression passed to a list constructor.
%timeit map(lambda _: l[_], idx) # using 'map'
%timeit [x for i, x in enumerate(l) if i in idx]
%timeit filter(lambda x: l.index(x) in idx, l) # UPDATE @Kundor: work only for list with unique elements
10000 loops, best of 3: 175 µs per loop
1000 loops, best of 3: 707 µs per loop
1000 loops, best of 3: 978 µs per loop
1000 loops, best of 3: 1.03 ms per loop
1000 loops, best of 3: 1.18 ms per loop
1000 loops, best of 3: 1.86 ms per loop
100 loops, best of 3: 12.3 ms per loop
10 loops, best of 3: 21.2 ms per loop
Le plus rapide est donc f = operator.itemgetter(*idx); f(l)
De nombreuses solutions proposées produiront un KeyError
si b
contient un index non présent dans a
. Les éléments suivants ignoreront les index non valides si vous le souhaitez.
>>> b = [0,2,4,5]
>>> a = [0,1,2,3,4,5,6]
>>> [x for i,x in enumerate(a) if i in b]
[0, 2, 4, 5]
>>> b = [0,2,4,500]
>>> [x for i,x in enumerate(a) if i in b]
[0, 2, 4]
enumerate
produit des tuples de paires index/valeur. Puisque nous avons à la fois l'élément et son index, nous pouvons vérifier la présence de l'index dans b
En utilisant List Comprehension , cela devrait fonctionner -
li = [a[i] for i in b]
Tester cela -
>>> a = [0,10,20,30,40,50,60]
>>> b = [0,2,4,5]
>>> li = [a[i] for i in b]
>>> li
[0, 20, 40, 50]
En utilisant numpy.asarray
. Numpy permet d'obtenir un sous-tableau de tableau par liste d'indices.
>>> import numpy as np
>>> a = [0,10,20,30,40,50,60]
>>> b = [0,2,4,5]
>>> res = np.asarray(a)[b].tolist()
>>> res
[0, 20, 40, 50]
Encore une autre alternative pour de meilleures performances si cela est important pour vous - ce n'est en aucun cas le plus Pythonic mais je suis presque sûr que c'est le plus efficace:
>>> list(filter(lambda x: a.index(x) in b, a))
[0, 2, 4, 5]
Remarque: Vous n'avez pas besoin de convertir en list
en Python 2. Cependant, vous le faites en Python À partir de 3 ans (si de futurs visiteurs peuvent avoir un problème similaire).