Cómo copiar datos de una matriz numpy a otra

86

¿Cuál es la forma más rápida de copiar datos de la matriz b a la matriz a, sin modificar la dirección de la matriz a. Necesito esto porque una biblioteca externa (PyFFTW) usa un puntero a mi matriz que no puede cambiar.

Por ejemplo:

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

¿Es posible hacerlo sin bucle?

Charles Brunet
fuente

Respuestas:

86

Yo creo

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

hará una copia profunda rápidamente. Como menciona Funsi, las versiones recientes de numpy también tienen la copytofunción.

Brian Hawkins
fuente
4
+1. ¿Pero numpy.empty no sería sustancialmente más rápido que numpy.zeros ?
mg007
9
@ M.ElSaka a = bsimplemente crea una nueva referencia a b. a[:] = bsignifica "establecer todos los elementos de aiguales a los de b". La diferencia es importante porque las matrices numpy son tipos mutables.
Brian Hawkins
14
@ mg007 Ejecuté algunas pruebas, que mostraron que empty()es aproximadamente un 10% más rápido que zeros(). Sorprendentemente, empty_like()es incluso más rápido. copyto(a,b)es más rápido que la sintaxis de matriz a[:] = b. Ver gist.github.com/bhawkins/5095558
Brian Hawkins
2
@Brian Hawkins tiene razón. Para saber cuándo usar np.copyto(a, b)y cuándo a = b.astype(b.dtype)mejorar la velocidad, consulte la respuesta a continuación: stackoverflow.com/a/33672015/3703716
mab
1
@michael_n Me sorprendió que empty_likesea ​​mucho más rápido que empty, especialmente porque zeros_likees más lento que zeros. Por cierto, volví a ejecutar mi punto de referencia (ahora actualizado) y la diferencia entre copyto(a,b)y a[:] = bparece haberse evaporado. gist.github.com/bhawkins/5095558
Brian Hawkins
19
a = numpy.array(b)

es incluso más rápido que las soluciones sugeridas hasta numpy v1.6 y también hace una copia de la matriz. Sin embargo, no pude probarlo contra copyto (a, b), ya que no tengo la versión más reciente de numpy.

Benor
fuente
Esta es una excelente manera de copiar una matriz, pero crea un nuevo objeto. El OP necesita saber cómo asignar rápidamente valores a una matriz que ya ha sido creada.
Brian Hawkins
15

Para responder a su pregunta, jugué con algunas variantes y las perfilé.

Conclusión: para copiar datos de una matriz numérica a otra, utilice una de las funciones numéricas integradas numpy.array(src)o numpy.copyto(dst, src)siempre que sea posible.

(Pero siempre elija el último si dstla memoria ya está asignada, para reutilizar la memoria. Consulte el perfil al final de la publicación).

configuración de perfiles

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 en CPU Intel i7, CPython v3.5.0, numpy v1.10.1.


Además, vea los resultados de una variante del perfil donde la memoria del destino ya está preasignada durante la copia de valores, ya que y = np.empty_like(x)es parte de la configuración:

mab
fuente
Además x.copy()es tan rápido como np.array(x)y me gusta mucho más la sintaxis: $ 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. Tengo resultados similares para np.array(x). Probado en Linux con un i5-4210U y numpy 1.10.4
Marco Sulla
Sí Marco, es más bien una cuestión de gusto personal. Pero tenga en cuenta que np.copyes más permisiva: np.copy(False), np.copy(None)siendo el trabajo, mientras a = None; a.copy()lanza AttributeError: 'NoneType' object has no attribute 'copy'. Además, somos más precisos al declarar lo que queremos que suceda en esta línea de código usando la función en lugar de la sintaxis del método.
mab
1
Bueno, el hecho de np.copy(None)que no arroje un error es realmente atípico. Una razón más para usar a.copy():)
Marco Sulla
1
Acabo de ejecutar estos puntos de referencia con Python 2.7.12, NumPy 1.11.2 y descubrí que y[:] = xahora es ligeramente más rápido que copyto(y, x). Código y salida en gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins
10

puedes usarlo fácilmente:

b = 1*a

esta es la forma más rápida, pero también tiene algunos problemas. Si no define directamente el dtypede ay tampoco marca el dtypede b, puede meterse en problemas. Por ejemplo:

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 poder aclarar el punto. A veces, tendrá un cambio de tipo de datos con solo una pequeña operación.

ahelm
fuente
1
No. Al hacerlo, se crea una nueva matriz. Es equivalente a b = a.copy ().
Charles Brunet
lo siento, pero no te entiendo. ¿A qué te refieres con crear una nueva matriz? Todos los demás métodos que se presentan aquí tienen el mismo comportamiento. a = numpy.zeros(len(b))o a = numpy.empty(n,dtype=complex)también creará una nueva matriz.
ahelm
2
Suponga que tiene a = numpy.empty (1000). Ahora, necesita llenar un con datos, sin cambiar su dirección en la memoria. Si hace un [0] = 1, no vuelve a crear una matriz, solo cambia el contenido de la matriz.
Charles Brunet
1
@CharlesBrunet, la matriz deberá crearse en algún momento. Este inteligente de una sola línea lo hace todo en una sola operación.
heltonbiker
7

Hay muchas cosas diferentes que puede hacer:

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)

Cosas que no funcionan

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

Por qué no usar

a = 0 + b

Creo que es similar a la multiplicación anterior, pero podría ser más simple.

JQK
fuente