examples - python array operations
Diferencia entre a-= b y a=a-b en Python (3)
Internamente, la diferencia es que esto:
a[1:] -= a[:-1]
es equivalente a esto:
a[1:] = a[1:].__isub__(a[:-1])
a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None)))
mientras esto:
b[1:] = b[1:] - b[:-1]
mapas para esto:
b[1:] = b[1:].__sub__(b[:-1])
b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None)))
En algunos casos,
__sub__()
y
__isub__()
funcionan de manera similar.
Pero los objetos mutables deben mutar y devolverse cuando usan
__isub__()
, mientras que deben devolver un nuevo objeto con
__sub__()
.
La aplicación de operaciones de corte en objetos numpy crea vistas en ellos, por lo que usarlos accede directamente a la memoria del objeto "original".
Recientemente he aplicado
this
solución para promediar cada N filas de matriz.
Aunque la solución funciona en general, tuve problemas cuando se aplicó a una matriz de 7x1.
He notado que el problema es cuando se usa el operador
-=
.
Para hacer un pequeño ejemplo:
import numpy as np
a = np.array([1,2,3])
b = np.copy(a)
a[1:] -= a[:-1]
b[1:] = b[1:] - b[:-1]
print a
print b
que salidas:
[1 1 2]
[1 1 1]
Entonces, en el caso de una matriz
a -= b
produce un resultado diferente que
a = a - b
.
Pensé hasta ahora que estas dos formas son exactamente las mismas.
¿Cuál es la diferencia?
¿Cómo es que el método que estoy mencionando para sumar cada N filas en una matriz está funcionando, por ejemplo, para una matriz 7x4 pero no para una matriz 7x1?
Los documentos dicen:
La idea detrás de la asignación aumentada en Python es que no es solo una manera más fácil de escribir la práctica común de almacenar el resultado de una operación binaria en su operando de la izquierda, sino también una forma para que el operando de la izquierda en cuestión sepa que debe funcionar `en sí mismo '', en lugar de crear una copia modificada de sí mismo.
Como regla general, la sustracción aumentada (
x-=y
) es
x.__isub__(y)
, para la operación
IN-
place
SI es
posible, cuando la sustracción normal (
x = xy
) es
x=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.
Nota: el uso de operaciones in situ en matrices NumPy que comparten memoria ya no es un problema en la versión 1.13.0 en adelante (ver detalles here ). Las dos operaciones producirán el mismo resultado. Esta respuesta solo se aplica a versiones anteriores de NumPy.
¡Mutar los arreglos mientras se usan en los cálculos puede conducir 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 en el tercer elemento de
a
.
Esto es lo que sucede con
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]
ya[:-1]
es[1, 2]
. -
La resta en el lugar
-=
comienza. El primer elemento dea[:-1]
, 1, se resta del primer elemento dea[1:]
. Esto ha modificadoa
para que sea[1, 1, 3]
. Ahora tenemos quea[1:]
es una vista de los datos[1, 3]
, ya[:-1]
es una vista de los datos[1, 1]
(el segundo elemento de la matriza
ha sido cambiado) . -
a[:-1]
ahora es[1, 1]
y NumPy ahora debe restar su segundo elemento que es 1 (¡ya no es 2!) del segundo elemento dea[1:]
. Esto hace quea[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 primero una
nueva
matriz y luego asigna los valores de esta matriz a
b[1:]
.
No modifica
b
sí durante la resta, por lo que las vistas
b[1:]
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 el uso del parámetro
out
en funciones universales (como
np.subtract
y
np.multiply
) para volver a escribir en una de las matrices.