Extraire le premier élément de chaque sous-liste

146

Je me demande quelle est la meilleure façon d'extraire le premier élément de chaque sous-liste dans une liste de listes et de l'ajouter à une nouvelle liste. Donc si j'ai:

lst = [[a,b,c], [1,2,3], [x,y,z]]

et je veux me retirer a , 1et xcréer une liste séparée de ceux -ci .

J'ai essayé:

lst2.append(x[0] for x in lst)
Konrad
la source
1
Votre code est presque correct. Le seul problème est l'utilisation de la compréhension de liste.
Abhishek Mittal le

Réponses:

198

Utilisation de la compréhension de liste :

>>> lst = [['a','b','c'], [1,2,3], ['x','y','z']]
>>> lst2 = [item[0] for item in lst]
>>> lst2
['a', 1, 'x']
alecxe
la source
La méthode de compréhension de liste est également la plus rapide, voire plus rapide que la méthode Numpy. La réponse de jboi parle de comparaison des performances,
Qiao Zhang
83

Vous pouvez utiliser zip:

>>> lst=[[1,2,3],[11,12,13],[21,22,23]]
>>> zip(*lst)[0]
(1, 11, 21)

Ou, Python 3 où zipne produit pas de liste:

>>> list(zip(*lst))[0]
(1, 11, 21)

Ou,

>>> next(zip(*lst))
(1, 11, 21)

Ou, (mon préféré) utilisez numpy:

>>> import numpy as np
>>> a=np.array([[1,2,3],[11,12,13],[21,22,23]])
>>> a
array([[ 1,  2,  3],
       [11, 12, 13],
       [21, 22, 23]])
>>> a[:,0]
array([ 1, 11, 21])
dawg
la source
N'ont pas voté contre mais le premier extrait de code (le zip) produit: "l'objet 'zip' n'est pas en indice". Python 3.6 sur Jupyter.
jboi
@jboi: Juste envelopper listautour première ou utilisation next. Merci
dawg
20

J'ai eu le même problème et j'étais curieux de connaître les performances de chaque solution.

Voici le %timeit:

import numpy as np
lst = [['a','b','c'], [1,2,3], ['x','y','z']]

Le premier numpy-way, transformant le tableau:

%timeit list(np.array(lst).T[0])
4.9 µs ± 163 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Entièrement natif en utilisant la compréhension de liste (comme expliqué par @alecxe):

%timeit [item[0] for item in lst]
379 ns ± 23.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Une autre façon native d'utiliser zip(comme expliqué par @dawg):

%timeit list(zip(*lst))[0]
585 ns ± 7.26 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Deuxième façon numpy. Egalement expliqué par @dawg:

%timeit list(np.array(lst)[:,0])
4.95 µs ± 179 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Étonnamment (enfin, du moins pour moi), la méthode native utilisant la compréhension de liste est la plus rapide et environ 10 fois plus rapide que la méthode numpy. Exécuter les deux numpy-way sans la finalelist permet d'économiser environ un µs, ce qui est toujours dans la différence de 10x.

Notez que, lorsque j'ai entouré chaque extrait de code d'un appel à len, pour m'assurer que les générateurs fonctionnent jusqu'à la fin, le timing est resté le même.

jboi
la source
4
il y a une surcharge importante lors de la création d'un tableau.
hpaulj
1
d'accord avec hpaulj, si vous commencez avec un tableau numpy, [:, 0] est plus rapide. Allez-y: lst = np.array ([['a', 'b', 'c'], [1,2,3], ['x', 'y', 'z']]), puis lst [:, 0]. La conversion dans l'exemple de contre-la-montre donne un avantage injuste à la compréhension de la liste. Donc, si vous le pouvez, utilisez un tableau numpy pour stocker vos données si la vitesse est votre objectif ultime. Numpy est presque toujours plus rapide. Il est conçu pour la vitesse.
spacedustpi
13

Python inclut une fonction appelée itemgetter pour renvoyer l'élément à un index spécifique dans une liste:

from operator import itemgetter

Passez à la fonction itemgetter () l'index de l'élément que vous souhaitez récupérer. Pour récupérer le premier élément, vous utiliseriez itemgetter (0). La chose importante à comprendre est que itemgetter (0) lui-même renvoie une fonction. Si vous passez une liste à cette fonction, vous obtenez l'élément spécifique:

itemgetter(0)([10, 20, 30]) # Returns 10

Ceci est utile lorsque vous le combinez avec map (), qui prend une fonction comme premier argument, et une liste (ou tout autre itérable) comme deuxième argument. Il renvoie le résultat de l'appel de la fonction sur chaque objet de l'itérable:

my_list = [['a', 'b', 'c'], [1, 2, 3], ['x', 'y', 'z']]
list(map(itemgetter(0), my_list)) # Returns ['a', 1, 'x']

Notez que map () renvoie un générateur, donc le résultat est passé à list () pour obtenir une liste réelle. En résumé, votre tâche pourrait être effectuée comme ceci:

lst2.append(list(map(itemgetter(0), lst)))

Il s'agit d'une méthode alternative à l'utilisation d'une compréhension de liste, et la méthode à choisir dépend fortement du contexte, de la lisibilité et des préférences.

Plus d'infos: https://docs.python.org/3/library/operator.html#operator.itemgetter

Christian Abbott
la source
2

Votre code est presque correct. Le seul problème est l'utilisation de la compréhension de liste.

Si vous utilisez comme: (x [0] pour x dans lst), cela renvoie un objet générateur. Si vous utilisez comme: [x [0] pour x dans lst], cela renvoie une liste.

Lorsque vous ajoutez la sortie de compréhension de liste à une liste, la sortie de compréhension de liste est l'élément unique de la liste.

lst = [["a","b","c"], [1,2,3], ["x","y","z"]]
lst2 = []
lst2.append([x[0] for x in lst])
print lst2[0]

lst2 = [['a', 1, 'x']]

lst2 [0] = ['a', 1, 'x']

Veuillez me faire savoir si je me trompe.

Abhishek Mittal
la source
1
lst = [['a','b','c'], [1,2,3], ['x','y','z']]
outputlist = []
for values in lst:
    outputlist.append(values[0])

print(outputlist) 

Production: ['a', 1, 'x']

PrabhuPrakash
la source
0

Vous avez dit que vous avez une liste existante. Alors je vais y aller.

>>> lst1 = [['a','b','c'], [1,2,3], ['x','y','z']]
>>> lst2 = [1, 2, 3]

En ce moment, vous ajoutez l'objet générateur à votre deuxième liste.

>>> lst2.append(item[0] for item in lst)
>>> lst2
[1, 2, 3, <generator object <genexpr> at 0xb74b3554>]

Mais vous voulez probablement que ce soit une liste des premiers éléments

>>> lst2.append([item[0] for item in lst])
>>> lst2
[1, 2, 3, ['a', 1, 'x']]

Nous avons maintenant ajouté la liste des premiers éléments à la liste existante. Si vous souhaitez ajouter les thèmes des éléments eux-mêmes, pas une liste d'entre eux, aux éléments existants, vous utiliserez list.extend. Dans ce cas, nous n'avons pas à nous soucier d'ajouter un générateur, car extend utilisera ce générateur pour ajouter chaque élément qu'il obtient à partir de là, pour étendre la liste actuelle.

>>> lst2.extend(item[0] for item in lst)
>>> lst2
[1, 2, 3, 'a', 1, 'x']

ou

>>> lst2 + [x[0] for x in lst]
[1, 2, 3, 'a', 1, 'x']
>>> lst2
[1, 2, 3]

https://docs.python.org/3.4/tutorial/datastructures.html#more-on-lists https://docs.python.org/3.4/tutorial/datastructures.html#list-comprehensions

Hendrik
la source
1
Votre réponse est belle et complète pour ce que cela ressemble à ce que veut l'OP, mais je pense que le mot appenddans la question est source de confusion. Il semble qu'il / elle souhaite simplement la partie compréhension de la liste de votre solution.
beroe