web-dev-qa-db-fra.com

Python pandas - filtre les lignes après groupby)

Par exemple, j'ai le tableau suivant:

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

Après le 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, c'est de supprimer des 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 pour traduire et formuler ce problème en anglais, voici donc l'exemple:

Valeur maximale des lignes de la colonne B du groupe 0: 8

Je veux donc déposer une ligne avec l'index 0 et conserver les lignes avec des index 1 et 2

Valeur maximale des lignes de la colonne B du groupe 1: 5

Je veux donc déposer une ligne avec l'index 4 et conserver la ligne avec l'index 3

J'ai essayé d'utiliser pandas, mais le problème est qu'il fonctionne sur toutes les lignes du groupe en même temps:

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 parcourt toutes les lignes du groupe.

Merci pour l'aide!

P.S. Existe-t-il également un moyen de supprimer uniquement les lignes des groupes et de ne pas renvoyer d'objet DataFrame?

37
jirinovo

Il vous suffit d'utiliser apply sur l'objet groupby. J'ai modifié vos données d'exemple pour le rendre 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 imprime:

         A  B
A index      
1 2      1  6
2 4      2  7
43
Paul H

EDIT: Je viens d'apprendre une façon beaucoup plus nette de le faire en utilisant le .transform grouper par 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 est identifiée à l'identique comme l'original df contenant la valeur maximale de B pour chaque groupe A. Vous pouvez transmettre de nombreuses fonctions à la méthode de transformation. Je pense qu'une fois qu'ils ont sorti soit comme un scalaire ou un vecteur de la même longueur. Vous pouvez même passer des chaînes en tant que noms de fonction courants comme 'median'. Ceci est légèrement différent de la méthode de Paul H dans la mesure où 'A' ne sera pas un index dans le résultat, mais vous pouvez facilement le définir 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

ÉDITER:

Voici une abstraction qui vous permet de sélectionner des lignes dans des groupes à l'aide de tout opérateur de comparaison valide et de toute méthode groupby valide:

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 au-dessus de la valeur B médiane dans chaque groupe A que 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
19
JoeCondron

Voici l'autre exemple pour: Filtrage des lignes avec une valeur maximale après une 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
5
Surya

Toutes ces réponses sont bonnes mais je voulais ce qui suit:

(DataframeGroupby object) --> filter some rows out --> (DataframeGroupby object)

Haussement d'épaules, il semble que ce soit plus difficile et plus intéressant que ce à quoi je m'attendais. Donc, cette doublure accomplit ce que je voulais, mais ce n'est probablement pas le moyen le plus efficace :)

gdf.apply(lambda g: g[g['team'] == 'A']).reset_index(drop=True).groupby(gdf.grouper.names) 

Exemple de code de travail:

import pandas as pd

def print_groups(gdf): 
    for name, g in gdf: 
        print('\n'+name) 
        print(g)

df = pd.DataFrame({'name': ['sue', 'jim', 'ted', 'moe'],
                   'team': ['A', 'A', 'B', 'B'], 
                   'fav_food': ['tacos', 'steak', 'tacos', 'steak']})                               

gdf = df.groupby('fav_food')                                                                                                                                           
print_groups(gdf)                                                                                                                                                      

    steak
        name team fav_food
    1  jim    A    steak
    3  moe    B    steak

    tacos
        name team fav_food
    0  sue    A    tacos
    2  ted    B    tacos

fgdf = gdf.apply(lambda g: g[g['team'] == 'A']).reset_index(drop=True).groupby(gdf.grouper.names)                                                                      
print_groups(fgdf)                                                                                                                                                     

    steak
      name team fav_food
    0  jim    A    steak

    tacos
      name team fav_food
    1  sue    A    tacos
0
Briford Wylie