Comment regrouper les lignes de la trame de données dans la liste dans les pandas groupby?

274

J'ai une trame de données pandas dfcomme:

a b
A 1
A 2
B 5
B 5
B 4
C 6

Je veux regrouper par la première colonne et obtenir la deuxième colonne sous forme de listes en lignes :

A [1,2]
B [5,5,4]
C [6]

Est-il possible de faire quelque chose comme ça en utilisant des pandas groupby?

Abhishek Thakur
la source

Réponses:

394

Vous pouvez le faire en utilisant groupbypour regrouper sur la colonne d'intérêt, puis apply listpour chaque groupe:

In [1]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6]})
        df

Out[1]: 
   a  b
0  A  1
1  A  2
2  B  5
3  B  5
4  B  4
5  C  6

In [2]: df.groupby('a')['b'].apply(list)
Out[2]: 
a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object

In [3]: df1 = df.groupby('a')['b'].apply(list).reset_index(name='new')
        df1
Out[3]: 
   a        new
0  A     [1, 2]
1  B  [5, 5, 4]
2  C        [6]
EdChum
la source
7
Cela prend beaucoup de temps si l'ensemble de données est énorme, disons 10 millions de lignes. Existe-t-il un moyen plus rapide de procéder? Le nombre d'uniques dans 'a' est cependant d'environ 500k
Abhishek Thakur
6
groupby est notoirement lent et gourmand en mémoire, ce que vous pourriez faire est de trier par colonne A, puis trouver les idxmin et idxmax (probablement les stocker dans un dict) et les utiliser pour découper votre trame de données serait plus rapide je pense
EdChum
1
Lorsque j'ai essayé cette solution avec mon problème (avoir plusieurs colonnes pour groupBy et pour grouper), cela n'a pas fonctionné - les pandas ont envoyé «La fonction ne réduit pas». Ensuite, j'ai utilisé tuplela deuxième réponse ici: stackoverflow.com/questions/19530568/… . Voir la deuxième réponse dans stackoverflow.com/questions/27439023/… pour des explications.
Andarin
Cette solution est bonne, mais existe-t-il un moyen de stocker un ensemble de listes, ce qui signifie que je peux supprimer les doublons puis les stocker?
Sriram Arvind Lakshmanakumar
1
@PoeteMaudit Désolé, je ne comprends pas ce que vous demandez et poser des questions dans les commentaires est une mauvaise forme dans SO. Demandez-vous comment concaténer plusieurs colonnes en une seule liste?
EdChum
47

Si les performances sont importantes, descendez au niveau numpy:

import numpy as np

df = pd.DataFrame({'a': np.random.randint(0, 60, 600), 'b': [1, 2, 5, 5, 4, 6]*100})

def f(df):
         keys, values = df.sort_values('a').values.T
         ukeys, index = np.unique(keys, True)
         arrays = np.split(values, index[1:])
         df2 = pd.DataFrame({'a':ukeys, 'b':[list(a) for a in arrays]})
         return df2

Tests:

In [301]: %timeit f(df)
1000 loops, best of 3: 1.64 ms per loop

In [302]: %timeit df.groupby('a')['b'].apply(list)
100 loops, best of 3: 5.26 ms per loop
BM
la source
8
Comment pourrions-nous l'utiliser si nous groupons par deux ou plusieurs clés, par exemple avec .groupby([df.index.month, df.index.day])au lieu de juste .groupby('a')?
ru111
25

Un moyen pratique d'y parvenir serait:

df.groupby('a').agg({'b':lambda x: list(x)})

Regardez dans l'écriture d'agrégations personnalisées: https://www.kaggle.com/akshaysehgal/how-to-group-by-aggregate-using-py

Anamika Modi
la source
5
lambda args: f(args)équivaut àf
BallpointBen
6
En fait, c'est juste agg(list)assez. Voir aussi ici .
cs95
!! Je cherchais simplement une syntaxe et j'ai réalisé que mon propre ordinateur portable était référencé pour la solution lol. Merci d'avoir lié ceci. Juste pour ajouter, puisque 'list' n'est pas une fonction série, vous devrez soit l'utiliser avec apply df.groupby('a').apply(list)soit l'utiliser avec agg dans le cadre d'un dict df.groupby('a').agg({'b':list}). Vous pouvez également l'utiliser avec lambda (que je recommande) car vous pouvez faire beaucoup plus avec. Exemple: df.groupby('a').agg({'c':'first', 'b': lambda x: x.unique().tolist()})qui vous permet d'appliquer une fonction série au col c et une fonction unique puis liste au col b.
Akshay Sehgal
21

Comme vous le disiez, la groupbyméthode d'un pd.DataFrameobjet peut faire le travail.

Exemple

 L = ['A','A','B','B','B','C']
 N = [1,2,5,5,4,6]

 import pandas as pd
 df = pd.DataFrame(zip(L,N),columns = list('LN'))


 groups = df.groupby(df.L)

 groups.groups
      {'A': [0, 1], 'B': [2, 3, 4], 'C': [5]}

qui donne et une description par index des groupes.

Pour obtenir des éléments de groupes uniques, vous pouvez faire, par exemple

 groups.get_group('A')

     L  N
  0  A  1
  1  A  2

  groups.get_group('B')

     L  N
  2  B  5
  3  B  5
  4  B  4
Acorbe
la source
21

Pour résoudre ce problème pour plusieurs colonnes d'une trame de données:

In [5]: df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6],'c'
   ...: :[3,3,3,4,4,4]})

In [6]: df
Out[6]: 
   a  b  c
0  A  1  3
1  A  2  3
2  B  5  3
3  B  5  4
4  B  4  4
5  C  6  4

In [7]: df.groupby('a').agg(lambda x: list(x))
Out[7]: 
           b          c
a                      
A     [1, 2]     [3, 3]
B  [5, 5, 4]  [3, 4, 4]
C        [6]        [4]

Cette réponse est inspirée de la réponse d' Anamika Modi . Je vous remercie!

Markus Dutschke
la source
12

Utilisez une des conditions suivantes groupbyet aggrecettes.

# Setup
df = pd.DataFrame({
  'a': ['A', 'A', 'B', 'B', 'B', 'C'],
  'b': [1, 2, 5, 5, 4, 6],
  'c': ['x', 'y', 'z', 'x', 'y', 'z']
})
df

   a  b  c
0  A  1  x
1  A  2  y
2  B  5  z
3  B  5  x
4  B  4  y
5  C  6  z

Pour agréger plusieurs colonnes sous forme de listes, utilisez l'une des méthodes suivantes:

df.groupby('a').agg(list)
df.groupby('a').agg(pd.Series.tolist)

           b          c
a                      
A     [1, 2]     [x, y]
B  [5, 5, 4]  [z, x, y]
C        [6]        [z]

Pour lister en groupe une seule colonne, convertissez le groupby en SeriesGroupByobjet, puis appelez SeriesGroupBy.agg. Utilisation,

df.groupby('a').agg({'b': list})  # 4.42 ms 
df.groupby('a')['b'].agg(list)    # 2.76 ms - faster

a
A       [1, 2]
B    [5, 5, 4]
C          [6]
Name: b, dtype: object
cs95
la source
les méthodes ci-dessus sont-elles garanties pour préserver l'ordre? ce qui signifie que les éléments de la même ligne (mais de colonnes différentes, b et c dans votre code ci-dessus) auront le même index dans les listes résultantes?
Kai
@Kai oh, bonne question. Oui et non. GroupBy trie la sortie en fonction des valeurs de clé de groupeur. Cependant, le tri est généralement stable, de sorte que l'ordre relatif par groupe est conservé. Pour désactiver complètement le comportement de tri, utilisez groupby(..., sort=False). Ici, cela ne ferait aucune différence puisque je suis en train de regrouper sur la colonne A qui est déjà triée.
cs95
je suis désolé, je ne comprends pas votre réponse. Pouvez-vous expliquer plus en détail. Je pense que cela mérite sa propre question ..
Kai
1
C'est une très bonne réponse! Existe-t-il également un moyen de rendre les valeurs de la liste uniques? quelque chose comme .agg (pd.Series.tolist.unique) peut-être?
Federico Gentile
1
@FedericoGentile, vous pouvez utiliser un lambda. Voici une façon:df.groupby('a')['b'].agg(lambda x: list(set(x)))
cs95
7

Si vous recherchez une liste unique tout en regroupant plusieurs colonnes, cela pourrait probablement aider:

df.groupby('a').agg(lambda x: list(set(x))).reset_index()
Vanshika
la source
2

Utilisons df.groupbyavec la liste et le Seriesconstructeur

pd.Series({x : y.b.tolist() for x , y in df.groupby('a')})
Out[664]: 
A       [1, 2]
B    [5, 5, 4]
C          [6]
dtype: object
YOBEN_S
la source
2

Il est temps d'utiliser aggau lieu de apply.

Quand

df = pd.DataFrame( {'a':['A','A','B','B','B','C'], 'b':[1,2,5,5,4,6], 'c': [1,2,5,5,4,6]})

Si vous voulez que plusieurs colonnes s’empilent dans la liste, pd.DataFrame

df.groupby('a')[['b', 'c']].agg(list)
# or 
df.groupby('a').agg(list)

Si vous voulez une seule colonne dans la liste, ps.Series

df.groupby('a')['b'].agg(list)
#or
df.groupby('a')['b'].apply(list)

Notez que le résultat pd.DataFrameest environ 10 fois plus lent que le résultat ps.Serieslorsque vous agrégez uniquement une seule colonne, utilisez-la dans le cas de plusieurs colonnes.

Mithril
la source
0

Ici, j'ai regroupé les éléments avec "|" comme séparateur

    import pandas as pd

    df = pd.read_csv('input.csv')

    df
    Out[1]:
      Area  Keywords
    0  A  1
    1  A  2
    2  B  5
    3  B  5
    4  B  4
    5  C  6

    df.dropna(inplace =  True)
    df['Area']=df['Area'].apply(lambda x:x.lower().strip())
    print df.columns
    df_op = df.groupby('Area').agg({"Keywords":lambda x : "|".join(x)})

    df_op.to_csv('output.csv')
    Out[2]:
    df_op
    Area  Keywords

    A       [1| 2]
    B    [5| 5| 4]
    C          [6]
Ganesh Kharad
la source
0

La manière la plus simple que j'ai vue ne permet pas d'obtenir la plupart du même résultat au moins pour une colonne qui est similaire à la réponse d' Anamika uniquement avec la syntaxe de tuple pour la fonction d'agrégation.

df.groupby('a').agg(b=('b','unique'), c=('c','unique'))
Metrd
la source