Zip z listą wyjściową zamiast krotką

95

Jaki jest najszybszy i najbardziej elegancki sposób tworzenia list z dwóch list?

mam

In [1]: a=[1,2,3,4,5,6]

In [2]: b=[7,8,9,10,11,12]

In [3]: zip(a,b)
Out[3]: [(1, 7), (2, 8), (3, 9), (4, 10), (5, 11), (6, 12)]

I chciałbym mieć

In [3]: some_method(a,b)
Out[3]: [[1, 7], [2, 8], [3, 9], [4, 10], [5, 11], [6, 12]]

Myślałem o użyciu map zamiast zip, ale nie wiem, czy istnieje jakaś standardowa metoda biblioteczna, którą można umieścić jako pierwszy argument.

Mogę do tego zdefiniować własną funkcję i użyć mapy. Moje pytanie brzmi, czy coś jest już zaimplementowane. Nie jest również odpowiedzią.

Jan Vorcak
źródło
1
Cóż, czy naprawdę potrzebujesz list? Co zamierzasz zrobić z wynikami?
Karl Knechtel
14
Przykładem może być sklearn, gdzie wiele razy dane muszą być organizowane w ten sposób.
tumultous_rooster

Odpowiedzi:

103

Jeśli pakujesz więcej niż 2 listy (lub nawet tylko 2), czytelnym sposobem byłoby:

[list(a) for a in zip([1,2,3], [4,5,6], [7,8,9])]

Używa to wyrażeń listowych i konwertuje każdy element na liście (krotki) na listy.

DK
źródło
55

Prawie znałeś odpowiedź. Nie używaj mapzamiast zip. Użyj map AND zip .

Możesz użyć mapy wraz z zipem, aby uzyskać eleganckie i funkcjonalne podejście:

list(map(list, zip(a, b)))

zipzwraca listę krotek. map(list, [...])wywołuje listkażdą krotkę na liście. list(map([...])zamienia obiekt mapy w czytelną listę.

Eldamir
źródło
niefortunna decyzja, aby operacje kolekcji w Pythonie 3 zwracały a, generatornakłada tutaj koszt podwójnego list.
StephenBoesch
15

Uwielbiam elegancję funkcji zip, ale użycie funkcji itemgetter () w module operatora wydaje się być znacznie szybsze. Napisałem prosty skrypt, aby to przetestować:

import time
from operator import itemgetter

list1 = list()
list2 = list()
origlist = list()
for i in range (1,5000000):
        t = (i, 2*i)
        origlist.append(t)

print "Using zip"
starttime = time.time()
list1, list2 = map(list, zip(*origlist))
elapsed = time.time()-starttime
print elapsed

print "Using itemgetter"
starttime = time.time()
list1 = map(itemgetter(0),origlist)
list2 = map(itemgetter(1),origlist)
elapsed = time.time()-starttime
print elapsed

Spodziewałem się, że zip będzie szybszy, ale metoda itemgetter wygrywa z daleka:

Using zip
6.1550450325
Using itemgetter
0.768098831177
kslnet
źródło
2
Jest to transpozycja tego, co próbuje zrobić PO. Czy możesz zaktualizować swój post, aby to odzwierciedlić? To znaczy, OP przekształca dwie listy w listę lub dowolną liczbę par. Konwertujesz dowolną liczbę par na parę list.
Mad Physicist
Z jaką wersją Pythona jest to mierzone?
Moberg
Nie pamiętam, to było ponad dwa lata temu, ale najprawdopodobniej 2,6 lub 2,7. Wyobrażam sobie, że możesz skopiować kod i wypróbować go na własnej wersji / platformie.
kslnet
2
python 2 ziptworzy prawdziwą listę. To spowalnia wszystko. Spróbuj wymienić zipz itertools.izippotem.
Jean-François Fabre
W Pythonie 3.5 zip zajmuje 3,5 sekundy, a itemgetter 0,10 sekundy. Dla tych, którzy lubią rozumieć listy, list1 = [x[0] for x in origlist]działa równie dobrze list1 = map(itemgetter(0), origlist).
Elias Strehle
3

Generalnie nie lubię używać lambdy, ale ...

>>> a = [1, 2, 3, 4, 5]
>>> b = [6, 7, 8, 9, 10]
>>> c = lambda a, b: [list(c) for c in zip(a, b)]
>>> c(a, b)
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

Jeśli potrzebujesz dodatkowej prędkości, mapa jest nieco szybsza:

>>> d = lambda a, b: map(list, zip(a, b))
>>> d(a, b)
[[1, 6], [2, 7], [3, 8], [4, 9], [5, 10]]

Jednak mapa jest uważana za niepytoniczną i powinna być używana tylko do dostrajania wydajności.

Ceasar Bautista
źródło
4
Co lambdatu dodaje? Można po prostu napisać wyrażenie zamiast wywoływać funkcję (to naprawdę nie jest skomplikowane), a nawet jeśli ktoś chce dla niej funkcję, można ją bezboleśnie zdefiniować w dwóch wierszach (jeden, jeśli klucz powrotu jest uszkodzony lub jesteś szalony) . mapz drugiej strony jest w porządku, jeśli pierwszy argument byłby zwykłą funkcją (w przeciwieństwie do a lambda).
1
Cóż, poprosił o funkcję. Ale zgadzam się - prawdopodobnie lepiej po prostu zapłacić dodatkową linię. Jeśli chodzi o mapę, uważam, że rozumienie list jest prawie zawsze jaśniejsze.
Ceasar Bautista
1
Polecam mapponad lambda. tak map(list, zip(a,b)). Listy mogą być trochę jaśniejsze, ale mapa powinna być szybsza (nieprzetestowana)
inspectorG4dget
Chodzi mi o to, że jeśli OP potrzebuje prędkości, mapa jest drogą do zrobienia. Ale ogólnie, aw Pythonie szczególnie, kładź nacisk na czytelność nad szybkość (w przeciwnym razie zanurzysz się w przedwczesną optymalizację).
Ceasar Bautista
3

Co powiesz na to?

>>> def list_(*args): return list(args)

>>> map(list_, range(5), range(9,4,-1))
[[0, 9], [1, 8], [2, 7], [3, 6], [4, 5]]

Albo jeszcze lepiej:

>>> def zip_(*args): return map(list_, *args)
>>> zip_(range(5), range(9,4,-1))
[[0, 9], [1, 8], [2, 7], [3, 6], [4, 5]]
Broseph
źródło
Wydaje mi się, że jest to lepsza odpowiedź niż reszta, ponieważ tutaj zmniejszamy jeden krok, nie wykonując zip i bezpośrednio tworząc listę. Niesamowite
Akshay Hazari
2

Używanie numpy

Definicja elegancji może być dość wątpliwa, ale jeśli pracujesz numpynad tworzeniem tablicy i jej konwersją na listę (w razie potrzeby ...) może być bardzo praktyczna, nawet jeśli nie jest tak wydajna w porównaniu z użyciem mapfunkcji lub rozumienia list.

import numpy as np 
a = b = range(10)
zipped = zip(a,b)
result = np.array(zipped).tolist()
Out: [[0, 0],
 [1, 1],
 [2, 2],
 [3, 3],
 [4, 4],
 [5, 5],
 [6, 6],
 [7, 7],
 [8, 8],
 [9, 9]]

W przeciwnym razie pomijanie zipfunkcji, której możesz użyć bezpośrednio np.dstack:

np.dstack((a,b))[0].tolist()
GM
źródło
1

Myślę, że rozumienie listy byłoby bardzo prostym rozwiązaniem.

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

b=[7,8,9,10,11,12]

x = [[i, j] for i, j in zip(a,b)]

print(x)

output : [[1, 7], [2, 8], [3, 9], [4, 10], [5, 11], [6, 12]]
axai_m
źródło