430 Stimmen

Unterschied zwischen der numpy.array-Form (R, 1) und (R,)

In numpy, einige Operationen geben das Format (R, 1) zurück, aber einige geben (R,) zurück. Dadurch wird die Matrixmultiplikation aufwendiger, da eine explizite reshape erforderlich ist. Zum Beispiel, gegeben eine Matrix M, wenn wir numpy.dot(M[:,0], numpy.ones((1, R))) machen möchten, wobei R die Anzahl der Zeilen ist (natürlich tritt das gleiche Problem auch spaltenweise auf). Es wird ein Fehler matrices are not aligned angezeigt, da M[:,0] das Format (R,) hat, jedoch numpy.ones((1, R)) das Format (1, R) hat.

Also meine Fragen sind:

  1. Was ist der Unterschied zwischen dem Format (R, 1) und (R,). Ich weiß, dass es sich buchstäblich um eine Liste von Zahlen und eine Liste von Listen handelt, wobei jede Liste nur eine Zahl enthält. Ich frage mich nur, warum numpy nicht so gestaltet ist, dass es das Format (R, 1) bevorzugt, anstelle von (R,), um die Matrixmultiplikation einfacher zu machen.

  2. Gibt es bessere Möglichkeiten für das obige Beispiel? Ohne explizites Umformen wie folgt: numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))

714voto

Gareth Rees Punkte 62623

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)

30voto

Evan Punkte 2247

Der Unterschied zwischen (R,) und (1,R) besteht buchstäblich in der Anzahl der Indizes, die du verwenden musst. ones((1,R)) ist ein 2-D-Array, das zufällig nur eine Zeile hat. ones(R) ist ein Vektor. Im Allgemeinen, wenn es keinen Sinn macht, dass die Variable mehr als eine Zeile/Spalte hat, solltest du einen Vektor verwenden, nicht eine Matrix mit einer einzelnen Dimension.

Für deinen speziellen Fall gibt es ein paar Optionen:

1) Mach einfach das zweite Argument zu einem Vektor. Folgendes funktioniert gut:

    np.dot(M[:,0], np.ones(R))

2) Wenn du Matlab-ähnliche Matrixoperationen möchtest, benutze die Klasse matrix anstelle von ndarray. Alle Matrizen werden in 2-D-Arrays gezwungen, und der Operator * führt eine Matrixmultiplikation durch, anstatt elementweise zu multiplizieren (somit brauchst du kein dot). Meiner Erfahrung nach ist das mehr Aufwand als es wert ist, aber es könnte nützlich sein, wenn du an Matlab gewöhnt bist.

24voto

Katie Jergens Punkte 341

Die Form ist ein Tupel. Wenn es nur 1 Dimension gibt, wird die Form eine Zahl sein und nach einem Komma leer sein. Für 2+ Dimensionen wird eine Zahl nach allen Kommas angezeigt.

# 1 Dimension mit 2 Elementen, Form = (2,).
# Beachten Sie, dass nach dem Komma nichts steht.
z=np.array([  # beginne Dimension
    10,       # keine Dimension
    20        # keine Dimension
])            # Ende Dimension
print(z.shape)

(2,)

# 2 Dimensionen, jeweils mit 1 Element, Form = (2,1)
w=np.array([  # beginne äußere Dimension
    [10],     # Element befindet sich in einer inneren Dimension
    [20]      # Element befindet sich in einer inneren Dimension
])            # Ende äußere Dimension
print(w.shape)

(2,1)

5voto

hpaulj Punkte 200871

Für seine Basis-Array-Klasse sind 2D-Arrays nicht spezieller als 1D- oder 3D-Arrays. Es gibt einige Operationen, die die Dimensionen erhalten, einige reduzieren sie, andere kombinieren oder erweitern sie sogar.

M=np.arange(9).reshape(3,3)
M[:,0].shape # (3,) wählt eine Spalte aus, gibt ein 1D-Array zurück
M[0,:].shape # dasselbe, eine Zeile, 1D-Array
M[:,[0]].shape # (3,1), Indexierung mit einer Liste (oder einem Array), gibt 2D zurück
M[:,[0,1]].shape # (3,2)

In [20]: np.dot(M[:,0].reshape(3,1),np.ones((1,3)))

Out[20]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

In [21]: np.dot(M[:,[0]],np.ones((1,3)))
Out[21]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

Weitere Ausdrücke, die dasselbe Array liefern

np.dot(M[:,0][:,np.newaxis],np.ones((1,3)))
np.dot(np.atleast_2d(M[:,0]).T,np.ones((1,3)))
np.einsum('i,j',M[:,0],np.ones((3)))
M1=M[:,0]; R=np.ones((3)); np.dot(M1[:,None], R[None,:])

MATLAB begann nur mit 2D-Arrays. Neuere Versionen erlauben mehr Dimensionen, behalten aber die untere Grenze von 2 bei. Aber man muss immer noch auf den Unterschied zwischen einer Zeilenmatrix und einer Spaltenmatrix achten, eine mit der Form (1,3) gegen (3,1). Wie oft haben Sie [1,2,3].' geschrieben? Ich wollte Reihenvektor und Spaltenvektor schreiben, aber bei dieser 2D-Beschränkung gibt es in MATLAB keine Vektoren - zumindest nicht im mathematischen Sinne eines Vektors als 1D.

Haben Sie sich np.atleast_2d (auch _1d- und _3d-Versionen) angesehen?

In neueren Python/numpy-Versionen gibt es einen matmul-Operator

In [358]: M[:,0,np.newaxis]@np.ones((1,3))
Out[358]: 
array([[0., 0., 0.],
       [3., 3., 3.],
       [6., 6., 6.]])

In numpy ist die elementweise Multiplikation in gewisser Weise grundlegender als die Matrixmultiplikation. Mit der Summe der Produkte in einer Dimension der Größe 1 ist es nicht notwendig, dot/matmul zu verwenden:

In [360]: M[:,0,np.newaxis]*np.ones((1,3))
Out[360]: 
array([[0., 0., 0.],
       [3., 3., 3.],
       [6., 6., 6.]])

Dies verwendet das Broadcasting, ein leistungsstarkes Feature, das numpy die ganze Zeit hatte. MATLAB hat es erst kürzlich hinzugefügt.

3voto

Mikhail_Sam Punkte 9146

Es gibt bereits viele gute Antworten hier. Aber für mich war es schwer, ein Beispiel zu finden, wo die Form oder das Array das gesamte Programm zerbrechen kann.

Also hier ist eines:

import numpy as np
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])

from sklearn.linear_model import LinearRegression
regr = LinearRegression()
regr.fit(a,b)

Dies wird mit dem Fehler fehlschlagen:

ValueError: Erwartet wurde ein 2D-Array, stattdessen wurde ein 1D-Array erhalten

aber wenn wir reshape zu a hinzufügen:

a = np.array([1,2,3,4]).reshape(-1,1)

funktioniert dies korrekt!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X