如何根据组标准使用 Pandas 对数据框进行子集化?
How to subset a data frame using Pandas based on a group criteria?
我有一个具有以下结构的大型数据集
User X
1 0
1 0
2 0
2 0
2 1
3 0
3 0
我想取一个数据子集,使每个用户的 X 列之和为 0。鉴于上述示例,该子集应仅包括用户 1 和 3 的观察结果,如下所示
User X
1 0
1 0
3 0
3 0
有没有办法在不对数据进行分组的情况下使用 groupby 函数来做到这一点?我希望子集包括个别观察结果。
,其中 selects 使用布尔掩码的行,即使 DataFrame 具有非唯一索引也能正常工作。
我的方法 selects 行使用索引值,当索引是唯一的时稍微慢一些,当索引包含重复值时 显着慢 。
@roland:请考虑接受 DSM 的回答。
您可以使用 groupby-filter
:
In [16]: df.loc[df.groupby('User')['X'].filter(lambda x: x.sum() == 0).index]
Out[16]:
User X
0 1 0
1 1 0
5 3 0
6 3 0
groupby-filter 本身就是 returns 这个:
In [29]: df.groupby('User')['X'].filter(lambda x: x.sum() == 0)
Out[29]:
0 0
1 0
5 0
6 0
Name: X, dtype: int64
但是你可以使用它的索引,
In [30]: df.groupby('User')['X'].filter(lambda x: x.sum() == 0).index
Out[30]: Int64Index([0, 1, 5, 6], dtype='int64')
使用 df.loc
. 到 select 所需的行
这是我使用的基准:
In [49]: df2 = pd.concat([df]*10000) # df2 has a non-unique index
我Ctrl-C因为完成时间太长:
In [50]: %timeit df2.loc[df2.groupby('User')['X'].filter(lambda x: x.sum() == 0).index]
当我意识到我的错误时,我制作了一个具有唯一索引的DataFrame:
In [51]: df3 = df2.reset_index() # this gives df3 a unique index
In [52]: %timeit df3.loc[df3.groupby('User')['X'].filter(lambda x: x.sum() == 0).index]
100 loops, best of 3: 13 ms per loop
In [53]: %timeit df3.loc[df3.groupby("User")["X"].transform(sum) == 0]
100 loops, best of 3: 11.4 ms per loop
这表明即使使用非唯一索引,DSM 的方法也表现良好:
In [54]: %timeit df2.loc[df2.groupby("User")["X"].transform(sum) == 0]
100 loops, best of 3: 11.2 ms per loop
作为@unutbu 答案的替代方案,还有
>>> df.loc[df.groupby("User")["X"].transform(sum) == 0]
User X
0 1 0
1 1 0
5 3 0
6 3 0
这将创建一个 df
长度的布尔系列用作选择器:
>>> df.groupby("User")["X"].transform(sum) == 0
0 True
1 True
2 False
3 False
4 False
5 True
6 True
dtype: bool
transform
用于 "broadcast" 将 groupby 缩减操作的结果备份到每个组的所有元素。派上用场了。
我有一个具有以下结构的大型数据集
User X
1 0
1 0
2 0
2 0
2 1
3 0
3 0
我想取一个数据子集,使每个用户的 X 列之和为 0。鉴于上述示例,该子集应仅包括用户 1 和 3 的观察结果,如下所示
User X
1 0
1 0
3 0
3 0
有没有办法在不对数据进行分组的情况下使用 groupby 函数来做到这一点?我希望子集包括个别观察结果。
@roland:请考虑接受 DSM 的回答。
您可以使用 groupby-filter
:
In [16]: df.loc[df.groupby('User')['X'].filter(lambda x: x.sum() == 0).index]
Out[16]:
User X
0 1 0
1 1 0
5 3 0
6 3 0
groupby-filter 本身就是 returns 这个:
In [29]: df.groupby('User')['X'].filter(lambda x: x.sum() == 0)
Out[29]:
0 0
1 0
5 0
6 0
Name: X, dtype: int64
但是你可以使用它的索引,
In [30]: df.groupby('User')['X'].filter(lambda x: x.sum() == 0).index
Out[30]: Int64Index([0, 1, 5, 6], dtype='int64')
使用 df.loc
. 到 select 所需的行
这是我使用的基准:
In [49]: df2 = pd.concat([df]*10000) # df2 has a non-unique index
我Ctrl-C因为完成时间太长:
In [50]: %timeit df2.loc[df2.groupby('User')['X'].filter(lambda x: x.sum() == 0).index]
当我意识到我的错误时,我制作了一个具有唯一索引的DataFrame:
In [51]: df3 = df2.reset_index() # this gives df3 a unique index
In [52]: %timeit df3.loc[df3.groupby('User')['X'].filter(lambda x: x.sum() == 0).index]
100 loops, best of 3: 13 ms per loop
In [53]: %timeit df3.loc[df3.groupby("User")["X"].transform(sum) == 0]
100 loops, best of 3: 11.4 ms per loop
这表明即使使用非唯一索引,DSM 的方法也表现良好:
In [54]: %timeit df2.loc[df2.groupby("User")["X"].transform(sum) == 0]
100 loops, best of 3: 11.2 ms per loop
作为@unutbu 答案的替代方案,还有
>>> df.loc[df.groupby("User")["X"].transform(sum) == 0]
User X
0 1 0
1 1 0
5 3 0
6 3 0
这将创建一个 df
长度的布尔系列用作选择器:
>>> df.groupby("User")["X"].transform(sum) == 0
0 True
1 True
2 False
3 False
4 False
5 True
6 True
dtype: bool
transform
用于 "broadcast" 将 groupby 缩减操作的结果备份到每个组的所有元素。派上用场了。