Zip com saída de lista em vez de tupla

95

Qual é a maneira mais rápida e elegante de fazer listas de duas listas?

eu tenho

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

E eu gostaria de ter

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

Eu estava pensando em usar map em vez de zip, mas não sei se existe algum método de biblioteca padrão para colocar como primeiro argumento.

Eu posso definir minha própria função para isso e usar map, minha dúvida é se já existe algo implementado. Não também é uma resposta.

Jan Vorcak
fonte
1
Bem, você realmente precisa de listas? O que você vai fazer com os resultados?
Karl Knechtel
14
Um exemplo seria sklearn, onde muitas vezes os dados devem ser organizados dessa maneira.
tumultous_rooster

Respostas:

103

Se você estiver compactando mais de 2 listas (ou mesmo apenas 2, nesse caso), uma forma legível seria:

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

Isso usa compreensões de lista e converte cada elemento da lista (tuplas) em listas.

DK
fonte
55

Você quase teve a resposta sozinho. Não use em mapvez de zip. Use map AND zip .

Você pode usar o mapa junto com o zip para uma abordagem elegante e funcional:

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

zipretorna uma lista de tuplas. map(list, [...])chama listcada tupla da lista. list(map([...])transforma o objeto do mapa em uma lista legível.

Eldamir
fonte
a infeliz decisão de fazer com que as operações de coleções do python 3 retornem a generatorimpõe o custo do dobro listaqui.
StephenBoesch
15

Adoro a elegância da função zip, mas usar a função itemgetter () no módulo do operador parece ser muito mais rápido. Escrevi um script simples para testar isso:

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

Eu esperava que o zip fosse mais rápido, mas o método itemgetter vence por muito tempo:

Using zip
6.1550450325
Using itemgetter
0.768098831177
kslnet
fonte
2
Esta é uma transposição do que o OP está tentando fazer. Você poderia atualizar sua postagem para refletir isso? Ou seja, OP está convertendo duas listas em uma lista ou número arbitrário de pares. Você está convertendo um número arbitrário de pares em um par de listas.
Mad Physicist
Com qual versão do Python isso é medido?
Moberg
Não me lembro, foi há mais de dois anos, mas provavelmente 2.6 ou 2.7. Imagino que você possa copiar o código e testá-lo em sua própria versão / plataforma.
kslnet
2
python 2 zipcria uma lista real. Isso retarda as coisas. Tente substituir zippor itertools.izipentão.
Jean-François Fabre
Em Python 3.5, zip leva 3,5 segundos e itemgetter leva 0,10 segundos. Para quem gosta de compreensão de listas, list1 = [x[0] for x in origlist]funciona tão bem quanto list1 = map(itemgetter(0), origlist).
Elias Strehle
3

Eu geralmente não gosto de usar lambda, mas ...

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

Se você precisar de velocidade extra, o mapa é um pouco mais rápido:

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

No entanto, o mapa é considerado impotônico e deve ser usado apenas para ajuste de desempenho.

Ceasar Bautista
fonte
4
O que lambdaadiciona aqui? Pode-se apenas escrever a expressão em vez de chamar uma função (não é realmente complicado), e mesmo se alguém quiser uma função para ela, ela pode ser definida sem dor em duas linhas (uma se sua tecla de retorno estiver quebrada ou você for louco) . mappor outro lado, está perfeitamente bem se o primeiro argumento for uma função simples (em oposição a a lambda).
1
Bem, ele pediu uma função. Mas eu concordo - provavelmente melhor apenas pagar a linha extra. Quanto ao mapa, acredito que as compreensões de listas são quase sempre mais claras.
Ceasar Bautista
1
Eu recomendaria mapmais lambda. então map(list, zip(a,b)). As compreensões da lista podem ser um pouco mais claras, mas o mapa deve ser mais rápido (não testado)
inspectorG4dget
Quero dizer, novamente, se o OP precisa de velocidade, o mapa é o caminho a percorrer. Mas em geral, e em Python especialmente, enfatize a legibilidade em vez da velocidade (caso contrário, você mergulhará na otimização prematura).
Ceasar Bautista
3

Que tal agora?

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

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

Ou melhor ainda:

>>> 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
fonte
Isso me parece uma resposta melhor do que o resto, pois aqui estamos reduzindo uma etapa ao não zipar e criar diretamente uma lista. Incrível
Akshay Hazari
2

Usando numpy

A definição de elegância pode ser bastante questionável, mas se você estiver trabalhando com numpya criação de um array e sua conversão para lista (se necessário ...) pode ser muito prática, embora não seja tão eficiente em comparação com o uso da mapfunção ou da compreensão de lista.

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

Caso contrário, pule a zipfunção que você pode usar diretamente np.dstack:

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

A compreensão de listas seria uma solução muito simples, eu acho.

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
fonte