Inicialização do array NumPy (preenchimento com valores idênticos)

237

Eu preciso criar uma matriz NumPy de comprimento n , cada elemento do qual év .

Existe algo melhor do que:

a = empty(n)
for i in range(n):
    a[i] = v

Eu sei zerose onesfuncionaria para v = 0, 1. Eu poderia usar v * ones(n), mas não funcionará quando vfor Nonee também seria muito mais lento.

max
fonte
1
No meu computador, para o caso 0, o uso a = np.zeros(n)no loop é mais rápido que a.fill(0). Isso é contrário ao que eu esperava, pois pensei a=np.zeros(n)que precisaria alocar e inicializar nova memória. Se alguém puder explicar isso, eu agradeceria.
user3731622
Você não pode colocar None em uma matriz numpy, pois as células são criadas com um tipo de dados específico, enquanto None possui seu próprio tipo e é de fato um ponteiro.
Camion
@Camion Sim, eu sei agora :) É claro que v * ones(n)ainda é horrível, pois usa a multiplicação cara. Substitua *por +, porém, e v + zeros(n)se mostra surpreendentemente bom em alguns casos ( stackoverflow.com/questions/5891410/… ).
Max
max, em vez de criar uma matriz com zeros antes de adicionar v, é ainda mais rápido criá-la vazia var = np.empty(n)e preenchê-la com 'var [:] = v'. (aliás, np.full()é tão rápido como este)
Camion

Respostas:

308

Introduzido o NumPy 1.8 np.full(), que é um método mais direto do que o empty()seguido fill()para criar uma matriz preenchida com um determinado valor:

>>> np.full((3, 5), 7)
array([[ 7.,  7.,  7.,  7.,  7.],
       [ 7.,  7.,  7.,  7.,  7.],
       [ 7.,  7.,  7.,  7.,  7.]])

>>> np.full((3, 5), 7, dtype=int)
array([[7, 7, 7, 7, 7],
       [7, 7, 7, 7, 7],
       [7, 7, 7, 7, 7]])

Essa é, sem dúvida, a maneira de criar uma matriz preenchida com certos valores, porque descreve explicitamente o que está sendo alcançado (e, em princípio, pode ser muito eficiente, pois executa uma tarefa muito específica).

Eric O Lebigot
fonte
1
Esse método full () está funcionando bem para mim, mas não consigo encontrar um pouco de documentação para ele. Alguém pode me apontar para o lugar certo?
James Adams
1
Você pode pelo menos fazer help(numpy.full)em um shell Python. Também estou surpreso que não esteja na documentação da web.
Eric O Lebigot
No meu sistema (Python 2.7, Numpy 1.8), o np.full () é realmente um pouco mais lento que o np.empty () seguido por np.fill ().
21914 John Zwinck
1
Para 10.000 elementos, observo a mesma coisa (exceto que np.fill()não existe e deveria existir arr.fill()), com uma diferença de cerca de 10%. Se a diferença fosse maior, eu levantaria um problema no rastreador de erros do NumPy. :) Prefiro um código mais explícito e claro, por uma diferença tão pequena no tempo de execução, por isso continuo np.full()o tempo todo.
Eric O Lebigot
No meu np.full máquina () é a mesma velocidade como np.array.fill ()
Fnord
92

Atualizado para o Numpy 1.7.0: (dica de chapéu para @Rolf Bartstra.)

a=np.empty(n); a.fill(5) é o mais rápido

Em ordem decrescente de velocidade:

%timeit a=np.empty(1e4); a.fill(5)
100000 loops, best of 3: 5.85 us per loop

%timeit a=np.empty(1e4); a[:]=5 
100000 loops, best of 3: 7.15 us per loop

%timeit a=np.ones(1e4)*5
10000 loops, best of 3: 22.9 us per loop

%timeit a=np.repeat(5,(1e4))
10000 loops, best of 3: 81.7 us per loop

%timeit a=np.tile(5,[1e4])
10000 loops, best of 3: 82.9 us per loop
Yariv
fonte
13
Adicionar um tempo para o mais recente e direto np.full()seria útil. Na minha máquina, com o NumPy 1.8.1, é cerca de 15% mais lento que a fill()versão menos direta (o que é inesperado, pois full()tem o potencial de ser um pouco mais rápido).
Eric O Lebigot
@ DavidSanders: Não tenho certeza se estou te seguindo: fill()é a solução mais rápida. A solução de multiplicação é muito mais lenta.
Eric O Lebigot
2
Nota: se a velocidade é realmente uma preocupação, usar um tamanho em 10000vez de 1e4faz uma diferença notável, por algum motivo ( full()é quase 50% mais lento com 1e4).
Eric O Lebigot
Apenas adicionando meus resultados com full(), ele é executado consideravelmente mais lento quando o tipo de dados não é explicitamente um float. Caso contrário, é comparável (mas um pouco mais lento) com os melhores métodos aqui.
user2699
@ user2699 eu não estou observando isso, com 100.000 elementos: full(100000, 5), full(100000, 5, dtype=float), full(100000, 5, dtype=int)e a =np.empty(100000); a.fill(5)todos têm aproximadamente o mesmo tempo na minha máquina (sem cache: %timeit -r1 -n1 …) (NumPy 1.11.2).
Eric O Lebigot
65

Eu acredito que fillé a maneira mais rápida de fazer isso.

a = np.empty(10)
a.fill(7)

Você também deve sempre evitar iterar como está fazendo no seu exemplo. Um simples a[:] = vrealizará o que sua iteração faz usando difusão numpy .

Paulo
fonte
1
Obrigado. Ao olhar fill, vi que repeatatende às minhas necessidades ainda melhor.
Max
Você se importa de atualizar sua resposta para dizer que sua recomendação a[:]=vé realmente mais rápida do que a fill?
max
@max É mais rápido? A transmissão é uma maneira mais geral de preencher uma matriz e eu acho que é mais lenta ou igual ao caso de uso muito restrito de fill.
Paul
16

Aparentemente, não apenas as velocidades absolutas, mas também a ordem de velocidade (conforme relatado pelo usuário 1579844) dependem da máquina; aqui está o que eu encontrei:

a=np.empty(1e4); a.fill(5) é mais rápido;

Em ordem decrescente de velocidade:

timeit a=np.empty(1e4); a.fill(5) 
# 100000 loops, best of 3: 10.2 us per loop
timeit a=np.empty(1e4); a[:]=5
# 100000 loops, best of 3: 16.9 us per loop
timeit a=np.ones(1e4)*5
# 100000 loops, best of 3: 32.2 us per loop
timeit a=np.tile(5,[1e4])
# 10000 loops, best of 3: 90.9 us per loop
timeit a=np.repeat(5,(1e4))
# 10000 loops, best of 3: 98.3 us per loop
timeit a=np.array([5]*int(1e4))
# 1000 loops, best of 3: 1.69 ms per loop (slowest BY FAR!)

Portanto, tente descobrir e use o que é mais rápido em sua plataforma.

Rolf Bartstra
fonte
14

eu tinha

numpy.array(n * [value])

em mente, mas aparentemente isso é mais lento que todas as outras sugestões de tamanho suficiente n .

Aqui está uma comparação completa com o perfplot (um projeto meu para animais de estimação).

insira a descrição da imagem aqui

As duas emptyalternativas ainda são as mais rápidas (com o NumPy 1.12.1). fullalcança grandes matrizes.


Código para gerar o gráfico:

import numpy as np
import perfplot


def empty_fill(n):
    a = np.empty(n)
    a.fill(3.14)
    return a


def empty_colon(n):
    a = np.empty(n)
    a[:] = 3.14
    return a


def ones_times(n):
    return 3.14 * np.ones(n)


def repeat(n):
    return np.repeat(3.14, (n))


def tile(n):
    return np.repeat(3.14, [n])


def full(n):
    return np.full((n), 3.14)


def list_to_array(n):
    return np.array(n * [3.14])


perfplot.show(
    setup=lambda n: n,
    kernels=[empty_fill, empty_colon, ones_times, repeat, tile, full, list_to_array],
    n_range=[2 ** k for k in range(27)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)
Nico Schlömer
fonte
7

Você pode usar numpy.tile, por exemplo:

v = 7
rows = 3
cols = 5
a = numpy.tile(v, (rows,cols))
a
Out[1]: 
array([[7, 7, 7, 7, 7],
       [7, 7, 7, 7, 7],
       [7, 7, 7, 7, 7]])

Embora tilese pretenda 'agrupar' uma matriz (em vez de uma escalar, como neste caso), ela fará o trabalho, criando matrizes pré-preenchidas de qualquer tamanho e dimensão.

Rolf Bartstra
fonte
5

sem entorpecente

>>>[2]*3
[2, 2, 2]
tnusraddinov
fonte
A sugestão [v] * nseria mais diretamente relevante para a questão do OP.
aceso
Essa resposta já mencionou essa abordagem.
CommonSense