web-dev-qa-db-fra.com

La valeur Groupby compte sur le bloc de données pandas

J'ai le dataframe suivant:

df = pd.DataFrame([
    (1, 1, 'term1'),
    (1, 2, 'term2'),
    (1, 1, 'term1'),
    (1, 1, 'term2'),
    (2, 2, 'term3'),
    (2, 3, 'term1'),
    (2, 2, 'term1')
], columns=['id', 'group', 'term'])

Je veux le regrouper par id et group et calculer le nombre de chaque terme pour cet identifiant, paire de groupes.

Donc à la fin je vais obtenir quelque chose comme ça:

enter image description here

J'ai pu réaliser ce que je voulais en parcourant toutes les lignes avec df.iterrows() et en créant un nouveau cadre de données, mais cela est clairement inefficace. (Si cela peut aider, je connais la liste de tous les termes à l’avance et il y en a environ 10).

Il semble que je doive regrouper puis compter les valeurs. J'ai donc essayé avec df.groupby(['id', 'group']).value_counts(), qui ne fonctionne pas car value_counts opère sur la série groupby et non sur une base de données.

Quoi qu'il en soit, je peux y arriver sans boucler?

34
Salvador Dali

J'utilise groupby et size

df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0)

enter image description here


Timing

enter image description here

1 000 000 de lignes

df = pd.DataFrame(dict(id=np.random.choice(100, 1000000),
                       group=np.random.choice(20, 1000000),
                       term=np.random.choice(10, 1000000)))

enter image description here

58
piRSquared

en utilisant la méthode pivot_table () :

In [22]: df.pivot_table(index=['id','group'], columns='term', aggfunc='size', fill_value=0)
Out[22]:
term      term1  term2  term3
id group
1  1          2      1      0
   2          0      1      0
2  2          1      0      1
   3          1      0      0

Timing contre 700K lignes DF:

In [24]: df = pd.concat([df] * 10**5, ignore_index=True)

In [25]: df.shape
Out[25]: (700000, 3)

In [3]: %timeit df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0)
1 loop, best of 3: 226 ms per loop

In [4]: %timeit df.pivot_table(index=['id','group'], columns='term', aggfunc='size', fill_value=0)
1 loop, best of 3: 236 ms per loop

In [5]: %timeit pd.crosstab([df.id, df.group], df.term)
1 loop, best of 3: 355 ms per loop

In [6]: %timeit df.groupby(['id','group','term'])['term'].size().unstack().fillna(0).astype(int)
1 loop, best of 3: 232 ms per loop

In [7]: %timeit df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0)
1 loop, best of 3: 231 ms per loop

Timing contre 7M lignes DF:

In [9]: df = pd.concat([df] * 10, ignore_index=True)

In [10]: df.shape
Out[10]: (7000000, 3)

In [11]: %timeit df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0)
1 loop, best of 3: 2.27 s per loop

In [12]: %timeit df.pivot_table(index=['id','group'], columns='term', aggfunc='size', fill_value=0)
1 loop, best of 3: 2.3 s per loop

In [13]: %timeit pd.crosstab([df.id, df.group], df.term)
1 loop, best of 3: 3.37 s per loop

In [14]: %timeit df.groupby(['id','group','term'])['term'].size().unstack().fillna(0).astype(int)
1 loop, best of 3: 2.28 s per loop

In [15]: %timeit df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0)
1 loop, best of 3: 1.89 s per loop
12
MaxU

Au lieu de vous rappeler de longues solutions, que diriez-vous de celle que pandas a construite pour vous:

df.groupby(['id', 'group', 'term']).count()
9
A.Kot

Vous pouvez utiliser crosstab :

print (pd.crosstab([df.id, df.group], df.term))
term      term1  term2  term3
id group                     
1  1          2      1      0
   2          0      1      0
2  2          1      0      1
   3          1      0      0

Une autre solution avec groupby avec agrégation size , remodelant par unstack :

df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0)

term      term1  term2  term3
id group                     
1  1          2      1      0
   2          0      1      0
2  2          1      0      1
   3          1      0      0

Timings :

df = pd.concat([df]*10000).reset_index(drop=True)

In [48]: %timeit (df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0))
100 loops, best of 3: 12.4 ms per loop

In [49]: %timeit (df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0))
100 loops, best of 3: 12.2 ms per loop
5
jezrael