2014-11-18 11 views
37

Im numpy manual über die reshape() Funktion, heißt esWas ist der Unterschied zwischen aneinandergrenzenden und nicht zusammenhängenden Arrays?

>>> a = np.zeros((10, 2)) 
# A transpose make the array non-contiguous 
>>> b = a.T 
# Taking a view makes it possible to modify the shape without modifying the 
# initial object. 
>>> c = b.view() 
>>> c.shape = (20) 
AttributeError: incompatible shape for a non-contiguous array 

Meine Fragen sind:

  1. Was sind kontinuierlich und nicht aufeinander Arrays? Ähnelt es dem zusammenhängenden Speicherblock in C wie What is a contiguous memory block?
  2. Gibt es einen Leistungsunterschied zwischen diesen beiden? Wann sollten wir das eine oder andere benutzen?
  3. Warum macht die Transponierung das Array nicht zusammenhängend?
  4. Warum gibt c.shape = (20) einen Fehler aus incompatible shape for a non-contiguous array?

Danke für Ihre Antwort!

Antwort

66

Ein zusammenhängendes Array ist nur ein Array, das in einem ununterbrochenen Speicherblock gespeichert ist: Um auf den nächsten Wert im Array zuzugreifen, gehen wir einfach zur nächsten Speicheradresse.

Betrachten Sie das 2D-Array arr = np.arange(12).reshape(3,4). Es sieht wie folgt aus:

enter image description here

In dem Speicher des Computers werden die Werte von arr werden wie folgt gespeichert:

enter image description here

Das bedeutet arr ist ein C zusammenhängenden Array, weil die Zeilen werden als zusammenhängende Speicherblöcke gespeichert. Die nächste Speicheradresse enthält den nächsten Zeilenwert in dieser Zeile. Wenn wir eine Spalte nach unten bewegen wollen, müssen wir nur über drei Blöcke springen (um beispielsweise von 0 auf 4 zu springen, überspringen wir über 1,2 und 3).

Das Transponieren des Arrays mit arr.T bedeutet, dass die C-Kontiguität verloren geht, weil sich benachbarte Zeileneinträge nicht mehr in benachbarten Speicheradressen befinden. Allerdings ist arr.TFortran zusammenhängenden seit den Spalten in zusammenhängende Blöcke von Speicher sind:

enter image description here


Performance-weise, Speicheradressen zugreifen, die nebeneinander sind, ist sehr oft schneller als Zugriff auf Adressen, die "ausgebreiteter" sind (das Abrufen eines Werts aus dem RAM könnte dazu führen, dass eine Anzahl benachbarter Adressen für die CPU abgerufen und zwischengespeichert wird.) Dies bedeutet, dass Operationen über zusammenhängenden Arrays oft schneller sind.

Als Konsequenz von C zusammenhängendem Speicherlayout sind zeilenweise Vorgänge normalerweise schneller als spaltenweise Vorgänge.Zum Beispiel werden Sie in der Regel feststellen, dass

np.sum(arr, axis=1) # sum the rows 

sind etwas schneller als:

np.sum(arr, axis=0) # sum the columns 

ähnlich Operationen auf Spalten für Fortran zusammenhängenden Arrays etwas schneller sein.


Schließlich, warum können wir nicht die Fortran zusammenhängenden Array durch Zuweisung einer neuen Form abflachen?

>>> arr2 = arr.T 
>>> arr2.shape = 12 
AttributeError: incompatible shape for a non-contiguous array 

Damit dies möglich NumPy die Reihen der arr.T zusammen so sagen müsste sein:

enter image description here

(Einstellen der shape Attribut C, um direkt übernimmt - also NumPy versucht, Führen Sie den Vorgang zeilenweise durch.)

Dies ist nicht möglich. Für jede Achse muss NumPy eine Konstante Schrittlänge (die Anzahl der zu verschiebenden Bytes) haben, um zum nächsten Element des Arrays zu gelangen. Wenn Sie auf diese Weise arr.T abflachen, müssen Sie im Speicher vorwärts und rückwärts springen, um fortlaufende Werte des Arrays abzurufen. Wenn wir arr2.reshape(12) stattdessen geschrieben haben, würde NumPy die Werte von arr2 in einen neuen Speicherblock kopieren (da es keine Ansicht auf die ursprünglichen Daten für diese Form zurückgeben kann).

+0

Ich denke, es wäre nützlich mit einer Notiz darüber, welcher Index in beliebiger Reihenfolge nebeneinander steht: letzter Index für 'C' und erster Index für' F'., Oder für ND-Arrays .. die späteren Indizes sind nahe miteinander als die erste für "C" und umgekehrt. – gauteh

5

wird vielleicht dabei helfen, dieses Beispiel mit 12 verschiedenen Array-Werte:

In [207]: x=np.arange(12).reshape(3,4).copy() 

In [208]: x.flags 
Out[208]: 
    C_CONTIGUOUS : True 
    F_CONTIGUOUS : False 
    OWNDATA : True 
    ... 
In [209]: x.T.flags 
Out[209]: 
    C_CONTIGUOUS : False 
    F_CONTIGUOUS : True 
    OWNDATA : False 
    ... 

Die C order Werte in der Reihenfolge sind, dass sie in generiert wurden, die transponierte diejenigen sind nicht

In [212]: x.reshape(12,) # same as x.ravel() 
Out[212]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) 

In [213]: x.T.reshape(12,) 
Out[213]: array([ 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11]) 

Sie bekommen können. 1d Ansichten von beiden

In [214]: x1=x.T 

In [217]: x.shape=(12,) 

die Form von x c ein auch geändert werden.

In [220]: x1.shape=(12,) 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-220-cf2b1a308253> in <module>() 
----> 1 x1.shape=(12,) 

AttributeError: incompatible shape for a non-contiguous array 

Die Form der Transponierung kann jedoch nicht geändert werden. Die data ist immer noch in der 0,1,2,3,4... Reihenfolge, auf die nicht zugegriffen werden kann als 0,4,8... in einem 1d-Array zugegriffen.

In [227]: x2=x1.copy() 

In [228]: x2.flags 
Out[228]: 
    C_CONTIGUOUS : True 
    F_CONTIGUOUS : False 
    OWNDATA : True 
    ... 
In [229]: x2.shape=(12,) 

Mit Blick auf strides helfen könnten auch:

Aber eine Kopie x1 kann geändert werden. Ein Schritt ist, wie weit (in Bytes) es gehen muss, um zum nächsten Wert zu gelangen. Für eine 2D-Anordnung wird es 2 stride Werte sein:

In [233]: x=np.arange(12).reshape(3,4).copy() 

In [234]: x.strides 
Out[234]: (16, 4) 

Um zu der nächsten Zeile zu erhalten, Schritt 16 Bytes, nur nächste Spalte 4.

In [235]: x1.strides 
Out[235]: (4, 16) 

Transponieren schalten nur die Reihenfolge der Schritte.Die nächste Zeile hat nur 4 Bytes - d. H. Die nächste Zahl.

Ändern der Form ändert auch die Schritte - nur Schritt durch den Puffer 4 Bytes gleichzeitig.

In [238]: x2=x1.copy() 

In [239]: x2.strides 
Out[239]: (12, 4) 

Obwohl x2 sieht genauso aus wie x1, es seinen eigenen Datenpuffer hat, mit den Werten in einer anderen Reihenfolge. Die nächste Spalte ist jetzt 4 Bytes über, während die nächste Zeile 12 (3 * 4) ist. die Schritte zum (4,)

In [240]: x2.shape=(12,) 

In [241]: x2.strides 
Out[241]: (4,) 

Und wie bei x, verringert um die Form bis 1d verändern.

Für x1, mit Daten in der 0,1,2,... Reihenfolge, gibt es keinen 1d Schritt, der 0,4,8... geben würde.

__array_interface__ ist eine weitere nützliche Art und Weise Array-Informationen anzuzeigen:

In [242]: x1.__array_interface__ 
Out[242]: 
{'strides': (4, 16), 
'typestr': '<i4', 
'shape': (4, 3), 
'version': 3, 
'data': (163336056, False), 
'descr': [('', '<i4')]} 

x1 Der Datenpufferadresse wird wie für x gleich sein, mit der er die Daten teilt. x2 hat eine andere Pufferadresse.

Sie könnten auch mit dem Hinzufügen eines order='F' Parameters zu den copy und reshape Befehlen experimentieren.

Verwandte Themen