2016-04-23 15 views
12

Ho una lunga tabella di dati (~ 200 righe per 50 colonne) e ho bisogno di creare un codice che possa calcolare i valori medi di ogni due righe e per ogni colonna nella tabella con l'output finale che è una nuova tabella dei valori medi. Questo è ovviamente folle da fare in Excel! Io uso python3 e sono a conoscenza di alcune domande simili: here, here e here. Ma nessuno di questi aiuta perché ho bisogno di un codice elegante per lavorare con più colonne e produce una tabella di dati organizzata. Tra l'altro il mio datatable originale è stato importato usando i panda ed è definito come un dataframe ma non è stato trovato un modo semplice per farlo nei panda. L'aiuto è molto apprezzato.Calcola la media di ogni x righe in una tabella e crea una nuova tabella

Un esempio della tabella (versione breve) è:

a b c d 
2 50 25 26 
4 11 38 44 
6 33 16 25 
8 37 27 25 
10 28 48 32 
12 47 35 45 
14 8 16 7 
16 12 16 30 
18 22 39 29 
20 9 15 47 

prevista tabella di media:

a b  c  d 
3 30.5 31.5 35 
7 35 21.5 25 
11 37.5 41.5 38.5 
15 10 16 18.5 
19 15.5 27 38 

risposta

13

È possibile creare un gruppo artificiale utilizzando df.index//2 (o come @DSM sottolineato, utilizzando np.arange(len(df))//2 - in modo che funzioni per tutti gli indici) e quindi utilizzare groupby:

df.groupby(np.arange(len(df))//2).mean() 
Out[13]: 
     a  b  c  d 
0 3.0 30.5 31.5 35.0 
1 7.0 35.0 21.5 25.0 
2 11.0 37.5 41.5 38.5 
3 15.0 10.0 16.0 18.5 
4 19.0 15.5 27.0 38.0 
+0

Ho trovato qualcosa di chiuso [qui] (StackOverflow.it/questions/36810595/calcola-media-di-ogni-x-rows-in-a-table-and-create-new-table) MA la tua risposta è molto elegante e compatta. Grazie mille! Solo per interesse cosa significa la prima barra in avanti in df.index // 2? – Gnu

+0

Prego. Questo vale per la divisione di interi in modo che sia 2 // 2 che 3 // 2 producano 1, 4 // 2 e 5 // 2 producano 2 ... (e siano inseriti nello stesso gruppo). – ayhan

+0

Trovato! Molte grazie! – Gnu

6

modo NumPythonic sarebbe quello di estrarre gli elementi come un array NumPy con df.values, poi rimodellare a un array 3D con 2 elementi lungo axis=1 e 4 lungo axis=2 ed eseguire la riduzione media lungo axis=1 ed infine riconvertire un dataframe, in questo modo -

pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1)) 

come si è visto, è possibile introdurre strumento molto efficace di NumPy: np.einsum per fare questo average-reduction come una combinazione di sum-reduction e scaling-down, in questo modo -

pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0) 

Si noti che gli approcci proposti presuppongono che il numero di righe sia divisibile per 2.

anche come noted by @DSM, per preservare i nomi delle colonne, è necessario aggiungere columns=df.columns quando riconversione dataframe, vale a dire -

pd.DataFrame(...,columns=df.columns) 

Campione run -

>>> df 
    0 1 2 3 
0 2 50 25 26 
1 4 11 38 44 
2 6 33 16 25 
3 8 37 27 25 
4 10 28 48 32 
5 12 47 35 45 
6 14 8 16 7 
7 16 12 16 30 
8 18 22 39 29 
9 20 9 15 47 
>>> pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1)) 
    0  1  2  3 
0 3 30.5 31.5 35.0 
1 7 35.0 21.5 25.0 
2 11 37.5 41.5 38.5 
3 15 10.0 16.0 18.5 
4 19 15.5 27.0 38.0 
>>> pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0) 
    0  1  2  3 
0 3 30.5 31.5 35.0 
1 7 35.0 21.5 25.0 
2 11 37.5 41.5 38.5 
3 15 10.0 16.0 18.5 
4 19 15.5 27.0 38.0 

test runtime -

In questa sezione, testiamo tutti e tre gli approcci elencati finora per risolvere il problema delle prestazioni, tra cui @ayhan's solution with groupby.

In [24]: A = np.random.randint(0,9,(200,50)) 

In [25]: df = pd.DataFrame(A) 

In [26]: %timeit df.groupby(df.index//2).mean() # @ayhan's solution 
1000 loops, best of 3: 1.61 ms per loop 

In [27]: %timeit pd.DataFrame(df.values.reshape(-1,2,df.shape[1]).mean(1)) 
1000 loops, best of 3: 317 µs per loop 

In [28]: %timeit pd.DataFrame(np.einsum('ijk->ik',df.values.reshape(-1,2,df.shape[1]))/2.0) 
1000 loops, best of 3: 266 µs per loop 
+0

Questo è ~ 2 volte più veloce di groupby. Bello. – ayhan

+1

@ayhan Sì, l'ho notato, stava per pubblicare i runtime, ma poi il pensiero finale è stato quello di lasciarlo passare :) La tua è una cosa nuova che ho imparato però, i panda non sono la mia cosa! – Divakar

+0

Nota che gli approcci di Numpy perdono i nomi delle colonne. Potresti aggiungere 'columns = df.columns' per correggere questo. – DSM

3
df.set_index(np.arange(len(df)) // 2).mean(level=0) 
0

Si può affrontare questo problema utilizzando pd.rolling() per creare un media mobile e poi basta prendere ogni secondo elemento utilizzando iloc

df = df.rolling(2).mean() 
df = df.iloc[::2, :] 

Si noti che la prima osservazione mancherà (cioè la laminazione inizia in cima)

Problemi correlati