6 过滤
filter
方法可以返回原始对象的子集.
例如,我们想提取分组内的和大于 3
的所有分组的元素
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
filter
的参数必须是一个函数,函数参数是每个分组,并且返回 True
或 False
例如,提取元素个数大于 2
的分组
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
另外,我们也可以过滤掉不满足条件的组,而是返回一个类似索引对象。在这个对象中,没有通过的分组的元素被 NaN
填充
In [140]: dff.groupby("B").filter(lambda x: len(x) > 2, dropna=False)
Out[140]:
A B
0 NaN NaN
1 NaN NaN
2 2.0 b
3 3.0 b
4 4.0 b
5 5.0 b
6 NaN NaN
7 NaN NaN
对于具有多列的 DataFrames
,过滤器应明确指定一列作为过滤条件
In [141]: dff["C"] = np.arange(8)
In [142]: dff.groupby("B").filter(lambda x: len(x["C"]) > 2)
Out[142]:
A B C
2 2 b 2
3 3 b 3
4 4 b 4
5 5 b 5
7 分派实例方法
在进行聚合或转换时,你可能想对每个分组调用一个实例方法,例如
In [144]: grouped = df.groupby("A")
In [145]: grouped.agg(lambda x: x.std())
Out[145]:
C D
A
bar 0.181231 1.366330
foo 0.912265 0.884785
但是,如果需要传递额外的参数时,它会变得很冗长。我们可以直接使用分派到组对象上的方法
In [146]: grouped.std()
Out[146]:
C D
A
bar 0.181231 1.366330
foo 0.912265 0.884785
实际上这生成了一个函数包装器,在调用时,它接受所有传递的参数,并在每个分组上进行调用。
然后,这个结果可以和 agg
和 transform
结合在一起使用
In [147]: tsdf = pd.DataFrame(
.....: np.random.randn(1000, 3),
.....: index=pd.date_range("1/1/2000", periods=1000),
.....: columns=["A", "B", "C"],
.....: )
.....:
In [148]: tsdf.iloc[::2] = np.nan
In [149]: grouped = tsdf.groupby(lambda x: x.year)
In [150]: grouped.fillna(method="pad")
Out[150]:
A B C
2000-01-01 NaN NaN NaN
2000-01-02 -0.353501 -0.080957 -0.876864
2000-01-03 -0.353501 -0.080957 -0.876864
2000-01-04 0.050976 0.044273 -0.559849
2000-01-05 0.050976 0.044273 -0.559849
... ... ... ...
2002-09-22 0.005011 0.053897 -1.026922
2002-09-23 0.005011 0.053897 -1.026922
2002-09-24 -0.456542 -1.849051 1.559856
2002-09-25 -0.456542 -1.849051 1.559856
2002-09-26 1.123162 0.354660 1.128135
[1000 rows x 3 columns]
在上面的例子中,我们按照年份分组,然后对每个分组中使用 fillna
补缺失值
nlargest
和 nsmallest
可以在 Series
类型的 groupby
上使用
In [151]: s = pd.Series([9, 8, 7, 5, 19, 1, 4.2, 3.3])
In [152]: g = pd.Series(list("abababab"))
In [153]: gb = s.groupby(g)
In [154]: gb.nlargest(3)
Out[154]:
a 4 19.0
0 9.0
2 7.0
b 1 8.0
3 5.0
7 3.3
dtype: float64
In [155]: gb.nsmallest(3)
Out[155]:
a 6 4.2
2 7.0
0 9.0
b 5 1.0
7 3.3
3 5.0
dtype: float64
8 灵活的 apply
对分组数据的某些操作可能并不适合聚合或转换。或者说,你可能只是想让 GroupBy
来推断如何合并结果
我们可以使用 apply
函数,例如
In [156]: df
Out[156]:
A B C D
0 foo one -0.575247 1.346061
1 bar one 0.254161 1.511763
2 foo two -1.143704 1.627081
3 bar three 0.215897 -0.990582
4 foo two 1.193555 -0.441652
5 bar two -0.077118 1.211526
6 foo one -0.408530 0.268520
7 foo three -0.862495 0.024580
In [157]: grouped = df.groupby("A")
# 也可以直接使用 .describe()
In [158]: grouped["C"].apply(lambda x: x.describe())
Out[158]:
A
bar count 3.000000
mean 0.130980
std 0.181231
min -0.077118
25% 0.069390
...
foo min -1.143704
25% -0.862495
50% -0.575247
75% -0.408530
max 1.193555
Name: C, Length: 16, dtype: float64
改变返回结果的维度
In [159]: grouped = df.groupby('A')['C']
In [160]: def f(group):
.....: return pd.DataFrame({'original': group,
.....: 'demeaned': group - group.mean()})
.....:
In [161]: grouped.apply(f)
Out[161]:
original demeaned
0 -0.575247 -0.215962
1 0.254161 0.123181
2 -1.143704 -0.784420
3 0.215897 0.084917
4 1.193555 1.552839
5 -0.077118 -0.208098
6 -0.408530 -0.049245
7 -0.862495 -0.503211
在 Series
上使用 apply
类似
In [162]: def f(x):
.....: return pd.Series([x, x ** 2], index=["x", "x^2"])
.....:
In [163]: s = pd.Series(np.random.rand(5))
In [164]: s
Out[164]:
0 0.321438
1 0.493496
2 0.139505
3 0.910103
4 0.194158
dtype: float64
In [165]: s.apply(f)
Out[165]:
x x^2
0 0.321438 0.103323
1 0.493496 0.243538
2 0.139505 0.019462
3 0.910103 0.828287
4 0.194158 0.037697
9 其他有用的特征
9.1 自动排除某些列
对于之前的示例数据
In [166]: df
Out[166]:
A B C D
0 foo one -0.575247 1.346061
1 bar one 0.254161 1.511763
2 foo two -1.143704 1.627081
3 bar three 0.215897 -0.990582
4 foo two 1.193555 -0.441652
5 bar two -0.077118 1.211526
6 foo one -0.408530 0.268520
7 foo three -0.862495 0.024580
假设,我们想按 A
分组并计算组内的标准差,但是 B
列的数据我们并不关心。
如果我们的函数不能应用于某些列,则会隐式的删除这些列,所以
In [167]: df.groupby("A").std()
Out[167]:
C D
A
bar 0.181231 1.366330
foo 0.912265 0.884785
直接计算标准差并不会报错
9.2 使用有序因子进行分组
可以使用分类变量进行分组,分组的顺序会按照分类变量的顺序
In [177]: data = pd.Series(np.random.randn(100))
In [178]: factor = pd.qcut(data, [0, 0.25, 0.5, 0.75, 1.0])
In [179]: data.groupby(factor).mean()
Out[179]:
(-2.645, -0.523] -1.362896
(-0.523, 0.0296] -0.260266
(0.0296, 0.654] 0.361802
(0.654, 2.21] 1.073801
dtype: float64
9.3 使用 grouper 分组
可以使用 pd.Grouper
控制分组,对于如下数据
In [180]: import datetime
In [181]: df = pd.DataFrame(
.....: {
.....: "Branch": "A A A A A A A B".split(),
.....: "Buyer": "Carl Mark Carl Carl Joe Joe Joe Carl".split(),
.....: "Quantity": [1, 3, 5, 1, 8, 1, 9, 3],
.....: "Date": [
.....: datetime.datetime(2013, 1, 1, 13, 0),
.....: datetime.datetime(2013, 1, 1, 13, 5),
.....: datetime.datetime(2013, 10, 1, 20, 0),
.....: datetime.datetime(2013, 10, 2, 10, 0),
.....: datetime.datetime(2013, 10, 1, 20, 0),
.....: datetime.datetime(2013, 10, 2, 10, 0),
.....: datetime.datetime(2013, 12, 2, 12, 0),
.....: datetime.datetime(2013, 12, 2, 14, 0),
.....: ],
.....: }
.....: )
.....:
In [182]: df
Out[182]:
Branch Buyer Quantity Date
0 A Carl 1 2013-01-01 13:00:00
1 A Mark 3 2013-01-01 13:05:00
2 A Carl 5 2013-10-01 20:00:00
3 A Carl 1 2013-10-02 10:00:00
4 A Joe 8 2013-10-01 20:00:00
5 A Joe 1 2013-10-02 10:00:00
6 A Joe 9 2013-12-02 12:00:00
7 B Carl 3 2013-12-02 14:00:00
可以按照一定的频率对特定列进行分组,就像重抽样一样
In [183]: df.groupby([pd.Grouper(freq="1M", key="Date"), "Buyer"]).sum()
Out[183]:
Quantity
Date Buyer
2013-01-31 Carl 1
Mark 3
2013-10-31 Carl 6
Joe 9
2013-12-31 Carl 3
Joe 9
可以分别对列或索引进行分组
In [184]: df = df.set_index("Date")
In [185]: df["Date"] = df.index + pd.offsets.MonthEnd(2)
In [186]: df.groupby([pd.Grouper(freq="6M", key="Date"), "Buyer"]).sum()
Out[186]:
Quantity
Date Buyer
2013-02-28 Carl 1
Mark 3
2014-02-28 Carl 9
Joe 18
In [187]: df.groupby([pd.Grouper(freq="6M", level="Date"), "Buyer"]).sum()
Out[187]:
Quantity
Date Buyer
2013-01-31 Carl 1
Mark 3
2014-01-31 Carl 9
Joe 18
9.4 获取分组的第一行
类似于 Series
和 DataFrame
,可以使用 head
和 tail
获取分组前后几行
In [188]: df = pd.DataFrame([[1, 2], [1, 4], [5, 6]], columns=["A", "B"])
In [189]: df
Out[189]:
A B
0 1 2
1 1 4
2 5 6
In [190]: g = df.groupby("A")
In [191]: g.head(1)
Out[191]:
A B
0 1 2
2 5 6
In [192]: g.tail(1)
Out[192]:
A B
1 1 4
2 5 6
9.5 获取每组的第 n 行
在 Series
或 DataFrame
中可以使用 nth()
来获取第 n
个元素,也可以用于获取每个分组的某一行
In [193]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=["A", "B"])
In [194]: g = df.groupby("A")
In [195]: g.nth(0)
Out[195]:
B
A
1 NaN
5 6.0
In [196]: g.nth(-1)
Out[196]:
B
A
1 4.0
5 6.0
In [197]: g.nth(1)
Out[197]:
B
A
1 4.0
如果你要选择非空项,可以使用关键字参数 dropna
,如果是 DataFrame
,需要指定为 any
或 all
(类似于 DataFrame.dropna(how='any|all')
)
# nth(0) 与 g.first() 等价
In [198]: g.nth(0, dropna="any")
Out[198]:
B
A
1 4.0
5 6.0
In [199]: g.first()
Out[199]:
B
A
1 4.0
5 6.0
# nth(-1) 与 g.last() 等价
In [200]: g.nth(-1, dropna="any") # NaNs denote group exhausted when using dropna
Out[200]:
B
A
1 4.0
5 6.0
In [201]: g.last()
Out[201]:
B
A
1 4.0
5 6.0
In [202]: g.B.nth(0, dropna="all")
Out[202]:
A
1 4.0
5 6.0
Name: B, dtype: float64
与其他方法一样,使用 as_index=False
分组名将不会作为索引
In [203]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=["A", "B"])
In [204]: g = df.groupby("A", as_index=False)
In [205]: g.nth(0)
Out[205]:
A B
0 1 NaN
2 5 6.0
In [206]: g.nth(-1)
Out[206]:
A B
1 1 4.0
2 5 6.0
你也可以传入一个整数列表,一次性选取多行
In [207]: business_dates = pd.date_range(start="4/1/2014", end="6/30/2014", freq="B")
In [208]: df = pd.DataFrame(1, index=business_dates, columns=["a", "b"])
# 选取每月的第 1、4 和最后一天
In [209]: df.groupby([df.index.year, df.index.month]).nth([0, 3, -1])
Out[209]:
a b
2014 4 1 1
4 1 1
4 1 1
5 1 1
5 1 1
5 1 1
6 1 1
6 1 1
6 1 1
9.6 枚举分组项
使用 cumcount
方法,可以查看每行在分组中出现的顺序
In [210]: dfg = pd.DataFrame(list("aaabba"), columns=["A"])
In [211]: dfg
Out[211]:
A
0 a
1 a
2 a
3 b
4 b
5 a
In [212]: dfg.groupby("A").cumcount()
Out[212]:
0 0
1 1
2 2
3 0
4 1
5 3
dtype: int64
In [213]: dfg.groupby("A").cumcount(ascending=False)
Out[213]:
0 3
1 2
2 1
3 1
4 0
5 0
dtype: int64
9.7 枚举分组
可以使用 ngroup()
查看分组的顺序,该顺序与 cumcount
的顺序相反。
注意:该顺序与迭代时的分组顺序一样,并不是第一次观测到的顺序
In [214]: dfg = pd.DataFrame(list("aaabba"), columns=["A"])
In [215]: dfg
Out[215]:
A
0 a
1 a
2 a
3 b
4 b
5 a
In [216]: dfg.groupby("A").ngroup()
Out[216]:
0 0
1 0
2 0
3 1
4 1
5 0
dtype: int64
In [217]: dfg.groupby("A").ngroup(ascending=False)
Out[217]:
0 1
1 1
2 1
3 0
4 0
5 1
dtype: int64
网友评论