Accéder à plusieurs éléments de la liste en connaissant leur index

233

J'ai besoin de choisir certains éléments de la liste donnée, en connaissant leur index. Disons que je voudrais créer une nouvelle liste, qui contient un élément d'index 1, 2, 5, à partir d'une liste donnée [-2, 1, 5, 3, 8, 5, 6]. Ce que j'ai fait c'est:

a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [ a[i] for i in b]

Y a-t-il une meilleure façon de le faire? quelque chose comme c = a [b]?

hoang tran
la source
1
au fait, j'ai trouvé une autre solution ici. Je ne l'ai pas encore testé, mais je pense que je peux le poster ici une fois que vous êtes intéressé par code.activestate.com/recipes/…
hoang tran
C'est la même solution que celle mentionnée dans la question, mais enveloppée dans une lambdafonction.
Will Dereham

Réponses:

219

Vous pouvez utiliser operator.itemgetter:

from operator import itemgetter 
a = [-2, 1, 5, 3, 8, 5, 6]
b = [1, 2, 5]
print(itemgetter(*b)(a))
# Result:
(1, 5, 5)

Ou vous pouvez utiliser numpy :

import numpy as np
a = np.array([-2, 1, 5, 3, 8, 5, 6])
b = [1, 2, 5]
print(list(a[b]))
# Result:
[1, 5, 5]

Mais vraiment, votre solution actuelle est très bien. C'est probablement le plus net de tous.

TerryA
la source
36
+1 pour avoir mentionné que c = [a[i] for i in b]c'est parfaitement bien. Notez que la itemgettersolution ne fera pas la même chose si b a moins de 2 éléments.
flornquake
Note latérale : Utiliser itemgetter tout en travaillant en multi-processus ne fonctionne pas. Numpy fonctionne très bien en multi-processus.
Lior Magen
3
Commentaire supplémentaire, nea[b] fonctionne que lorsqu'il as'agit d'un tableau numpy , c'est-à-dire que vous le créez avec une fonction numpy.
Ludwig Zhou
J'ai comparé les options non numpy et itemgetter semble être le plus rapide, même légèrement plus rapide que de simplement taper les index souhaités entre parenthèses, en utilisant Python 3.44
ragardner
@ citizen2077, pouvez-vous donner un exemple de la syntaxe que vous décrivez?
alancalvitti
47

Alternatives:

>>> map(a.__getitem__, b)
[1, 5, 5]

>>> import operator
>>> operator.itemgetter(*b)(a)
(1, 5, 5)
falsetru
la source
le premier est sympa parce que vous utilisez des build-infonctions
silgon
Le problème avec le premier est que __getitem__cela ne semble pas être comparable, par exemple, comment mapper le type de l'élément? map(type(a.__getitem__), b)
alancalvitti
@alancalvitti, lambda x: type(a.__getitem__(x)), b. Dans ce cas, l'utilisation [..]est plus compacte:lambda x: type(a[x]), b
falsetru
9

Une autre solution pourrait être via la série pandas:

import pandas as pd

a = pd.Series([-2, 1, 5, 3, 8, 5, 6])
b = [1, 2, 5]
c = a[b]

Vous pouvez ensuite reconvertir c en liste si vous le souhaitez:

c = list(c)
BossaNova
la source
7

Tests basiques et peu poussés comparant le temps d'exécution des cinq réponses fournies:

def numpyIndexValues(a, b):
    na = np.array(a)
    nb = np.array(b)
    out = list(na[nb])
    return out

def mapIndexValues(a, b):
    out = map(a.__getitem__, b)
    return list(out)

def getIndexValues(a, b):
    out = operator.itemgetter(*b)(a)
    return out

def pythonLoopOverlap(a, b):
    c = [ a[i] for i in b]
    return c

multipleListItemValues = lambda searchList, ind: [searchList[i] for i in ind]

en utilisant l'entrée suivante:

a = range(0, 10000000)
b = range(500, 500000)

la boucle python simple était la plus rapide avec l'opération lambda une seconde près, mapIndexValues ​​et getIndexValues ​​étaient constamment assez similaires avec la méthode numpy beaucoup plus lente après la conversion des listes en tableaux numpy.Si les données sont déjà dans les tableaux numpy, la méthode numpyIndexValues ​​avec la conversion numpy.array supprimée est le plus rapide.

numpyIndexValues -> time:1.38940598 (when converted the lists to numpy arrays)
numpyIndexValues -> time:0.0193445 (using numpy array instead of python list as input, and conversion code removed)
mapIndexValues -> time:0.06477512099999999
getIndexValues -> time:0.06391049500000001
multipleListItemValues -> time:0.043773591
pythonLoopOverlap -> time:0.043021754999999995
Don Smythe
la source
Je ne sais pas quel interpréteur Python vous utilisez mais la première méthode numpyIndexValuesne fonctionne pas depuis a, bsont de type range. Je suppose que vous avez l'intention de vous convertir a, bd' numpy.ndarraysabord?
strpeter
@strpeter Oui, je ne comparais pas des pommes avec des pommes, j'avais créé des tableaux numpy comme entrée dans le cas de test pour les numpyIndexValues. J'ai corrigé cela maintenant et tous utilisent les mêmes listes en entrée.
Don Smythe
4

Je suis sûr que cela a déjà été pris en compte: si la quantité d'indices en b est petite et constante, on pourrait simplement écrire le résultat comme:

c = [a[b[0]]] + [a[b[1]]] + [a[b[2]]]

Ou encore plus simple si les indices eux-mêmes sont constants ...

c = [a[1]] + [a[2]] + [a[5]]

Ou s'il y a une gamme consécutive d'indices ...

c = a[1:3] + [a[5]]
ecp
la source
Merci de me le rappeler[a] + [b] = [a, b]
onewhaleid
3

Voici un moyen plus simple:

a = [-2,1,5,3,8,5,6]
b = [1,2,5]
c = [e for i, e in enumerate(a) if i in b]
Max Sirwa
la source
1

Ma réponse n'utilise pas de collections numpy ou python.

Une façon triviale de trouver des éléments serait la suivante:

a = [-2, 1, 5, 3, 8, 5, 6]
b = [1, 2, 5]
c = [i for i in a if i in b]

Inconvénient: cette méthode peut ne pas fonctionner pour les grandes listes. L'utilisation de numpy est recommandée pour les grandes listes.

Lavya 'Orion'
la source
5
Pas besoin d'itérer a. [a[i] for i in b]
falsetru
1
Cette méthode ne fonctionne même dans aucun autre cas. Et si il y en aavait encore 5?
TerryA
OMI, plus rapide pour faire ce genre d'intersection en utilisant des ensembles
sirgogo
Si IndexErrors vous inquiète si b a des nombres qui dépassent la taille de a, essayez[a[i] if i<len(a) else None for i in b]
576i
0

Index statiques et petite liste?

N'oubliez pas que si la liste est petite et que les index ne changent pas, comme dans votre exemple, la meilleure chose à faire est parfois d'utiliser le décompression de séquence :

_,a1,a2,_,_,a3,_ = a

Les performances sont bien meilleures et vous pouvez également enregistrer une ligne de code:

 %timeit _,a1,b1,_,_,c1,_ = a
10000000 loops, best of 3: 154 ns per loop 
%timeit itemgetter(*b)(a)
1000000 loops, best of 3: 753 ns per loop
 %timeit [ a[i] for i in b]
1000000 loops, best of 3: 777 ns per loop
 %timeit map(a.__getitem__, b)
1000000 loops, best of 3: 1.42 µs per loop
GM
la source
0

Type de manière pythonique:

c = [x for x in a if a.index(x) in b]
Dmitry K. Chebanov
la source
2
Je dirais que c'est moins "pythonique" que même l'exemple de l'OP - vous avez réussi à transformer leur O(n)solution en une O(n^2)solution tout en doublant presque la longueur du code. Vous voudrez également noter que l'approche échouera si la liste contient des objets sera floue ou égalité partielle, par exemple si acontient float('nan'), cela soulèvera toujours a ValueError.
Brian