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. :(

petisco
fonte
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.

Sven Marnach
fonte
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?


fonte
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)])
ekta
fonte
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
waqas ahmed
fonte
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.

Jurjen Bos
fonte