Estrai il primo elemento di ciascun elenco secondario


146

Mi chiedo quale sia il modo migliore per estrarre il primo elemento di ciascun elenco secondario in un elenco di elenchi e aggiungerlo a un nuovo elenco. Quindi se ho:

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

e voglio tirare fuori a, 1e xe creare una lista separata da quelle.

Provai:

lst2.append(x[0] for x in lst)

1
Il tuo codice è quasi corretto. L'unico problema è l'uso della comprensione dell'elenco.
Abhishek Mittal,

Risposte:


198

Utilizzando la comprensione dell'elenco :

>>> lst = [['a','b','c'], [1,2,3], ['x','y','z']]
>>> lst2 = [item[0] for item in lst]
>>> lst2
['a', 1, 'x']

Il metodo di comprensione dell'elenco è anche il più veloce, anche più veloce del metodo Numpy. La risposta di jboi parla del confronto delle prestazioni,
Qiao Zhang

83

Puoi usare zip:

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

Oppure, Python 3 dove zipnon produce un elenco:

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

O,

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

Oppure, (il mio preferito) usa 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])

Non sono stati sottoposti a downgrade, ma il primo frammento di codice (zip) produce: "L'oggetto 'zip' non è sottoscrivibile". Python 3.6 su Jupyter.
jboi,

@jboi: basta avvolgerlo listprima o usarlo next. Grazie
dawg

20

Aveva lo stesso problema e incuriosito dalle prestazioni di ciascuna soluzione.

Ecco il %timeit:

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

Il primo modo intorpidito, trasformando l'array:

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

Completamente nativo usando la comprensione dell'elenco (come spiegato da @alecxe):

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

Un altro modo nativo usando zip(come spiegato da @dawg):

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

Secondo modo intontito. Spiegato anche da @dawg:

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

Sorprendentemente (beh, almeno per me) il modo nativo usando la comprensione della lista è il più veloce e circa 10 volte più veloce del modo intorpidito. Eseguendo i due modi intorpiditi senza il finale si listrisparmia circa un µs che è ancora nella differenza di 10x.

Nota che, quando ho circondato ogni frammento di codice con una chiamata a len, per garantire che i generatori funzionino fino alla fine, i tempi sono rimasti gli stessi.


4
c'è un notevole sovraccarico durante la creazione di un array.
hpaulj,

1
d'accordo con hpaulj, se inizi con l'array numpy, [:, 0] è più veloce. Provalo: lst = np.array ([['a', 'b', 'c'], [1,2,3], ['x', 'y', 'z']]), quindi lst [:, 0]. La conversione nelle prove a tempo di esempio fornisce alla comprensione dell'elenco un vantaggio sleale. Quindi, se puoi, usa un array intorpidito per archiviare i tuoi dati se la velocità è il tuo obiettivo finale. Numpy è quasi sempre più veloce. È costruito per la velocità.
spacedustpi,

13

Python include una funzione chiamata itemgetter per restituire l'oggetto in corrispondenza di un indice specifico in un elenco:

from operator import itemgetter

Passa la funzione itemgetter () all'indice dell'elemento che desideri recuperare. Per recuperare il primo elemento, utilizzare itemgetter (0). La cosa importante da capire è che itemgetter (0) stesso restituisce una funzione. Se si passa un elenco a quella funzione, si ottiene l'elemento specifico:

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

Ciò è utile quando lo si combina con map (), che accetta una funzione come primo argomento e un elenco (o qualsiasi altro iterabile) come secondo argomento. Restituisce il risultato della chiamata della funzione su ciascun oggetto nell'iterabile:

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

Notare che map () restituisce un generatore, quindi il risultato viene passato a list () per ottenere un elenco effettivo. In sintesi, l'attività potrebbe essere eseguita in questo modo:

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

Questo è un metodo alternativo all'utilizzo di una comprensione dell'elenco e quale metodo scegliere fortemente dipende dal contesto, dalla leggibilità e dalle preferenze.

Maggiori informazioni: https://docs.python.org/3/library/operator.html#operator.itemgetter


2

Il tuo codice è quasi corretto. L'unico problema è l'uso della comprensione dell'elenco.

Se usi like: (x [0] per x in lst), restituisce un oggetto generatore. Se usi come: [x [0] per x in lst], restituisce un elenco.

Quando si aggiunge l'output di comprensione dell'elenco a un elenco, l'output della comprensione dell'elenco è il singolo elemento dell'elenco.

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']

Per favore fatemi sapere se non sono corretto.


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

print(outputlist) 

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


0

Hai detto di avere un elenco esistente. Quindi vado con quello.

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

In questo momento stai aggiungendo l'oggetto generatore al tuo secondo elenco.

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

Ma probabilmente vuoi che sia un elenco dei primi elementi

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

Ora abbiamo aggiunto l'elenco dei primi elementi all'elenco esistente. Se desideri aggiungere gli stessi temi, non un elenco di essi, a quelli esistenti, useresti list.extend. In tal caso non dobbiamo preoccuparci di aggiungere un generatore, perché extender utilizzerà quel generatore per aggiungere ogni elemento che ottiene da lì, per estendere l'elenco corrente.

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

o

>>> 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


1
La tua risposta è bella e completa per quello che sembra che l'OP voglia, ma penso che la parola appendnella domanda stia causando confusione. Sembra che voglia semplicemente la parte di comprensione dell'elenco della tua soluzione.
beroe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.