Numpy Index Slice ohne Verlust von Dimensionsinformationen

96

Ich verwende numpy und möchte eine Zeile indizieren, ohne die Dimensionsinformationen zu verlieren.

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10,:]
xslice.shape   # >> (10,)  

In diesem Beispiel ist xslice jetzt 1 Dimension, aber ich möchte, dass es (1,10) ist. In R würde ich X [10,:, drop = F] verwenden. Gibt es etwas ähnliches in numpy. Ich konnte es in der Dokumentation nicht finden und sah keine ähnliche Frage.

Vielen Dank!

Mindmatters
quelle

Antworten:

57

Es ist wahrscheinlich am einfachsten x[None, 10, :]oder gleichwertig (aber besser lesbar) x[np.newaxis, 10, :].

Was den persönlichen Grund angeht, finde ich es sehr schnell ärgerlich, ständig Arrays mit Singleton-Dimensionen zu haben. Ich würde vermuten, dass es den numpy Entwicklern genauso ging.

Außerdem können numpy Broadcasting-Arrays sehr gut verarbeiten, sodass es normalerweise wenig Grund gibt, die Dimension des Arrays beizubehalten, von dem das Slice stammt. Wenn Sie das getan haben, dann Dinge wie:

a = np.zeros((100,100,10))
b = np.zeros(100,10)
a[0,:,:] = b

entweder würde nicht funktionieren oder wäre viel schwieriger zu implementieren.

(Zumindest ist das meine Vermutung über die Argumentation des Numpy-Entwicklers, die beim Schneiden von Dimensionsinformationen gelöscht wurde.)

Joe Kington
quelle
6
@ Lisa: x[None, 10]wird machen was du willst.
naught101
Jep. NoneStellen Sie Ihr s neben die Dims, die Sie hacken.
Verrückter Physiker
1
Im Beispiel fehlen zusätzliche Klammern für das Tupel in der Zuordnung zu b; es sollte sein b = np.zeros((100,10)).
Jerzy
Was ist der Grund für die Verwendung von insgesamt 3 Indizes anstelle von nur zwei? Ich meine X[10,None](am Beispiel Ihres Codes).
Greenoldman
8
"Es gibt normalerweise wenig Grund, die Dimension des Arrays beizubehalten " ... Nun, es wird die Matrixmultiplikation ( np.matmul()oder@ ) mit Sicherheit völlig vermasseln . Ich bin gerade davon verbrannt worden.
Jean-François Corbett
89

Eine andere Lösung ist zu tun

X[[10],:]

oder

I = array([10])
X[I,:]

Die Dimensionalität eines Arrays bleibt erhalten, wenn die Indizierung durch eine Liste (oder ein Array) von Indizes durchgeführt wird. Das ist schön, weil Sie die Wahl haben, die Abmessung beizubehalten und zu drücken.

gnebehay
quelle
2
Dies kopiert die Array-Daten
Per
Dies ist nicht immer der Fall. Siehe: x = np.array([[1,2,3,4]]) Wenn Sie es dann mit schneiden, erhalten x[[0],[1,2]] Sie die eindimensionale. array([2, 3]) Meiner Meinung nach ist es bei der Auswahl von Spalten- oder Zeilenvektoren am besten, das Slice einfach zu machen und dann zu verwenden np.reshape. In meinem Beispiel wäre es alsonp.reshape(x[0,[1,2]],[1,2])
Alexander
1
andere, die sich am Ende eines Semikolons bewusst sind - es ist wichtig, dass X[[10]]dies so interpretiert wird, dass die X[10]Form kleiner ist; ähnlich X[[10, 20]] == X[10, 20]und Form ist noch kleiner
Ben Usman
1
Warnung : Mischen Sie diese Art der Indizierung nicht mit einer ganzzahligen Indizierung! Wenn Sie avon Form hatten (10, 20, 30), dann a[0, :, [0]]haben Sie Form (1, 20), nicht (20, 1), weil in letzteren Indizes gesendet werden, zu a[[0], :, [0]]denen oft nicht ganz das ist, was Sie erwarten! Während a[0, :, :1]wird Ihnen (20, 1)wie erwartet geben. Lesen Sie außerdem den obigen Kommentar für einen seltsamen Randfall mit einem einzelnen Index. Insgesamt scheint diese Methode zu viele Randfälle zu haben.
Ben Usman
29

Ich habe ein paar vernünftige Lösungen gefunden.

1) verwenden numpy.take(X,[10],0)

2) Verwenden Sie diese seltsame Indizierung X[10:11:, :]

Im Idealfall sollte dies die Standardeinstellung sein. Ich habe nie verstanden, warum Dimensionen jemals fallen gelassen werden. Aber das ist eine Diskussion für Numpy ...

Mindmatters
quelle
1
'Dimensionen' werden beim Indizieren von Python-Listen gelöscht alist[0]und beim Schneiden beibehalten.
Hpaulj
4
Option 2 (die wie slice(n, n+1)zum Extrahieren des Index geschrieben werden kann n) sollte die akzeptierte Antwort sein, da sie die einzige ist, die sich natürlich auf den n-dimensionalen Fall erstreckt.
Norok2
Option 2 scheint wie X[10:11, :]in Python 3.7.5 geschrieben werden zu können (dh ohne den zusätzlichen Doppelpunkt nach dem 11.)
Joe
6

Hier ist eine Alternative, die mir besser gefällt. Anstatt mit einer einzelnen Zahl zu indizieren, indizieren Sie mit einem Bereich. Das heißt, verwenden X[10:11,:]. (Beachten Sie, dass 10:1111 nicht enthalten ist).

import numpy as np
X = np.zeros((100,10))
X.shape        # >> (100, 10)
xslice = X[10:11,:]
xslice.shape   # >> (1,10)

Dies macht es einfach, auch mit mehr Dimensionen zu verstehen, ohne zu Nonejonglieren und herauszufinden, welche Achse welchen Index verwenden soll. Außerdem müssen Sie keine zusätzliche Buchhaltung in Bezug auf die Arraygröße durchführen, nur i:i+1für alle i, die Sie für die reguläre Indizierung verwendet hätten.

b = np.ones((2, 3, 4))
b.shape # >> (2, 3, 4)
b[1:2,:,:].shape  # >> (1, 3, 4)
b[:, 2:3, :].shape .  # >> (2, 1, 4)
Andrew Schwartz
quelle
0

Dies ist besonders ärgerlich, wenn Sie nach einem Array indizieren, das zur Laufzeit möglicherweise die Länge 1 hat. Für diesen Fall gibt es np.ix_:

some_array[np.ix_(row_index,column_index)]
Jthorpe
quelle