2009-11-23 1 views
18

In reinen Python Sie Matrizen Spalte für Spalte ziemlich leicht wachsen kann:Wachsende Matrizen spalten in NumPy

data = [] 
for i in something: 
    newColumn = getColumnDataAsList(i) 
    data.append(newColumn) 

NumPy ‚s Array nicht über die Append-Funktion. Die hstack Funktion funktioniert nicht auf Null Größe Arrays, so wird folgendes nicht:

data = numpy.array([]) 
for i in something: 
    newColumn = getColumnDataAsNumpyArray(i) 
    data = numpy.hstack((data, newColumn)) # ValueError: arrays must have same number of dimensions 

Also, meine Optionen sind entweder die initialisiert iside die Schleife mit geeigneten Zustand zu entfernen:

data = None 
for i in something: 
    newColumn = getColumnDataAsNumpyArray(i) 
    if data is None: 
     data = newColumn 
    else: 
     data = numpy.hstack((data, newColumn)) # works 

... oder eine Python-Liste zu verwenden und zu konvertieren ist später Array:

data = [] 
for i in something: 
    newColumn = getColumnDataAsNumpyArray(i) 
    data.append(newColumn) 
data = numpy.array(data) 

beiden Varianten ein wenig umständlich zu sein scheinen. Gibt es bessere Lösungen?

Antwort

18

NumPy hat tatsächlich haben eine append-Funktion, die es scheint, könnte das tun, was Sie wollen, zB

import numpy as NP 
my_data = NP.random.random_integers(0, 9, 9).reshape(3, 3) 
new_col = NP.array((5, 5, 5)).reshape(3, 1) 
res = NP.append(my_data, new_col, axis=1) 

Ihre zweite Schnipsel (hstack) wird funktionieren, wenn Sie eine weitere Zeile hinzufügen, zum Beispiel

my_data = NP.random.random_integers(0, 9, 16).reshape(4, 4) 
# the line to add--does not depend on array dimensions 
new_col = NP.zeros_like(my_data[:,-1]).reshape(-1, 1) 
res = NP.hstack((my_data, new_col)) 

hstack g ies das gleiche Ergebnis wie concatenate((my_data, new_col), axis=1), ich bin nicht sicher, wie sie leistungsmäßig vergleichen.


Während das ist die direkte Antwort auf Ihre Frage, soll ich erwähnen, dass durch eine Datenquelle Looping, ein Ziel zu bevölkern über hängen, während in Python ganz gut, nicht idiomatischer NumPy ist. Hier ist der Grund:

ein NumPy Array Initialisierung relativ teuer ist, und mit diesem herkömmlichen Python-Muster, entstehen Sie, dass Kosten, mehr oder weniger, bei jeder Schleife Iteration (dh jede angehängt an eine NumPy Array ist ungefähr so ​​initialisiert ein neues Array mit einer anderen Größe).

Aus diesem Grunde ist das gemeinsame Muster in NumPy zur iterativen Addition von Spalten mit einem 2D-Array ist ein leeres Zielarrays einmal (oder vorbelegen ein einzelnes 2D NumPy Array all leeren Spalten) zu initialisieren, die sukzessiv die leeren Spalten füllen, indem die gewünschten spaltenweise versetzt (index) Einstellen - viel leichter zu zeigen, als zu erklären: wie im OP

>>> # initialize your skeleton array using 'empty' for lowest-memory footprint 
>>> M = NP.empty(shape=(10, 5), dtype=float) 

>>> # create a small function to mimic step-wise populating this empty 2D array: 
>>> fnx = lambda v : NP.random.randint(0, 10, v) 

bevöl NumPy Array, mit der Ausnahme jede Iteration gerade neu setzt die Werte von M bei aufeinanderfolgenden spaltenweisen Offsets

>>> for index, itm in enumerate(range(5)):  
     M[:,index] = fnx(10) 

>>> M 
    array([[ 1., 7., 0., 8., 7.], 
     [ 9., 0., 6., 9., 4.], 
     [ 2., 3., 6., 3., 4.], 
     [ 3., 4., 1., 0., 5.], 
     [ 2., 3., 5., 3., 0.], 
     [ 4., 6., 5., 6., 2.], 
     [ 0., 6., 1., 6., 8.], 
     [ 3., 8., 0., 8., 0.], 
     [ 5., 2., 5., 0., 1.], 
     [ 0., 6., 5., 9., 1.]]) 
Natürlich

von, wenn Sie nicht im Voraus bekannt ist, welche Größe Ihr Array nur erstellen sollte viel größer, als Sie die ‚nicht verwendeten‘ Teile müssen und trimmen, wenn Sie es fertig bevöl

>>> M[:3,:3] 
    array([[ 9., 3., 1.], 
     [ 9., 6., 8.], 
     [ 9., 7., 5.]]) 
+0

Sehr hilfreiche Post für einen numpigen Neuling. Kurze Frage: Gibt es einen Grund, warum Sie 'für den Index verwenden, itm in enumerate (Bereich (5)):' und nicht nur zum Beispiel 'für x in range (5):' Sehen als Index und itm haben den gleichen Wert und nur einer wird verwendet. –

+0

@ JohnBarça danke für das Feedback. Möglicherweise haben Sie recht, dass die Details meines Code-Snippets sorgfältiger gewählt worden sein sollten - dh in meinem Beispiel ist der Wert von "index" bei jeder Iteration tatsächlich der gleiche wie der Wert der Loop-Variablen. Das ist jedoch ein Artefakt - die Werte dieser beiden Variablen sind in der Praxis wahrscheinlich nicht gleich (z. B. ist die Iterable eine Liste mit Werten, die an eine Funktion übergeben werden, die die 1D-Arrays erstellt, die dann in das Zielarray "eingefügt" werden)). – doug

1

Im Allgemeinen ist es kostspielig, das NumPy-Array neu zuzuordnen - also ist Ihre dritte Lösung wirklich die beste Leistung.

aber ich denke, hstack wird tun, was Sie wollen - das Stichwort in der Fehlermeldung ist,

ValueError: arrays must have same number of dimensions`

Ich vermute, dass newColumn zwei Dimensionen hat (anstelle eines 1D-Vektor), so dass Sie Daten benötigen um auch zwei Dimensionen zu haben ..., zum Beispiel data = np.array([[]]) - oder alternativ newColumn zu einem 1D-Vektor machen (im Allgemeinen, wenn die Dinge 1D sind, ist es besser, sie 1D in NumPy zu behalten, damit Broadcasting usw. besser funktioniert). In diesem Fall sollten Sie np.squeeze(newColumn) und hstack oder vstack mit Ihrer ursprünglichen Definition der Daten arbeiten.

4

Normalerweise ändern Sie die Größe eines NumPy-Arrays nicht, wenn Sie es erstellen. Was magst du nicht an deiner dritten Lösung? Wenn es sich um eine sehr große Matrix/Array ist, dann könnte es sich lohnen, die Array-Zuweisung, bevor Sie seine Werte beginnen zuweisen:

x = len(something) 
y = getColumnDataAsNumpyArray.someLengthProperty 

data = numpy.zeros((x,y)) 
for i in something: 
    data[i] = getColumnDataAsNumpyArray(i) 
3

Die hstack kann auf null große Arrays arbeiten:

import numpy as np 

N = 5 
M = 15 

a = np.ndarray(shape = (N, 0)) 
for i in range(M): 
    b = np.random.rand(N, 1) 
    a = np.hstack((a, b))