2013-01-12 19 views
15

Se ho due matrici, della stessa lunghezza - dire a e bQual è il modo pythonic per scorrere due array contemporaneamente?

a = [4,6,2,6,7,3,6,7,2,5]

b = [6,4,6,3,2,7,8,5,3,5]

Normalmente, vorrei farlo in questo modo:

for i in range(len(a)): 
    print a[i] + b[i] 

piuttosto che qualcosa così:

i=0 
for number in a: 
    print number + b[i] 
    i += 1 

perché preferisco essere coerente con i metodi utilizzati.

So di zip, ma non lo uso mai. È per questo che è stato creato zip?

sarebbe

for pair in zip(a,b): 
    print pair[0] + pair[1] 

essere il divinatorio modo per fare questo?

risposta

17

Se le liste a e b sono brevi, usano zip (come indicato @Vincenzo Pii):

for x, y in zip(a, b): 
    print(x + y) 

Se le liste a e b sono lunghi, quindi utilizzare itertools.izip per risparmiare memoria:

import itertools as IT 
for x, y in IT.izip(a, b): 
    print(x + y) 

zip crea un elenco di tuple. Questo può essere gravoso (in termini di memoria) se a e b sono grandi.

itertools.izip restituisce un iteratore. L'iteratore non genera l'elenco completo di tuple; restituisce solo ogni articolo come richiesto dal ciclo for. Così può farti risparmiare un po 'di memoria.

In Python2 la chiamata zip(a,b) su elenchi brevi è più veloce rispetto all'utilizzo di itertools.izip(a,b). Ma in Python3 si noti che zip restituisce un iteratore per impostazione predefinita (ad esempio è equivalente a itertools.izip in Python2).


Altre varianti di interesse:

+1

Per rendere tutto ciò completo, vorrei menzionare anche 'future_builtins'. – georg

+0

Quindi in zip python3 è uguale a itertools.izip? O cambia a seconda delle dimensioni degli elenchi? – will

+2

@will: Sì, 'zip' in Python3 è lo stesso di' itertools.izip' in Python2. (Non cambia il comportamento con la dimensione della lista.) Per ottenere il vecchio comportamento 'zip' in Python3, usa' lista (zip (a, b)) '. – unutbu

7

Una possibile soluzione sta usando zip, come lei ha ricordato voi stessi, ma in modo leggermente diverso da come hai scritto nella domanda:

for x, y in zip(a, b): 
    print x, y 

Si noti che la lunghezza della lista di tuple restituita da zip() sarà pari al minimo tra le lunghezze di a e b. Questo ha effetto quando a e b non sono della stessa lunghezza.

+0

Questo ha un aspetto molto familiare da vari esempi e pagine di documentazione – will

+0

Ciò conferma che è sdolcinato :)! –

4

Invece di utilizzare zip è possibile utilizzare Numpy, soprattutto se la velocità è importante e si dispone di array lunghi. La sua molto più veloce e una volta che si sta utilizzando gli array NumPy non hai bisogno di un ciclo, e può solo scrivere:

print a + b 

Grafico che mostra una media di tempi per sommare diverse liste di lunghezza utilizzando zip, izip, e NumPy: enter image description here

+1

Yah so di numpy. È sicuramente applicabile in questo caso, ma l'esempio che ho dato era solo un artificio. Quello che voglio davvero è quando hai due liste di oggetti e vuoi accedere all'ennesimo oggetto di entrambi. Mi è apparso chiaro quando volevo aggiungere un pattern di 'tratteggio' a' wedges' in un grafico a piombi 'matplotlib'. L'uso finale sarebbe qualcosa come 'per modello, zeppa in zip (modelli, zeppe): wedge.set_hatch (pattern)' – will

0

Offrire questa risposta per completezza dal numpy è stata discussa in un'altra risposta, ed è spesso utile accoppiare i valori insieme con array di livello superiore.

Il accepted answer grandi opere per qualsiasi sequenza/matrice di rango 1. Tuttavia, se la sequenza è di più livelli (quali un numpy matrice di rango 2 o più, ma anche ad esempio in un list di list s, o tuple di tuple s), è necessario iterare attraverso ciascuna classificazione. Di seguito riportiamo un esempio con un 2D numpy matrice:

import numpy as np 
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) 
b = np.array([list('abc'), list('pdq'), list('xyz')]) 
c = np.array([[frobnicate(aval, bval) for aval, bval in zip(arow, brow)] for arow, brow in zip(a, b)]) 

E lo stesso concetto funziona per qualsiasi insieme di due sequenze nidificate dimensionali della stessa forma:

a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 
b = [list('abc'), list('pdq'), list('xyz')] 
c = [[frobnicate(aval, bval) for aval, bval in zip(arow, brow)] for arow, brow in zip(a, b)] 

Se uno o entrambi i nidificata sequenze ha "buchi" in esso, utilizzare itertools.zip_longest per riempire i buchi (le impostazioni predefinite di valore di riempimento per None ma possono essere specificati):

from itertools import zip_longest as zipl 
a = [[], [4, 5, 6], [7, 8, 9]] # empty list in the first row 
b = [list('abc'), list('pdq'), []] # empty list in the last row 
c = [[frobnicate(aval, bval) for aval, bval in zipl(arow, brow)] for arow, brow in zipl(a, b)] 
Problemi correlati