Zip із виведенням списку замість кортежу


94

Який найшвидший та найелегантніший спосіб складання списків із двох списків?

У мене є

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

І я хотів би мати

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

Я думав про те, щоб використовувати map замість zip, але я не знаю, чи існує якийсь стандартний метод бібліотеки, який можна поставити як перший аргумент.

Я можу визначити власну функцію для цього і використовувати карту, моє питання полягає в тому, чи вже щось там реалізовано. Ні - це також відповідь.


1
Ну, вам справді потрібні списки? Що ви збираєтесь робити з результатами?
Karl Knechtel

14
Прикладом може бути sklearn, де багато разів дані повинні бути організовані таким чином.
tumultous_rooster

Відповіді:


101

Якщо ви стискаєте більше двох списків (або навіть лише 2, з цього приводу), доступним для читання способом буде:

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

Це використовує розуміння списків і перетворює кожен елемент у списку (кортежі) у списки.


54

Ви майже мали відповідь самі. Не використовуйте mapзамість zip. Використання map AND zip .

Ви можете використовувати карту разом із блискавкою для елегантного, функціонального підходу:

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

zipповертає список кортежів. map(list, [...])дзвінки listна кожен кортеж у списку. list(map([...])перетворює об'єкт карти в читабельний список.


невдале рішення зробити операції колекції python 3 повертає a generatorнакладає вартість дубля listтут.
StephenBoesch

15

Мені подобається елегантність функції zip, але використання функції itemgetter () в модулі оператора виявляється набагато швидшим. Я написав простий скрипт, щоб перевірити це:

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

Я очікував, що zip буде швидшим, але метод itemgetter виграє довгим ударом:

Using zip
6.1550450325
Using itemgetter
0.768098831177

2
Це транспонування того, що намагається зробити OP. Не могли б ви оновити свою публікацію, щоб відображати це? Тобто, OP перетворює два списки в список або довільну кількість пар. Ви перетворюєте довільну кількість пар у пару списків.
Божевільний фізик,

З якою версією python це вимірюється?
Moberg

Не пам’ятаю, це було більше двох років тому, але, швидше за все, 2,6 чи 2,7. Думаю, ви можете скопіювати код і спробувати на своїй власній версії / платформі.
kslnet

2
python 2 zipстворює реальний список. Це сповільнює ситуацію. Спробуйте замінити zipна itertools.izipтоді.
Жан-Франсуа Фабр

У Python 3.5 zip займає 3,5 секунди, а itemgetter - 0,10 секунди. Для тих, хто любить розуміння списків, list1 = [x[0] for x in origlist]працює так само добре list1 = map(itemgetter(0), origlist).
Elias Strehle

3

Я взагалі не люблю використовувати лямбду, але ...

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

Якщо вам потрібна додаткова швидкість, карта трохи швидша:

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

Однак карта вважається непітонічною і її слід використовувати лише для налаштування продуктивності.


4
Що lambdaтут додається? Можна просто написати вираз, замість того, щоб викликати функцію (це насправді не складно), і навіть якщо хтось хоче функцію для неї, її можна безболісно визначити у два рядки (один, якщо ваш ключ повернення зламаний або ви божевільні) . mapз іншого боку, цілком нормально, якщо перший аргумент буде простою функцією (на відміну від a lambda).

1
Ну, він попросив функції. Але я згоден - мабуть, краще просто заплатити додаткову лінію. Що стосується карти, я вважаю, що розуміння списків майже завжди чіткіше.
Ceasar Bautista

1
Я б рекомендував mapбільше lambda. отже map(list, zip(a,b)). Розуміння списку може бути трохи зрозумілішим, але карта повинна бути швидшою (неперевіреною)
inspectorG4dget

Я маю на увазі, знову ж таки, якщо ОП потребує швидкості, карта - це шлях. Але загалом, і особливо в Python, наголошуйте на читабельності над швидкістю (інакше ви зануритесь у передчасну оптимізацію).
Ceasar Bautista

3

Як щодо цього?

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

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

Або ще краще:

>>> def zip_(*args): return map(list_, *args)
>>> zip_(range(5), range(9,4,-1))
[[0, 9], [1, 8], [2, 7], [3, 6], [4, 5]]

Мені це здається кращою відповіддю, ніж інші, оскільки тут ми зменшуємо один крок, не роблячи архівування та безпосередньо створюючи список. Чудово
Акшай Хазарі

2

Використання numpy

Визначення елегантності може бути досить сумнівним, але якщо ви працюєте зі numpyстворенням масиву та його перетворенням у список (якщо потрібно ...), це може бути дуже практичним, хоча і не настільки ефективним у порівнянні з використанням mapфункції або розуміння списку.

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

В іншому випадку пропускаючи zipфункцію, яку ви можете використовувати безпосередньо np.dstack:

np.dstack((a,b))[0].tolist()

1

Я думаю, розуміння списку було б дуже простим рішенням.

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]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.