2014-12-15 13 views
23

Ad esempio ho seguente tabella:Python panda - filtra righe dopo groupby

index,A,B 
0,0,0 
1,0,8 
2,0,8 
3,1,0 
4,1,5 

Dopo raggruppamento per A:

0: 
index,A,B 
0,0,0 
1,0,8 
2,0,8 

1: 
index,A,B 
3,1,5 
4,1,3 

Ciò che serve è far cadere righe di ogni gruppo, in cui il numero nella colonna B è inferiore al valore massimo di tutte le righe dalla colonna del gruppo B. Beh io ho un problema di traduzione e la formulazione di questo problema a Inglese ecco l'esempio:

massimo valore dalle righe nella colonna B nel gruppo 0:

quindi voglio far cadere fila con indice 0 e mantenere le righe con indici 1 e 2

valore massimo di righe nella colonna B nel gruppo 1:

quindi voglio goccia fila con indice 4 e tenere riga con indice 3

Ho provato ad utilizzare la funzione di filtro panda, ma il problema è che è operativo su tutte le righe gruppo contemporaneamente:

data = <example table> 
grouped = data.groupby("A") 
filtered = grouped.filter(lambda x: x["B"] == x["B"].max()) 

Quindi quello di cui ho idealmente bisogno è un filtro, che itera su tutte le righe del gruppo.

Grazie per l'aiuto!

P.S. C'è anche modo di cancellare solo le righe nei gruppi e non restituire l'oggetto DataFrame?

+0

l'ultimo bit è totalmente ambiguo: se stai cancellando le righe in ogni gruppo, ma * non * restituendo un dataframe, allora cosa vuoi restituire? –

+0

I dati, quando si confronta la tabella completa con i gruppi, non corrispondono. per favore chiariscilo –

+0

Scusa ero un po 'impegnato quando ho scritto questa domanda. I dati sono corretti ora. Insomma, voglio solo cancellare le righe dai gruppi e mantenere questi gruppi così come sono. Devo applicare diversi filtri e dopo ogni applicazione è necessario un nuovo groupby. – jirinovo

risposta

29

È sufficiente utilizzare apply sull'oggetto groupby. Ho modificato i dati di esempio per rendere questo un po 'più chiaro:

import pandas 
from io import StringIO 

csv = StringIO("""index,A,B 
0,1,0.0 
1,1,3.0 
2,1,6.0 
3,2,0.0 
4,2,5.0 
5,2,7.0""") 

df = pandas.read_csv(csv, index_col='index') 
groups = df.groupby(by=['A']) 
print(groups.apply(lambda g: g[g['B'] == g['B'].max()])) 

che stampa:

  A B 
A index  
1 2  1 6 
2 4  2 7 
+0

Grazie, funziona bene. Posso solo chiederti, cosa fa 'apply()' specificamente? E sono un po 'confuso con 'g [g [' B ']' – jirinovo

+2

@jirinovo 'groupby.apply (funzione)' esegue ogni singolo gruppo attraverso quella funzione e concatena tutti i risultati. 'g [...]' è l'indicizzazione fancy/boolean - il che significa che restituisce solo le righe in cui quella condizione interna è vera. In questo caso, la condizione è 'g ['B'] == g ['B']. Max()', ad esempio, tutte le righe in cui il valore nella colonna B è uguale al valore più grande di B all'interno di quel gruppo . –

+0

Wow non sapevo nulla di simile all'indicizzazione booleana: è davvero fantastico! Grazie. – jirinovo

10

EDIT: Ho appena imparato un modo molto più ordinato di farlo usando il gruppo .transform con il metodo:

def get_max_rows(df): 
    B_maxes = df.groupby('A').B.transform(max) 
    return df[df.B == B_maxes] 

B_maxes è una serie che identicamente indicizzato dell'originale df contenente il valore massimo di B per ciascun gruppo A. È possibile passare molte funzioni al metodo di trasformazione. Penso che una volta abbiano prodotto o uno scalare o un vettore della stessa lunghezza. È anche possibile passare alcune stringhe come nomi di funzioni comuni come 'median'. Questo è leggermente diverso dal metodo di Paul H in quanto "A" non sarà un indice nel risultato, ma puoi facilmente impostarlo dopo.

import numpy as np 
import pandas as pd 
df_lots_groups = pd.DataFrame(np.random.rand(30000, 3), columns = list('BCD') 
df_lots_groups['A'] = np.random.choice(range(10000), 30000) 

%timeit get_max_rows(df_lots_groups) 
100 loops, best of 3: 2.86 ms per loop 

%timeit df_lots_groups.groupby('A').apply(lambda df: df[ df.B == df.B.max()]) 
1 loops, best of 3: 5.83 s per loop 

EDIT:

Ecco un'astrazione che permette di selezionare le righe da parte di gruppi che utilizzano qualsiasi operatore di confronto valido e qualsiasi metodo groupby valida:

def get_group_rows(df, group_col, condition_col, func=max, comparison='=='): 
    g = df.groupby(group_col)[condition_col] 
    condition_limit = g.transform(func) 
    df.query('condition_col {} @condition_limit'.format(comparison)) 

Così, per esempio, se si desidera tutte le righe sopra il valore B mediano in ciascun gruppo A che chiamate

get_group_rows(df, 'A', 'B', 'median', '>') 

A Alcuni esempi:

%timeit get_group_rows(df_lots_small_groups, 'A', 'B', 'max', '==') 
100 loops, best of 3: 2.84 ms per loop 
%timeit get_group_rows(df_lots_small_groups, 'A', 'B', 'mean', '!=') 
100 loops, best of 3: 2.97 ms per loop 
+3

Ho dovuto ridurre il caffè a causa di come la documentazione sui panda ha fatto alzare la pressione sanguigna ... Posso chiederti dove hai imparato a conoscere questa cosa? Inoltre, permettimi di [collegarti alla pagina di documento 'transform()' (http://pandas.pydata.org/pandas-docs/stable/generated/pandas.core.groupby.GroupBy.transform.html#pandas. core.groupby.GroupBy.transform) – mccc

+2

Adoro Pandas ma i documenti, i messaggi di errore e i test lasciano a desiderare. Non ricordo dove ho visto la prima volta "trasformare" in uso, ma sono abbastanza sicuro che fosse qui su SO. Trovo spesso nuovi modi per risolvere i problemi esaminando le domande e le risposte qui. Se usi 'ipython notebook' puoi usare tab completamento per scansionare i vari metodi, leggere le docstring (non eccezionale, lo so) e solo sperimentare con loro (in questo caso creare un gruppo per oggetto e scansionarne i metodi) – JoeCondron

+0

@ mccc hai bisogno di guardare i documenti scritti da umani e non i riferimenti generati automaticamente: http://pandas.pydata.org/pandas-docs/stable/groupby.html#transformation –

1

Ecco l'altro esempio di: Filtrare le righe con valore massimo dopo un'operazione groupby utilizzando idxmax() e .loc()

In [465]: import pandas as pd 

In [466]: df = pd.DataFrame({ 
       'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2'], 
       'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4'], 
       'value' : [3,2,5,8,10,1]  
       }) 

In [467]: df 
Out[467]: 
    mt sp value 
0 S1 MM1  3 
1 S1 MM1  2 
2 S3 MM1  5 
3 S3 MM2  8 
4 S4 MM2  10 
5 S4 MM2  1 

### Here, idxmax() finds the indices of the rows with max value within groups, 
### and .loc() filters the rows using those indices : 
In [468]: df.loc[df.groupby(["mt"])["value"].idxmax()]                               
Out[468]: 
    mt sp value 
0 S1 MM1  3 
3 S3 MM2  8 
4 S4 MM2  10