Como copiar dados de uma matriz numpy para outra

86

Qual é a maneira mais rápida de copiar dados do array b para o array a, sem modificar o endereço do array a. Eu preciso disso porque uma biblioteca externa (PyFFTW) usa um ponteiro para minha matriz que não pode ser alterado.

Por exemplo:

a = numpy.empty(n, dtype=complex)
for i in xrange(a.size):
  a[i] = b[i]

É possível fazer isso sem loop?

Charles Brunet
fonte

Respostas:

86

Acredito

a = numpy.empty_like (b)
a[:] = b

fará uma cópia profunda rapidamente. Como menciona Funsi, as versões recentes do numpy também têm essa copytofunção.

Brian Hawkins
fonte
4
+1. Mas numpy.empty não seria substancialmente mais rápido do que numpy.zeros ?
mg007
9
@ M.ElSaka a = bapenas cria uma nova referência para b. a[:] = bsignifica "definir todos os elementos aiguais aos de b". A diferença é importante porque matrizes numpy são tipos mutáveis.
Brian Hawkins
14
@ mg007 Fiz alguns testes, que mostraram empty()ser cerca de 10% mais rápido do que zeros(). Surpreendentemente, empty_like()é ainda mais rápido. copyto(a,b)é mais rápido do que a sintaxe de array a[:] = b. Consulte gist.github.com/bhawkins/5095558
Brian Hawkins
2
@Brian Hawkins está certo. Para saber quando usar np.copyto(a, b)e a = b.astype(b.dtype)para melhorar a velocidade, veja a resposta abaixo: stackoverflow.com/a/33672015/3703716
mab
1
@michael_n Fiquei surpreso que empty_likeseja muito mais rápido do que empty, especialmente porque zeros_likeé mais lento do que zeros. BTW, acabei de executar novamente meu benchmark (agora atualizado), e a diferença entre copyto(a,b)e a[:] = bparece ter evaporado. gist.github.com/bhawkins/5095558
Brian Hawkins
26

NumPy versão 1.7 tem a numpy.copytofunção que faz o que você está procurando:

numpy.copyto (dst, src)

Copia valores de uma matriz para outra, transmitindo conforme necessário.

Veja: https://docs.scipy.org/doc/numpy/reference/generated/numpy.copyto.html

Funsi
fonte
Isso não funciona para mim. Eu receboAttributeError: 'module' object has no attribute 'copyto'
kalu
19
a = numpy.array(b)

é ainda mais rápido do que as soluções sugeridas até a v1.6 numpy e também faz uma cópia do array. No entanto, não pude testá-lo contra copyto (a, b), pois não tenho a versão mais recente do numpy.

Benor
fonte
Essa é uma ótima maneira de copiar um array, mas cria um novo objeto. O OP precisa saber como atribuir valores rapidamente a um array que já foi criado.
Brian Hawkins
15

Para responder à sua pergunta, eu brinquei com algumas variantes e criei um perfil delas.

Conclusão: para copiar dados de uma matriz numpy para outra, use uma das funções numpy integradas numpy.array(src)ou numpy.copyto(dst, src)sempre que possível.

(Mas sempre escolha o último se dsta memória de já estiver alocada, para reutilizar a memória. Veja o perfil no final do post.)

configuração de perfil

import timeit
import numpy as np
import pandas as pd
from IPython.display import display

def profile_this(methods, setup='', niter=10 ** 4, p_globals=None, **kwargs):
    if p_globals is not None:
        print('globals: {0}, tested {1:.0e} times'.format(p_globals, niter))
    timings = np.array([timeit.timeit(method, setup=setup, number=niter,
                                      globals=p_globals, **kwargs) for 
                        method in methods])
    ranking = np.argsort(timings)
    timings = np.array(timings)[ranking]
    methods = np.array(methods)[ranking]
    speedups = np.amax(timings) / timings

    pd.set_option('html', False)
    data = {'time (s)': timings,
            'speedup': ['{:.2f}x'.format(s) if 1 != s else '' for s in speedups],
            'methods': methods}
    data_frame = pd.DataFrame(data, columns=['time (s)', 'speedup', 'methods'])

    display(data_frame)
    print()

código de perfil

setup = '''import numpy as np; x = np.random.random(n)'''
methods = (
    '''y = np.zeros(n, dtype=x.dtype); y[:] = x''',
    '''y = np.zeros_like(x); y[:] = x''',
    '''y = np.empty(n, dtype=x.dtype); y[:] = x''',
    '''y = np.empty_like(x); y[:] = x''',
    '''y = np.copy(x)''',
    '''y = x.astype(x.dtype)''',
    '''y = 1*x''',
    '''y = np.empty_like(x); np.copyto(y, x)''',
    '''y = np.empty_like(x); np.copyto(y, x, casting='no')''',
    '''y = np.empty(n)\nfor i in range(x.size):\n\ty[i] = x[i]'''
)

for n, it in ((2, 6), (3, 6), (3.8, 6), (4, 6), (5, 5), (6, 4.5)):
    profile_this(methods[:-1:] if n > 2 else methods, setup, 
                 niter=int(10 ** it), p_globals={'n': int(10 ** n)})

resultados para Windows 7 em CPU Intel i7, CPython v3.5.0, numpy v1.10.1.


Além disso, veja os resultados de uma variante do perfil em que a memória do destino já está pré-alocada durante a cópia do valor, uma vez que y = np.empty_like(x)faz parte da configuração:

mab
fonte
Também x.copy()é tão rápido quanto np.array(x)e gosto muito mais da sintaxe: $ python3 -m timeit -s "import numpy as np; x = np.random.random((100, 100))" "x.copy()"- 100000 loops, best of 3: 4.7 usec per loop. Tenho resultados semelhantes para np.array(x). Testado em Linux com um i5-4210U e numpy 1.10.4
Marco Sulla
Sim, Marco, é antes uma questão de gosto pessoal. Mas note que np.copyé mais complacente: np.copy(False), np.copy(None)ainda trabalho, enquanto a = None; a.copy()joga AttributeError: 'NoneType' object has no attribute 'copy'. Além disso, somos mais precisos ao declarar o que queremos que aconteça nesta linha de código usando a função em vez da sintaxe do método.
mab
1
Bem, o fato de np.copy(None)não lançar um erro é realmente impotônico. Mais um motivo para usar a.copy():)
Marco Sulla
1
Acabei de executar esses benchmarks com Python 2.7.12, NumPy 1.11.2 e descobri que y[:] = xagora é ligeiramente mais rápido do que copyto(y, x). Código e saída em gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins
10

você pode usar facilmente:

b = 1*a

este é o caminho mais rápido, mas também tem alguns problemas. Se você não definir diretamente o dtypede ae também não verificar o dtypede, bpoderá ter problemas. Por exemplo:

a = np.arange(10)        # dtype = int64
b = 1*a                  # dtype = int64

a = np.arange(10.)       # dtype = float64
b = 1*a                  # dtype = float64

a = np.arange(10)        # dtype = int64
b = 1. * a               # dtype = float64

Espero ter conseguido deixar esse ponto claro. Às vezes, você terá uma mudança de tipo de dados com apenas uma pequena operação.

ahelm
fonte
1
Não. Fazer isso cria uma nova matriz. É equivalente a b = a.copy ().
Charles Brunet
desculpe, mas eu não entendo você. O que você quer dizer com criar uma nova matriz? Todos os outros métodos aqui apresentados têm o mesmo comportamento. a = numpy.zeros(len(b))ou a = numpy.empty(n,dtype=complex)também criará uma nova matriz.
ahelm
2
Suponha que você tenha a = numpy.empty (1000). Agora, você precisa preencher um com dados, sem alterar seu endereço na memória. Se você fizer um [0] = 1, você não recria um array, apenas altera o conteúdo do array.
Charles Brunet
1
@CharlesBrunet o array terá que ser criado em algum momento. Este inteligente one-liner faz tudo em uma única operação.
heltonbiker
7

Existem muitas coisas diferentes que você pode fazer:

a=np.copy(b)
a=np.array(b) # Does exactly the same as np.copy
a[:]=b # a needs to be preallocated
a=b[np.arange(b.shape[0])]
a=copy.deepcopy(b)

Coisas que não funcionam

a=b
a=b[:] # This have given my code bugs 
Peter Mølgaard Pallesen
fonte
1

Por que não usar

a = 0 + b

Acho que é semelhante à multiplicação anterior, mas pode ser mais simples.

JQK
fonte