1. Die Bedeutung von Formen in NumPy
Sie schreiben: "Ich weiß buchstäblich, dass es eine Liste von Zahlen und eine Liste von Listen ist, in der jede Liste nur eine Zahl enthält", aber das ist eine etwas unhilfreiche Art, darüber nachzudenken.
Die beste Art, über NumPy-Arrays nachzudenken, ist, dass sie aus zwei Teilen bestehen, einem Datenpuffer, der einfach ein Block roher Elemente ist, und einer Ansicht, die beschreibt, wie der Datenpuffer interpretiert werden soll.
Zum Beispiel, wenn wir ein Array von 12 Ganzzahlen erstellen:
>>> a = numpy.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Dann besteht a
aus einem Datenpuffer, der ungefähr so angeordnet ist:
0 1 2 3 4 5 6 7 8 9 10 11
und einer Ansicht, die beschreibt, wie die Daten interpretiert werden:
>>> a.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)
Hier bedeutet die Form (12,)
, dass das Array von einem einzelnen Index indiziert wird, der von 0 bis 11 läuft. Konzeptionell, wenn wir diesen einzelnen Index i
nennen, sieht das Array a
i= 0 1 2 3 4 5 6 7 8 9 10 11
0 1 2 3 4 5 6 7 8 9 10 11
Wenn wir ein Array umformen, ändert dies nicht den Datenpuffer. Es erstellt stattdessen eine neue Ansicht, die eine andere Möglichkeit beschreibt, die Daten zu interpretieren. Nach:
>>> b = a.reshape((3, 4))
hat das Array b
den gleichen Datenpuffer wie a
, ist aber jetzt von zwei Indizes indiziert, die von 0 bis 2 bzw. von 0 bis 3 laufen. Wenn wir die zwei Indizes i
und j
bezeichnen, sieht das Array b
i= 0 0 0 0 1 1 1 1 2 2 2 2
j= 0 1 2 3 0 1 2 3 0 1 2 3
0 1 2 3 4 5 6 7 8 9 10 11
was bedeutet, dass:
>>> b[2,1]
9
Sie können sehen, dass sich der zweite Index schnell ändert und der erste Index langsam ändert. Wenn Sie dies lieber andersherum haben möchten, können Sie den order
-Parameter angeben:
>>> c = a.reshape((3, 4), order='F')
was zu einem Array führt, das so indiziert ist:
i= 0 1 2 0 1 2 0 1 2 0 1 2
j= 0 0 0 1 1 1 2 2 2 3 3 3
0 1 2 3 4 5 6 7 8 9 10 11
was bedeutet, dass:
>>> c[2,1]
5
Es sollte jetzt klar sein, was es bedeutet, wenn ein Array eine Form mit einer oder mehreren Dimensionen der Größe 1 hat. Nach:
>>> d = a.reshape((12, 1))
ist das Array d
von zwei Indizes indiziert, wobei der erste von 0 bis 11 läuft und der zweite Index immer 0 ist:
i= 0 1 2 3 4 5 6 7 8 9 10 11
j= 0 0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11
und so:
>>> d[10,0]
10
Eine Dimension der Länge 1 ist "frei" (in einem gewissen Sinne), also gibt es nichts, was Sie daran hindert, kreativ zu sein:
>>> e = a.reshape((1, 2, 1, 6, 1))
was ein Array ergibt, das so indiziert ist:
i= 0 0 0 0 0 0 0 0 0 0 0 0
j= 0 0 0 0 0 0 1 1 1 1 1 1
k= 0 0 0 0 0 0 0 0 0 0 0 0
l= 0 1 2 3 4 5 0 1 2 3 4 5
m= 0 0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11
und so:
>>> e[0,1,0,0,0]
6
Sehen Sie sich die NumPy-Interner-Dokumentation für weitere Details darüber an, wie Arrays implementiert sind.
2. Was tun?
Da numpy.reshape
nur eine neue Ansicht erstellt, sollten Sie keine Angst davor haben, es immer dann zu verwenden, wenn es notwendig ist. Es ist das richtige Werkzeug, wenn Sie ein Array auf eine andere Weise indizieren möchten.
In einer langen Berechnung ist es jedoch normalerweise möglich, sicherzustellen, dass Arrays von Anfang an mit der "richtigen" Form erstellt werden, um die Anzahl der Umformungen und Transpositionen zu minimieren. Aber ohne den tatsächlichen Kontext zu sehen, der zur Notwendigkeit einer Umformung geführt hat, ist es schwer zu sagen, was geändert werden sollte.
Das Beispiel in Ihrer Frage ist:
numpy.dot(M[:,0], numpy.ones((1, R)))
aber das ist unrealistisch. Erstens berechnet dieser Ausdruck:
M[:,0].sum()
das Ergebnis einfacher. Zweitens, gibt es wirklich etwas Besonderes an Spalte 0? Vielleicht benötigen Sie tatsächlich Folgendes:
M.sum(axis=0)