Python pandas-filtrer les lignes après groupby

Par exemple, j'ai tableau suivant:

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

Après regroupement par A:

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

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

Ce dont j'ai besoin est de supprimer les lignes de chaque groupe, où le nombre dans la colonne B est inférieur à la valeur maximale de toutes les lignes de la colonne du groupe B. Eh bien, j'ai un problème de traduction et de formulation de ce problème en anglais donc voici l'exemple:

valeur maximale des lignes de la colonne B dans le groupe 0: 8

je tiens Donc à baisse de ligne avec index 0 et de garder les lignes avec index 1 et 2

valeur maximale des lignes de la colonne B dans le groupe 1: 5

je tiens Donc à baisse de ligne avec index 4 et garder la ligne avec index 3

j'ai essayé d'utiliser la fonction de filtre pandas, mais le problème est qu'elle fonctionne sur toutes les lignes dans le groupe à un heure:

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

donc ce dont j'ai idéalement besoin c'est d'un filtre, qui itère à travers toutes les lignes du groupe.

Merci pour votre aide!

P. S. Est-il aussi moyen uniquement supprimer des lignes dans les groupes, et ne pas retourner DataFrame objet?

26
demandé sur jirinovo 2014-12-15 18:59:19

3 réponses

Vous avez juste besoin d'utiliser objet. J'ai modifié vos données d'exemple pour rendre cela un peu plus clair:

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

Qui affiche:

         A  B
A index      
1 2      1  6
2 4      2  7
30
répondu Paul H 2014-12-15 17:05:39

EDIT: je viens d'apprendre une bien plus propre façon de le faire à l'aide de la .transform groupe par la méthode:

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

B_maxes est une série qui à l'identique indexée comme l'original df contenant la valeur maximale de B pour chaque A groupe. Vous pouvez passer beaucoup de fonctions de la méthode de transformation. Je pense qu'une fois qu'ils ont une sortie comme un scalaire ou un vecteur de même longueur. Vous pouvez même passer quelques chaînes comme des noms de fonctions communs comme 'median'. C'est un peu différent de la méthode de Paul H dans ce " A " ne sera pas un index dans le résultat, mais vous pouvez facilement définir cela après.

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:

Voici une abstraction qui vous permet de sélectionner des lignes à partir de groupes en utilisant n'importe quel opérateur de comparaison valide et n'importe quelle méthode valide groupby:

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

ainsi, par exemple, si vous voulez que toutes les lignes se situent au-dessus de la valeur médiane B de chaque groupe A, vous appelez

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

quelques exemples:

%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
13
répondu JoeCondron 2015-09-22 10:40:27

Voici l'autre exemple pour : filtrage des lignes avec valeur maximale après l'opération groupby en utilisant idxmax() et .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
2
répondu Surya 2017-07-07 18:02:47