1. 问题
有如下一个dataframe,打算对a
的每一个类别求b
的众数(mode),dir(df.groupby('a'))
可以看到是没有mode
函数的,因此不能直接使用df.groupby('a').mode().reset_index()
>>> import pandas as pd
>>> df = pd.DataFrame({'a':['A','A','A','A','B','B','B','B','B'],'b':[1,1,2,3,1,2,2,3,3]})
>>> df
a b
0 A 1
1 A 1
2 A 2
3 A 3
4 B 1
5 B 2
6 B 2
7 B 3
8 B 3
>>> df.groupby('a').mode().reset_index()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\site-packages\pandas\core\groupby.py", line 548, in __getattr__
return self._make_wrapper(attr)
File "C:\Python27\lib\site-packages\pandas\core\groupby.py", line 562, in _make_wrapper
raise AttributeError(msg)
AttributeError: Cannot access callable attribute 'mode' of 'DataFrameGroupBy' objects, try using the 'apply' method
>>> type(df.groupby('a'))
<class 'pandas.core.groupby.DataFrameGroupBy'>
>>> dir(df.groupby('a'))
['__bytes__', '__class__', ... ... 'std', 'sum', 'tail', 'take', 'transform', 'tshift', 'var']
2. 解决方案
既然df.groupby('a')
没有mode
函数,又考虑到可能聚合函数agg
能够接用外部函数,搜索网上的解决办法,经过尝试,在此总结并列出以下几种解决方案(当然也可以直接跳到最后一个解决方案)。
使用
scipy.stats.mode()
:df中的B类别有两个众数,返回的结果B类别的众数取了较小的结果>>> from scipy import stats >>> df.groupby('a').agg(lambda x: stats.mode(x)[0][0]).reset_index() a b 0 A 1 1 B 2
使用
value_counts()
(1) 先看value_counts()
的作用:可以看到得到的结果中的index
是取值,内容是计数,并且index
是降序排列的,因此取index[0]
是取最大值,因此有两个众数以上的时候,会取到较大的结果>>> ss = pd.Series([1,2,2,3,3]) >>> ss 0 1 1 2 2 2 3 3 4 3 dtype: int64 >>> ss.value_counts() 3 2 2 2 1 1 dtype: int64 >>> ss.value_counts().index[0] 3
(2) 应用到dataframe的groupby之后的聚合函数中:
>>> df.groupby('a').agg(lambda x: x.value_counts().index[0]).reset_index() a b 0 A 1 1 B 3
使用
pd.Series.mode()
:该函数是返回Series
的众数的,当众数有多个时,会返回一个list
,里面包含了所有众数>>> df.groupby('a').agg(pd.Series.mode).reset_index() a b 0 A 1 1 B [2, 3]
使用
pd.Series.mode()
和np.mean()
:上述结果显然不是我想要的,但是如果对有多个众数的结果取均值作为新的众数,是较为满意的结果,则可以这样>>> import numpy as np >>> df.groupby('a').agg(lambda x: np.mean(pd.Series.mode(x))).reset_index() a b 0 A 1.0 1 B 2.5