2013-10-03 18 views
5

sto cercando di ordinare la seguente matrice su column1, quindi column2 quindi column3ordinamento matrice NumPy su più colonne in Python

[['2008' '1' '23' 'AAPL' 'Buy' '100'] 
['2008' '1' '30' 'AAPL' 'Sell' '100'] 
['2008' '1' '23' 'GOOG' 'Buy' '100'] 
['2008' '1' '30' 'GOOG' 'Sell' '100'] 
['2008' '9' '8' 'GOOG' 'Buy' '100'] 
['2008' '9' '15' 'GOOG' 'Sell' '100'] 
['2008' '5' '1' 'XOM' 'Buy' '100'] 
['2008' '5' '8' 'XOM' 'Sell' '100']] 

ho usato il codice seguente:

idx=np.lexsort((order_array[:,2],order_array[:,1],order_array[:,0])) 
    order_array=order_array[idx] 

La matrice risultante è

[['2008' '1' '23' 'AAPL' 'Buy' '100'] 
['2008' '1' '23' 'GOOG' 'Buy' '100'] 
['2008' '1' '30' 'AAPL' 'Sell' '100'] 
['2008' '1' '30' 'GOOG' 'Sell' '100'] 
['2008' '5' '1' 'XOM' 'Buy' '100'] 
['2008' '5' '8' 'XOM' 'Sell' '100'] 
['2008' '9' '15' 'GOOG' 'Sell' '100'] 
['2008' '9' '8' 'GOOG' 'Buy' '100']] 

Il problema è che le ultime due righe sono errate. La matrice corretta dovrebbe avere l'ultima riga come la penultima. Ho provato tutto ma non sono in grado di capire perché questo sta accadendo. Apprezzerà qualche aiuto

Sto usando il seguente codice per ottenere array_ordine.

for i in …. 
    x= ldt_timestamps[i] # this is a list of timestamps 
    s_sym=…… 
    list=[int(x.year),int(x.month),int(x.day),s_sym,'Buy',100] 
    rows_list.append(list) 

order_array=np.array(rows_list) 
+0

possibile duplicato di [Ordinamento 2D matrice NumPy da più assi] (http://stackoverflow.com/questions/2706605/sorting-a-2d-numpy-array-by-multiple -axes) Usa quella risposta, ma usa un dtype che abbia senso per i tuoi dati (non tutte le stringhe), ad es 'dt = dt = [('y', np.uint32), ('m', np.uint32), ('d', np.uint32), ('sym', 'S4'), ('bs' , 'S4'), ('huh', np.uint32)] ' – askewchan

risposta

7

TLDR: NumPy brilla fare calcoli numerici su array numerici. Sebbene sia possibile (vedi sotto) NumPy non è adatto per questo. Probabilmente stai meglio usando Pandas.


La causa del problema:

I valori vengono ordinati come stringhe. È necessario ordinarli come ints.

In [7]: sorted(['15', '8']) 
Out[7]: ['15', '8'] 

In [8]: sorted([15, 8]) 
Out[8]: [8, 15] 

Questo è accaduto perché order_array contiene stringhe. È necessario convertire tali stringhe in ints laddove appropriato.

Conversione di dtype da string-dtype a dtype numerico richiede allocazione dello spazio per un nuovo array. Pertanto, probabilmente starai meglio rivedendo il modo in cui stai creando order_array dall'inizio.

È interessante notare che, anche se è stato convertito i valori da interi, quando si chiama

order_array = np.array(rows_list) 

NumPy per default crea una omogenea serie . In un array omogeneo ogni valore ha lo stesso dtype. Così NumPy ha cercato di trovare il comune denominatore tra tutti i tuoi valori e ha scelto un dtype stringa, vanificando lo sforzo che hai messo nella conversione delle stringhe in int.

È possibile controllare la DTYPE per te ispezionando order_array.dtype:

In [42]: order_array = np.array(rows_list) 

In [43]: order_array.dtype 
Out[43]: dtype('|S4') 

Ora, come possiamo risolvere questo problema?


Utilizzando un DTYPE oggetto:

Il modo più semplice è quello di utilizzare un 'oggetto' DTYPE

In [53]: order_array = np.array(rows_list, dtype='object') 

In [54]: order_array 
Out[54]: 
array([[2008, 1, 23, AAPL, Buy, 100], 
     [2008, 1, 30, AAPL, Sell, 100], 
     [2008, 1, 23, GOOG, Buy, 100], 
     [2008, 1, 30, GOOG, Sell, 100], 
     [2008, 9, 8, GOOG, Buy, 100], 
     [2008, 9, 15, GOOG, Sell, 100], 
     [2008, 5, 1, XOM, Buy, 100], 
     [2008, 5, 8, XOM, Sell, 100]], dtype=object) 

Il problema qui è che np.lexsort o np.sort non funzionano su array di dtype object. Per ovviare a questo problema, si poteva ordinare la rows_list prima di creare order_list:

In [59]: import operator 

In [60]: rows_list.sort(key=operator.itemgetter(0,1,2)) 
Out[60]: 
[(2008, 1, 23, 'AAPL', 'Buy', 100), 
(2008, 1, 23, 'GOOG', 'Buy', 100), 
(2008, 1, 30, 'AAPL', 'Sell', 100), 
(2008, 1, 30, 'GOOG', 'Sell', 100), 
(2008, 5, 1, 'XOM', 'Buy', 100), 
(2008, 5, 8, 'XOM', 'Sell', 100), 
(2008, 9, 8, 'GOOG', 'Buy', 100), 
(2008, 9, 15, 'GOOG', 'Sell', 100)] 

order_array = np.array(rows_list, dtype='object') 

Una soluzione migliore sarebbe quella di combinare le prime tre colonne in datetime.date oggetti:

import operator 
import datetime as DT 

for i in ...: 
    seq = [DT.date(int(x.year), int(x.month), int(x.day)) ,s_sym, 'Buy', 100] 
    rows_list.append(seq) 
rows_list.sort(key=operator.itemgetter(0,1,2))   
order_array = np.array(rows_list, dtype='object') 

In [72]: order_array 
Out[72]: 
array([[2008-01-23, AAPL, Buy, 100], 
     [2008-01-30, AAPL, Sell, 100], 
     [2008-01-23, GOOG, Buy, 100], 
     [2008-01-30, GOOG, Sell, 100], 
     [2008-09-08, GOOG, Buy, 100], 
     [2008-09-15, GOOG, Sell, 100], 
     [2008-05-01, XOM, Buy, 100], 
     [2008-05-08, XOM, Sell, 100]], dtype=object) 

Anche se questo è semplice, non mi piacciono gli array NumPy dell'oggetto dtype. Non si ottengono né la velocità né la memoria risparmiando spazio per gli array NumPy con i dtypes nativi . A questo punto si potrebbe trovare che lavorare con un elenco Python degli elenchi più rapido e sintatticamente più facile da gestire.


Utilizzando una matrice strutturato:

Una soluzione più NumPy-ish che offre ancora prestazioni di velocità e la memoria è utilizzare un structured array (al contrario di matrice omogenea). Per fare una serie strutturato con np.array avrete bisogno di fornire una DTYPE esplicitamente:

dt = [('year', '<i4'), ('month', '<i4'), ('day', '<i4'), ('symbol', '|S8'), 
     ('action', '|S4'), ('value', '<i4')] 
order_array = np.array(rows_list, dtype=dt) 

In [47]: order_array.dtype 
Out[47]: dtype([('year', '<i4'), ('month', '<i4'), ('day', '<i4'), ('symbol', '|S8'), ('action', '|S4'), ('value', '<i4')]) 

per ordinare l'array strutturato è possibile utilizzare il metodo sort:

order_array.sort(order=['year', 'month', 'day']) 

Per eseguire operazioni array strutturati, è necessario conoscere alcune differenze tra array omogenei e strutturati:

Il tuo originale la matrice omogenea era bidimensionale. Al contrario, tutti array strutturati sono 1-dimensionale:

In [51]: order_array.shape 
Out[51]: (8,) 

Se indice dell'array strutturato con un int o scorrere la matrice, è restituiti righe:

In [52]: order_array[3] 
Out[52]: (2008, 1, 30, 'GOOG', 'Sell', 100) 

Con matrici omogenee puoi accedere alle colonne con order_array[:, i] Ora, con un array strutturato, puoi accedervi per nome: es. order_array['year'].


In alternativa, utilizzare Panda:

Se è possibile installare Pandas, penso che si potrebbe essere più felice di lavoro con un Pandas dataframe:

In [73]: df = pd.DataFrame(rows_list, columns=['date', 'symbol', 'action', 'value']) 
In [75]: df.sort(['date']) 
Out[75]: 
     date symbol action value 
0 2008-01-23 AAPL Buy 100 
2 2008-01-23 GOOG Buy 100 
1 2008-01-30 AAPL Sell 100 
3 2008-01-30 GOOG Sell 100 
6 2008-05-01 XOM Buy 100 
7 2008-05-08 XOM Sell 100 
4 2008-09-08 GOOG Buy 100 
5 2008-09-15 GOOG Sell 100 

Panda dispone di funzioni utili per l'allineamento timeseries per data, compilando i valori mancanti , raggruppando e aggregando/trasformando righe o colonne.


In genere è più utile avere una sola colonna della data, invece di tre colonne valori interi per l'anno, mese, giorno.

Se avete bisogno di anno, mese, giorno colonne come distinti ai fini di outputing, per dire csv, quindi è possibile sostituire la colonna data con anno, mese, colonne giornata come questa:

In [33]: df = df.join(df['date'].apply(lambda x: pd.Series([x.year, x.month, x.day], index=['year', 'month', 'day']))) 

In [34]: del df['date'] 

In [35]: df 
Out[35]: 
    symbol action value year month day 
0 AAPL Buy 100 2008  1 23 
1 GOOG Buy 100 2008  1 23 
2 AAPL Sell 100 2008  1 30 
3 GOOG Sell 100 2008  1 30 
4 XOM Buy 100 2008  5 1 
5 XOM Sell 100 2008  5 8 
6 GOOG Buy 100 2008  9 8 
7 GOOG Sell 100 2008  9 15 

Oppure, se non si ha alcun uso per la colonna 'data', si può ovviamente lasciare rows_list da solo e creare il DataFrame con le colonne anno, mese, giorno dall'inizio. L'ordinamento è ancora facile:

df.sort(['year', 'month', 'day']) 
+0

grazie. ma sto convertendo le stringhe in int. Ho modificato la domanda per includere il codice per la creazione di order_array. Attendo con ansia il vostro aiuto – user2842122

+0

@ user2842122 - questi 'ints' sono in fase di riconversione in stringhe. unutbu - Penso che la soluzione più semplice potrebbe essere quella di introdurre un ricognitore NumPy, composto da un oggetto NumPy [datetime object] (http://docs.scipy.org/doc/numpy/reference/arrays.datetime.html) e il resto dati stringa e intero. C'è [un esempio completo qui] (http://stackoverflow.com/a/16618557/122022). –

+0

@AronAhmadia: Grazie per il commento! Sì, stavo pensando di aggiungere qualcosa del genere, ma temo che questa risposta sia già troppo lunga, e Panda è ancora probabilmente il modo migliore per andare. – unutbu

Problemi correlati