Nota: el uso de operaciones in situ en matrices NumPy que comparten memoria ya no es un problema a partir de la versión 1.13.0 (consulte los detalles aquí ). Las dos operaciones producirán el mismo resultado. Esta respuesta solo se aplica a versiones anteriores de NumPy.
¡La mutación de matrices mientras se utilizan en cálculos puede dar lugar a resultados inesperados!
En el ejemplo de la pregunta, la resta con -=
modifica el segundo elemento de a
y luego usa inmediatamente ese segundo elemento modificado en la operación sobre el tercer elemento de a
.
Esto es lo que sucede con el a[1:] -= a[:-1]
paso a paso:
a
es la matriz con los datos [1, 2, 3]
.
Tenemos dos puntos de vista sobre estos datos: a[1:]
es [2, 3]
y a[:-1]
es [1, 2]
.
-=
Comienza la resta in situ . El primer elemento de a[:-1]
, 1, se resta del primer elemento de a[1:]
. Esto ha cambiado a
para ser [1, 1, 3]
. Ahora tenemos que a[1:]
es una vista de los datos [1, 3]
y a[:-1]
es una vista de los datos [1, 1]
(el segundo elemento de la matriz a
ha sido cambiado).
a[:-1]
es ahora [1, 1]
y NumPy ahora debe restar su segundo elemento, que es 1 (¡ya no 2!) del segundo elemento de a[1:]
. Esto hace a[1:]
una vista de los valores [1, 2]
.
a
ahora es una matriz con los valores [1, 1, 2]
.
b[1:] = b[1:] - b[:-1]
no tiene este problema porque b[1:] - b[:-1]
crea una nueva matriz primero y luego asigna los valores de esta matriz a b[1:]
. No se modifica b
durante la resta, por lo que las vistas b[1:]
y b[:-1]
no cambian.
El consejo general es evitar modificar una vista in situ con otra si se superponen. Esto incluye los operadores -=
, *=
, etc. y usando el out
parámetro en funciones universales (como np.subtract
y np.multiply
) para escribir de nuevo a uno de los arrays.
Internamente, la diferencia es que esto:
es equivalente a esto:
mientras esto:
mapas a esto:
En algunos casos,
__sub__()
y__isub__()
funcionan de manera similar. Pero los objetos mutables deben mutar y volver a sí mismos cuando se usan__isub__()
, mientras que deben devolver un nuevo objeto con__sub__()
.La aplicación de operaciones de corte en objetos numpy crea vistas sobre ellos, por lo que su uso accede directamente a la memoria del objeto "original".
fuente
Los doctores dicen:
Como regla general, la sustracción aumentada (
x-=y
) esx.__isub__(y)
, para la operación IN- place SI es posible, cuando la sustracción normal (x = x-y
) esx=x.__sub__(y)
. En objetos no mutables como enteros es equivalente. Pero para los mutables como matrices o listas, como en su ejemplo, pueden ser cosas muy diferentes.fuente