本文的目的在于指导读者快速上手groupby。在每个知识点下,我会先说明用法,再给出示例。本文只给出了基本的用法,想要获得更详尽的内容请参阅文档:
Group by: split-apply-combine
10.1 分组
分组依据可以是:
- 函数,会在各个索引值上被调用一次,其返回值就会被用作分组名称
- numpy array
- 字典或Series
- 列名
- 以上类型的混合组成的列表
示例
In [1]: df = pd.DataFrame(
...: [
...: ("bird", "Falconiformes", 389.0),
...: ("bird", "Psittaciformes", 24.0),
...: ("mammal", "Carnivora", 80.2),
...: ("mammal", "Primates", np.nan),
...: ("mammal", "Carnivora", 58),
...: ],
...: index=["falcon", "parrot", "lion", "monkey", "leopard"],
...: columns=("class", "order", "max_speed"),
...: )
...:
In [2]: df
Out[2]:
class order max_speed
falcon bird Falconiformes 389.0
parrot bird Psittaciformes 24.0
lion mammal Carnivora 80.2
monkey mammal Primates NaN
leopard mammal Carnivora 58.0
# default is axis=0
In [3]: grouped = df.groupby("class")
In [4]: grouped = df.groupby("order", axis="columns")
In [5]: grouped = df.groupby(["class", "order"])
10.2 查看分组
grouped.first()
,注意并不是查看第一个分组,而是查看每个分组的第一个元素grouped.last()
,同理grouped.nth(2)
,同理,查看每个分组的第二个元素grouped.get_group("A")
,查看指定分组dict(list(grouped))
,把分组转化为字典,可以方便查看。
示例
In [29]: df3 = pd.DataFrame({"X": ["A", "B", "A", "B","A","B"], "Y": [1, 4, 3, 2,4,120]})
In [30]: grouped=df3.groupby('X')
In [31]: grouped.last()
Out[31]:
Y
X
A 4
B 120
In [32]: df3
Out[32]:
X Y
0 A 1
1 B 4
2 A 3
3 B 2
4 A 4
5 B 120
In [33]: grouped.get_group("A")
Out[33]:
Y
0 1
2 3
4 4
In [34]: d=dict(list(grouped))
In [35]: d
Out[35]:
{'A': X Y
0 A 1
2 A 3
4 A 4,
'B': X Y
1 B 4
3 B 2
5 B 120}
In [37]: d["A"]
Out[37]:
X Y
0 A 1
2 A 3
4 A 4
10.3 聚合Aggregation 1
聚合:计算统计量,把组内数据聚合成一个标量,比如计算组内均值。
形如grouped.agg(func)
示例
In [62]: grouped = df.groupby('A')
In [63]: for name, group in grouped:
....: print(name)
....: print(group)
....:
bar
A B C D
1 bar one 0.254161 1.511763
3 bar three 0.215897 -0.990582
5 bar two -0.077118 1.211526
foo
A B C D
0 foo one -0.575247 1.346061
2 foo two -1.143704 1.627081
4 foo two 1.193555 -0.441652
6 foo one -0.408530 0.268520
7 foo three -0.862495 0.024580
In [68]: grouped.aggregate(np.sum)
Out[68]:
C D
A
bar 0.392940 1.732707
foo -1.796421 2.824590
对于一些常用的统计函数,groupby
对象可以直接使用,形如grouped.sum()
。
常用的聚合函数:
函数 | 说明 |
mean() | |
sum() | |
size() | 返回组内数据个数 |
count() | 返回每个位置数据的个数,具体的看上边的演示 |
std() | |
var() | |
sem() | 标准误,即样本均数的标准差,是描述均数抽样分布的离散程度及衡量均数抽样误差大小的尺度,反映的是样本均数之间的变异。标准误不是标准差,是多个样本平均数的标准差。标准误用来衡量抽样误差。标准误越小,表明样本统计量与总体参数的值越接近,样本对总体越有代表性,用样本统计量推断总体参数的可靠度越大。因此,标准误是统计推断可靠性的指标。 |
describe() | |
first() | 取每个分组的第一条数据 |
last() | |
nth() | 取每个分组的第n条数据,如果n是列表则返回一个子集 |
min() | |
max() | |
nunique() | 返回每个位置unique元素的个数 |
更详尽的见10.6节
10.4 转换Transformation
转换:输入的形状和输出的形状一致。比如标准化组内数据。
转换会返回一个和分组大小相同的对象。不要在分组对象上原地操作,因为改变分组对象可能会产生意外的效果。
示例
In [42]: index = pd.date_range("10/1/1999", periods=10)
In [44]: ts = pd.Series(np.random.normal(0.5, 2, 10), index)
In [46]: ts
Out[46]:
1999-10-01 3.557378
1999-10-02 -0.483196
1999-10-03 3.285124
1999-10-04 -0.745478
1999-10-05 -2.002660
1999-10-06 3.390502
1999-10-07 0.946926
1999-10-08 2.007789
1999-10-09 1.848048
1999-10-10 -0.793525
Freq: D, dtype: float64
In [47]: transformed = ts.groupby(lambda x: x.year).transform(
...: .....: lambda x: (x - x.mean()) / x.std()
...: .....: )
...: .....:
In [48]: transformed
Out[48]:
1999-10-01 1.218182
1999-10-02 -0.785718
1999-10-03 1.083159
1999-10-04 -0.915795
1999-10-05 -1.539288
1999-10-06 1.135421
1999-10-07 -0.076457
1999-10-08 0.449672
1999-10-09 0.370449
1999-10-10 -0.939624
Freq: D, dtype: float64
如果转换函数输出标量,会自动广播到整个输入数组。
In [49]: max = ts.groupby(lambda x: x.year).transform("max")
In [50]: max
Out[50]:
1999-10-01 3.557378
1999-10-02 3.557378
1999-10-03 3.557378
1999-10-04 3.557378
1999-10-05 3.557378
1999-10-06 3.557378
1999-10-07 3.557378
1999-10-08 3.557378
1999-10-09 3.557378
1999-10-10 3.557378
Freq: D, dtype: float64
In [51]: ts.groupby(lambda x: x.year).transform(lambda x: x.max() - x.min())
Out[51]:
1999-10-01 5.560038
1999-10-02 5.560038
1999-10-03 5.560038
1999-10-04 5.560038
1999-10-05 5.560038
1999-10-06 5.560038
1999-10-07 5.560038
1999-10-08 5.560038
1999-10-09 5.560038
1999-10-10 5.560038
Freq: D, dtype: float64
还有一个常用用法是填补na
In [121]: transformed = grouped.transform(lambda x: x.fillna(x.mean()))
10.5 过滤Filtration
过滤:根据计算结果丢弃一些组,比如根据组内均值过滤掉一些组。而不是丢弃组内的一些值。
filter
函数返回原始对象的子集。filter
函数的参数必须是应用在每个分组,返回True
或False
的函数。
示例
In [136]: sf = pd.Series([1, 1, 2, 3, 3, 3])
In [137]: sf.groupby(sf).filter(lambda x: x.sum() > 2)
Out[137]:
3 3
4 3
5 3
dtype: int64
In [138]: dff = pd.DataFrame({"A": np.arange(8), "B": list("aabbbbcc")})
In [139]: dff.groupby("B").filter(lambda x: len(x) > 2)
Out[139]:
A B
2 2 b
3 3 b
4 4 b
5 5 b
10.6 聚合Aggregation 2
10.6.1 一次应用多个函数
你可以对groupby对象一次应用多个函数。
示例
注意区别groupby对象分别是Series
和Dataframe
。
In [81]: grouped = df.groupby("A")
In [82]: grouped["C"].agg([np.sum, np.mean, np.std])
Out[82]:
sum mean std
A
bar 0.392940 0.130980 0.181231
foo -1.796421 -0.359284 0.912265
In [83]: grouped.agg([np.sum, np.mean, np.std])
Out[83]:
C D
sum mean std sum mean std
A
bar 0.392940 0.130980 0.181231 1.732707 0.577569 1.366330
foo -1.796421 -0.359284 0.912265 2.824590 0.564918 0.884785
可以更改聚合后的列名
In [85]: (
....: grouped.agg([np.sum, np.mean, np.std]).rename(
....: columns={"sum": "foo", "mean": "bar", "std": "baz"}
....: )
....: )
....:
Out[85]:
C D
foo bar baz foo bar baz
A
bar 0.392940 0.130980 0.181231 1.732707 0.577569 1.366330
foo -1.796421 -0.359284 0.912265 2.824590 0.564918 0.884785
10.6.2 具名聚合Named aggregation
可以对具体的列使用不同的函数:
In [88]: animals = pd.DataFrame(
....: {
....: "kind": ["cat", "dog", "cat", "dog"],
....: "height": [9.1, 6.0, 9.5, 34.0],
....: "weight": [7.9, 7.5, 9.9, 198.0],
....: }
....: )
....:
In [89]: animals
Out[89]:
kind height weight
0 cat 9.1 7.9
1 dog 6.0 7.5
2 cat 9.5 9.9
3 dog 34.0 198.0
In [90]: animals.groupby("kind").agg(
....: min_height=pd.NamedAgg(column="height", aggfunc="min"),
....: max_height=pd.NamedAgg(column="height", aggfunc="max"),
....: average_weight=pd.NamedAgg(column="weight", aggfunc=np.mean),
....: )
....:
Out[90]:
min_height max_height average_weight
kind
cat 9.1 9.5 8.90
dog 6.0 34.0 102.75
10.6.3 对不同列使用不同聚合函数
In [95]: grouped.agg({"C": "sum", "D": "std"})
Out[95]:
C D
A
bar 0.392940 1.366330
foo -1.796421 0.884785