web-dev-qa-db-fra.com

python pandas, DF.groupby (). agg (), référence de colonne dans agg ()

Sur un problème concret, disons que j'ai un DataFrame DF

     Word  tag count
0    a     S    30
1    the   S    20
2    a     T    60
3    an    T    5
4    the   T    10 

Je veux trouver, pour chaque "mot", le "tag" qui a le plus "compte". Donc, le retour serait quelque chose comme

     Word  tag count
1    the   S    20
2    a     T    60
3    an    T    5

Je ne me soucie pas de la colonne de compte ou si la commande/Index est original ou foiré. Retourner un dictionnaire {'the': 'S', ...} est parfait.

J'espère que je peux faire

DF.groupby(['Word']).agg(lambda x: x['tag'][ x['count'].argmax() ] )

mais ça ne marche pas. Je ne peux pas accéder aux informations de la colonne.

Plus abstraitement, que fait la fonction dans agg () ) voir comme son argument?

au fait, est-ce que .agg () est identique à .aggregate ()?

Merci beaucoup.

41
jf328

agg est identique à aggregate. Son appelable est passé les colonnes (Series objets) du DataFrame, un à la fois.


Vous pouvez utiliser idxmax pour collecter les étiquettes d'index des lignes avec le nombre maximal:

idx = df.groupby('Word')['count'].idxmax()
print(idx)

les rendements

Word
a       2
an      3
the     1
Name: count

puis utilisez loc pour sélectionner ces lignes dans les colonnes Word et tag:

print(df.loc[idx, ['Word', 'tag']])

les rendements

  Word tag
2    a   T
3   an   T
1  the   S

Notez que idxmax renvoie l'index labels. df.loc peut être utilisé pour sélectionner des lignes par étiquette. Mais si l’index n’est pas unique, c’est-à-dire s’il existe des lignes avec des étiquettes d’index dupliquées, alors df.loc sélectionnera toutes les lignes avec les étiquettes listées dans idx. Alors faites attention que df.index.is_unique est True si vous utilisez idxmax avec df.loc


Alternative, vous pouvez utiliser apply. L'appelable de apply reçoit un sous-DataFrame qui vous donne accès à toutes les colonnes:

import pandas as pd
df = pd.DataFrame({'Word':'a the a an the'.split(),
                   'tag': list('SSTTT'),
                   'count': [30, 20, 60, 5, 10]})

print(df.groupby('Word').apply(lambda subf: subf['tag'][subf['count'].idxmax()]))

les rendements

Word
a       T
an      T
the     S

L'utilisation de idxmax et loc est généralement plus rapide que apply, en particulier pour les grands DataFrames. Utilisation de% timeit d'IPython:

N = 10000
df = pd.DataFrame({'Word':'a the a an the'.split()*N,
                   'tag': list('SSTTT')*N,
                   'count': [30, 20, 60, 5, 10]*N})
def using_apply(df):
    return (df.groupby('Word').apply(lambda subf: subf['tag'][subf['count'].idxmax()]))

def using_idxmax_loc(df):
    idx = df.groupby('Word')['count'].idxmax()
    return df.loc[idx, ['Word', 'tag']]

In [22]: %timeit using_apply(df)
100 loops, best of 3: 7.68 ms per loop

In [23]: %timeit using_idxmax_loc(df)
100 loops, best of 3: 5.43 ms per loop

Si vous voulez un dictionnaire mappant des mots en balises, vous pouvez utiliser set_index et to_dict comme ça:

In [36]: df2 = df.loc[idx, ['Word', 'tag']].set_index('Word')

In [37]: df2
Out[37]: 
     tag
Word    
a      T
an     T
the    S

In [38]: df2.to_dict()['tag']
Out[38]: {'a': 'T', 'an': 'T', 'the': 'S'}
68
unutbu

Voici un moyen simple de déterminer ce qui est en train d'être adopté (la solution unutbu), puis d'appliquer la solution!

In [33]: def f(x):
....:     print type(x)
....:     print x
....:     

In [34]: df.groupby('Word').apply(f)
<class 'pandas.core.frame.DataFrame'>
  Word tag  count
0    a   S     30
2    a   T     60
<class 'pandas.core.frame.DataFrame'>
  Word tag  count
0    a   S     30
2    a   T     60
<class 'pandas.core.frame.DataFrame'>
  Word tag  count
3   an   T      5
<class 'pandas.core.frame.DataFrame'>
  Word tag  count
1  the   S     20
4  the   T     10

votre fonction ne fait qu'opérer (dans ce cas) sur une sous-section du cadre avec la variable groupée ayant tous la même valeur (dans ce cas, 'Word'), si vous passez une fonction, vous devez alors gérer l'agrégation des colonnes potentiellement non chaîne; les fonctions standard, comme 'sum' le font pour vous

Automatiquement ne pas agréger sur les colonnes de chaîne

In [41]: df.groupby('Word').sum()
Out[41]: 
      count
Word       
a        90
an        5
the      30

Vous agrégez sur toutes les colonnes

In [42]: df.groupby('Word').apply(lambda x: x.sum())
Out[42]: 
        Word tag count
Word                  
a         aa  ST    90
an        an   T     5
the   thethe  ST    30

Vous pouvez faire à peu près n'importe quoi dans la fonction

In [43]: df.groupby('Word').apply(lambda x: x['count'].sum())
Out[43]: 
Word
a       90
an       5
the     30
17
Jeff