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

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

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

Ян Ворчак
источник
1
Ну разве списки нужны ? Что вы собираетесь делать с результатами?
Карл Кнехтель
14
Примером может служить sklearn, где часто данные должны быть организованы таким образом.
tumultous_rooster 01

Ответы:

101

Если вы архивируете более 2 списков (или даже только 2, если на то пошло), удобочитаемым способом будет:

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

Это использует понимание списка и преобразует каждый элемент в списке (кортежи) в списки.

DK
источник
54

Вы сами почти получили ответ. Не используйте mapвместо zip. Использование map AND zip .

Вы можете использовать карту вместе с zip для элегантного и функционального подхода:

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

zipвозвращает список кортежей. map(list, [...])вызывает listкаждый кортеж в списке. list(map([...])превращает объект карты в читаемый список.

Эльдамир
источник
неудачное решение о возврате операций коллекций python 3 приводит generatorк тому, что здесь стоит двойная стоимость list.
StephenBoesch 01
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
kslnet
источник
2
Это транспонирование того, что пытается сделать OP. Не могли бы вы обновить свой пост, чтобы отразить это? То есть OP преобразует два списка в список или произвольное количество пар. Вы конвертируете произвольное количество пар в пару списков.
Mad Physicist
С какой версией Python это измеряется?
Moberg
Не помню, это было больше двух лет назад, но скорее всего 2,6 или 2,7. Я полагаю, вы можете скопировать код и попробовать его на своей собственной версии / платформе.
kslnet
2
python 2 zipсоздает реальный список. Это замедляет работу. Попробуйте заменить zipна itertools.izipthen.
Жан-Франсуа Фабр
В Python 3.5 zip занимает 3,5 секунды, а получение элементов - 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]]
Broseph
источник
Это кажется мне лучшим ответом, чем остальные, поскольку здесь мы сокращаем один шаг, не создавая zip-архив, а напрямую создавая список. Потрясающе
Акшай Хазари
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()
GM
источник
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]]
axai_m
источник