Python: maneira mais rápida de criar uma lista de n listas


97

Então, eu queria saber a melhor forma de criar uma lista de listas em branco:

[[],[],[]...]

Por causa de como o Python funciona com listas na memória, isso não funciona:

[[]]*n

Isso cria, [[],[],...]mas cada elemento é a mesma lista:

d = [[]]*n
d[0].append(1)
#[[1],[1],...]

Algo como a compreensão de uma lista funciona:

d = [[] for x in xrange(0,n)]

Mas isso usa o Python VM para loop. Existe alguma maneira de usar um loop implícito (aproveitando que ele está escrito em C)?

d = []
map(lambda n: d.append([]),xrange(0,10))

Na verdade, isso é mais lento. :(


1
Eu ficaria surpreso se houvesse algo substancialmente mais rápido do que d = [[] for x in xrange(0,n)]. Você precisa fazer um loop explicitamente no Python ou chamar uma função / lambda do Python repetidamente (que deve ser mais lenta). Mas ainda espero que alguém poste algo que mostre que estou errado :).
— MAK

1
Quando você mediu isso com timeit, o que você aprendeu?
— S.Lott,

Acabei de confirmar que map(lambda x: [], xrange(n))é mais lento do que a compreensão de uma lista.
— Andrew Clark,

Respostas:


105

Provavelmente a única maneira que é ligeiramente mais rápida do que

d = [[] for x in xrange(n)]

é

from itertools import repeat
d = [[] for i in repeat(None, n)]

Não é necessário criar um novo intobjeto a cada iteração e é cerca de 15% mais rápido na minha máquina.

Editar : usando NumPy, você pode evitar o loop Python usando

d = numpy.empty((n, 0)).tolist()

mas isso é na verdade 2,5 vezes mais lento do que a compreensão da lista.


Que tal map(lambda x:[], repeat(None,n))?
— PaulMcG

3
@Paul: Isso seria muito mais lento novamente devido à sobrecarga da chamada de função da expressão lambda.
— Sven Marnach

Isso não deveria ser atualizado agora que o intervalo é diferente no Python 3?
— beruic

@beruic, estou apenas citando o código da pergunta no primeiro bloco de código, então realmente não faz sentido mudar isso.
— Sven Marnach

12

As compreensões de lista, na verdade, são implementadas de forma mais eficiente do que o loop explícito (consulte a dissaída para funções de exemplo ) e a mapmaneira deve invocar um objeto opaco que pode ser chamado em cada iteração, o que incorre em um overhead considerável.

Independentemente disso, [[] for _dummy in xrange(n)]é a maneira certa de fazer isso e nenhuma das pequenas (se existentes) diferenças de velocidade entre as várias outras maneiras deveriam importar. A menos, claro, que você gaste a maior parte do seu tempo fazendo isso - mas, nesse caso, você deve trabalhar em seus algoritmos. Com que frequência você cria essas listas?


5
Por favor, não _como um nome de variável! Caso contrário, boa resposta :)
— Sven Marnach,

11
@Sven: Por que não? É comumente usado para variáveis ​​não utilizadas (se fosse chamado i, eu estaria procurando onde é usado). A única armadilha seria que ele obscurece a _realização do último resultado no REPL ... e isso é apenas o caso em 2.x onde as compreensões de lista vazam.

Não com muita frequência, por isso fui em frente e usei uma compreensão de lista. Achei que seria interessante ver o que as pessoas tinham a dizer. Eu vi o Dropbox falar na PyCon sobre o uso de itertools.imap em vez de um loop for para atualizar um hash md5 uma quantidade absurdamente grande de vezes, e estive um pouco obcecado por loops C desde então.
— munchybunch de

12
O motivo mais importante para não usá-lo é que ele tende a confundir as pessoas, fazendo-as pensar que é algum tipo de sintaxe especial. E além do conflito com _no interpretador interativo, ele também conflita com o alias gettext comum. Se você quiser deixar claro que a variável é uma variável fictícia, chame-a dummy, não _.
— Sven Marnach,

12

Aqui estão dois métodos, um doce e simples (e conceitual), o outro mais formal e pode ser estendido em uma variedade de situações, após a leitura de um conjunto de dados.

Método 1: conceitual

X2=[]
X1=[1,2,3]
X2.append(X1)
X3=[4,5,6]
X2.append(X3)
X2 thus has [[1,2,3],[4,5,6]] ie a list of lists. 

Método 2: formal e extensível

Outra maneira elegante de armazenar uma lista como uma lista de listas de números diferentes - que ela lê de um arquivo. (O arquivo aqui tem o trem do conjunto de dados) Trem é um conjunto de dados com, digamos, 50 linhas e 20 colunas. ie. Train [0] me dá a 1ª linha de um arquivo csv, train [1] me dá a 2ª linha e assim por diante. Estou interessado em separar o conjunto de dados com 50 linhas como uma lista, exceto a coluna 0, que é minha variável explicada aqui, portanto, deve ser removido do conjunto de dados do trem original e, em seguida, aumentar lista após lista - ou seja, uma lista de uma lista . Aqui está o código que faz isso.

Observe que estou lendo "1" no loop interno, pois estou interessado apenas em variáveis ​​explicativas. E eu reinicializo X1 = [] no outro loop, senão o X2.append ([0: (len (train [0]) - 1)]) irá reescrever X1 repetidamente - além de ser mais eficiente em termos de memória.

X2=[]
for j in range(0,len(train)):
    X1=[]
    for k in range(1,len(train[0])):
        txt2=train[j][k]
        X1.append(txt2)
    X2.append(X1[0:(len(train[0])-1)])

4

Para criar uma lista e uma lista de listas, use a sintaxe abaixo

     x = [[] for i in range(10)]

isto irá criar uma lista 1-d e para inicializá-la coloque o número em [[número] e definir o comprimento da lista colocar o comprimento no intervalo (comprimento)

  • Para criar uma lista de listas, use a sintaxe abaixo.
    x = [[[0] for i in range(3)] for i in range(10)]

isso inicializará a lista de listas com dimensão 10 * 3 e com valor 0

  • Para acessar / manipular o elemento
    x[1][5]=value

2

Então, fiz algumas comparações de velocidade para obter o caminho mais rápido. As compreensões de listas são realmente muito rápidas. A única maneira de chegar perto é evitar que o bytecode seja executado durante a construção da lista. Minha primeira tentativa foi o seguinte método, que parece ser mais rápido em princípio:

l = [[]]
for _ in range(n): l.extend(map(list,l))

(produz uma lista de comprimento 2 ** n, é claro) Esta construção é duas vezes mais lenta do que a compreensão da lista, de acordo com o tempo, para listas curtas e longas (um milhão).

Minha segunda tentativa foi usar o starmap para chamar o construtor de lista para mim. Há uma construção, que parece executar o construtor de lista em alta velocidade, mas ainda é mais lento, mas apenas por uma pequena quantidade:

from itertools import starmap
l = list(starmap(list,[()]*(1<<n)))

Curiosamente, o tempo de execução sugere que é a chamada de lista final que torna a solução do mapa estelar lenta, já que seu tempo de execução é quase exatamente igual à velocidade de:

l = list([] for _ in range(1<<n))

Minha terceira tentativa veio quando percebi que list (()) também produz uma lista, então tentei o aparentemente simples:

l = list(map(list, [()]*(1<<n)))

mas isso foi mais lento do que a chamada do mapa estelar.

Conclusão: para os maníacos por velocidade: Use a compreensão de lista. Apenas chame funções, se for necessário. Use builtins.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.