2016-01-27 35 views
88

Ich habe vor kurzem this Lösung für die Mittelwertbildung alle N Zeilen der Matrix angewendet. Obwohl die Lösung im Allgemeinen funktioniert, hatte ich Probleme bei der Anwendung auf ein 7x1-Array. Ich habe festgestellt, dass das Problem bei der Verwendung des -= Operators auftritt. Um ein kleines Beispiel zu machen:Unterschied zwischen a - = b und a = a - b in Python

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 

die Ausgänge:

[1 1 2] 
[1 1 1] 

So wird im Fall eines Arrays a -= b erzeugt ein anderes Ergebnis als a = a - b. Ich dachte bis jetzt, dass diese beiden Wege genau gleich sind. Was ist der Unterschied?

Wie kommt es, dass die Methode, die ich für das Summieren jeder N Zeilen in einer Matrix erwähne, z. für eine 7x4-Matrix, aber nicht für eine 7x1-Matrix?

Antwort

79

Hinweis: Die Verwendung von In-Place-Operationen auf NumPy-Arrays mit gemeinsamem Speicher ist ab der Version 1.13.0 kein Problem mehr (siehe Details here). Die zwei Operationen führen zu demselben Ergebnis. Diese Antwort gilt nur für frühere Versionen von NumPy.


Mutierende Arrays, während sie in Berechnungen verwendet werden, zu unerwarteten Ergebnissen führen kann!

Im Beispiel in der Frage, Subtraktion mit -= modifiziert das zweite Element a und verwendet dann sofort, dass Sekunden Element im Betrieb auf dem dritten Element des a modifiziert.

Hier ist, was für Schritt mit a[1:] -= a[:-1] Schritt geschieht:

  • a ist das Array mit den Daten [1, 2, 3].

  • Wir haben zwei Ansichten auf diese Daten: a[1:] ist [2, 3] und a[:-1] ist [1, 2].

  • Die In-Place-Subtraktion -= beginnt. Das erste Element von a[:-1], 1, wird von dem ersten Element von a[1:] subtrahiert. Dies hat a zu [1, 1, 3] geändert. Jetzt haben wir, dass a[1:] ist eine Ansicht der Daten [1, 3], und a[:-1] ist eine Ansicht der Daten [1, 1] (das zweite Element des Arrays a wurde geändert).

  • a[:-1] ist jetzt [1, 1] und NumPy muss nun sein zweites Element subtrahiert die 1 (nicht 2 mehr!) Von dem zweiten Elemente von a[1:]. Dies macht a[1:] eine Ansicht der Werte [1, 2].

  • a ist jetzt ein Array mit den Werten [1, 1, 2].

b[1:] = b[1:] - b[:-1] haben dieses Problem nicht, weil b[1:] - b[:-1] eine erste neue Array erstellt und ordnet dann die Werte in diesem Array zu b[1:]. Während der Subtraktion ändert sich b nicht selbst, daher ändern sich die Ansichten b[1:] und b[:-1] nicht.

Der allgemeine Ratschlag ist, zu vermeiden, eine Ansicht inplace mit einer anderen zu ändern, wenn sie sich überlappen. Dazu gehören die Operatoren -=, *= usw. und der Parameter out in universellen Funktionen (wie np.subtract und np.multiply), um auf eines der Arrays zurückzuschreiben.

+4

Ich bevorzuge diese Antwort mehr zu der derzeit akzeptierten. Es verwendet eine sehr klare Sprache, um den Effekt der Modifizierung von mutablen Objekten an Ort und Stelle zu zeigen. Noch wichtiger ist, dass im letzten Absatz direkt auf die Bedeutung der direkten Anpassung für überlappende Ansichten hingewiesen wird. Dies sollte die Lehre sein, die man aus dieser Frage mitnehmen sollte. – Reti43

42

Intern ist der Unterschied, dass diese:

a[1:] -= a[:-1] 

dazu äquivalent ist:

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))) 

während dies:

b[1:] = b[1:] - b[:-1] 

Karten dazu:

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))) 

In einigen Fällen funktionieren __sub__() und __isub__() auf ähnliche Weise. Veränderbare Objekte sollten jedoch mutieren und sich selbst zurückgeben, wenn sie __isub__() verwenden, während sie ein neues Objekt mit __sub__() zurückgeben sollten.

Wenn Sie Slice-Operationen auf numpy Objekte anwenden, werden Ansichten auf ihnen erstellt, so dass sie direkt auf den Speicher des "ursprünglichen" Objekts zugreifen.

11

The docs sagen:

Die Idee hinter Augmented Zuweisung in Python ist, dass es nicht nur ein einfacher Weg ist, um die gängige Praxis der Speicherung des Ergebnisses einer binären Operation in seiner linken Hand zu schreiben Operand, sondern auch ein Weg für den linken Operanden in Frage zu wissen, dass es "auf sich selbst" betreiben sollte, anstatt eine modifizierte Kopie von selbst zu erstellen.

Als Faustregel gilt, Augmented Substraktion (x-=y) ist x.__isub__(y), für IN -ort Betrieb IF möglich, wenn die normale Substraktion (x = x-y) x=x.__sub__(y) ist. Bei nicht veränderbaren Objekten wie ganzen Zahlen ist es gleichwertig. Aber für veränderbare wie Arrays oder Listen, wie in Ihrem Beispiel, können sie sehr unterschiedliche Dinge sein.

Verwandte Themen