2009-11-23 13 views
18

in puro Python si può crescere colonna di matrici per colonna abbastanza facilmente:matrici Growing columnwise in NumPy

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

s' NumPy serie non ha la funzione di accodamento. La funzione hstack non funziona su array di dimensioni pari a zero, quindi il seguente non funziona:

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

Quindi, le mie opzioni sono o di rimuovere l'inizializzazione del Iside il ciclo con condizione appropriata:

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

... o per utilizzare un elenco Python e convertire è in seguito ad un array:

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

Entrambe le varianti sembrano un po 'scomodo per essere. Ci sono soluzioni migliori?

risposta

18

NumPy in realtà ha un accodamento funzione , che a quanto pare potrebbe fare quello che vuoi, ad esempio,

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) 

il secondo frammento (hstack) funzionerà se si aggiunge un'altra linea, ad esempio,

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 È lo stesso risultato di concatenate((my_data, new_col), axis=1), non sono sicuro di come si confrontano le prestazioni.


Mentre questa è la risposta più diretta alla tua domanda, devo dire che il ciclo attraverso una fonte di dati per popolare una destinazione tramite accoda, mentre bene in Python, non è idiomatica NumPy. Ecco perché:

inizializzare una matrice NumPy è relativamente costoso, e con questo modello pitone convenzionale, si incorrere quel costo, più o meno, ad ogni iterazione del ciclo (cioè, ogni accoda a una matrice NumPy è approssimativamente come inizializzazione un nuovo array con una dimensione diversa).

Per tale motivo, il modello comune in NumPy per iterativa aggiunta di colonne di una matrice 2D è inizializzare una matrice di destinazione vuoto volta (o pre-allocare un singolo array 2D NumPy aventi tutte le colonne vuote) la successivamente popolano le colonne vuote impostando il desiderato colonna-saggio offset() - molto più facile da mostrare che da spiegare:

>>> # 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) 

popolano matrice NumPy come nel PO, tranne ogni iterazione appena ri-imposta i valori di M a successivi offset di colonna

>>> 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.]]) 

naturalmente se non si sa in anticipo che cosa dimensioni l'array dovrebbe essere basta creare una molto più grande del necessario e tagliare le porzioni 'Libero' quando hai finito popolato

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

Post molto utile per un principiante intorpidito. Domanda veloce: v'è alcuna ragione per cui si utilizza 'per l'indice, ITM in enumerate (range (5)):' e non solo, per esempio, 'per x nella gamma (5):' visto come index e itm hanno lo stesso valore e ne viene usato solo uno. –

+0

@ JohnBarça grazie per il feedback. Potresti avere ragione nel ritenere che i dettagli del mio frammento di codice avrebbero dovuto essere scelti con maggiore attenzione, ovvero, nel mio esempio, il valore di "indice" ad ogni iterazione è in effetti uguale al valore della variabile di ciclo. Questo è un artefatto però - i valori di queste due variabili probabilmente non saranno uguali nella pratica (ad esempio, l'iterabile è una lista contenente valori da passare a una funzione che crea gli array 1D che vengono poi "inseriti" nell'array di destinazione). – doug

1

In genere è costoso mantenere la riallocazione dell'array NumPy, quindi la terza soluzione è davvero la migliore per quanto riguarda le prestazioni.

Tuttavia penso hstack farà ciò che si vuole - lo spunto è nel messaggio di errore,

ValueError: arrays must have same number of dimensions`

Sto indovinando che newColumn ha due dimensioni (piuttosto che un vettore 1D), quindi è necessario i dati avere anche due dimensioni ..., ad esempio, data = np.array([[]]) - o in alternativa rendere newColumn un vettore 1D (in genere se le cose sono 1D è meglio tenerle 1D in NumPy, quindi le trasmissioni, ecc. funzionano meglio). nel qual caso utilizzare np.squeeze(newColumn) e hstack o vstack dovrebbe funzionare con la definizione originale dei dati.

4

Di solito non si continua a ridimensionare un array NumPy quando lo si crea. Cosa non ti piace della tua terza soluzione? Se si tratta di una grande matrice/matrice, allora potrebbe valere la pena di allocare la matrice prima di iniziare ad assegnare i suoi valori:

x = len(something) 
y = getColumnDataAsNumpyArray.someLengthProperty 

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

Il hstack può funzionare su array di dimensioni zero:

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)) 
Problemi correlati