例如我有以下表格:
index,A,B
0,0,0
1,0,8
2,0,8
3,1,0
4,1,5
按A
分组后:
0:
index,A,B
0,0,0
1,0,8
2,0,8
1:
index,A,B
3,1,5
4,1,3
我需要从每个组中删除行,其中B
列中的数字小于来自组B
的所有行的最大值。好吧,我有一个问题,将这个问题翻译成英文,所以这里是一个例子:
组B
中列0
中行的最大值: 8
所以我想删除索引为0
的行,并保留包含索引1
和2
的行
组B
中列1
中行的最大值: 5
所以我想删除索引为4
的行并保留索引3
的行
我曾尝试使用pandas过滤器功能,但问题是它一次在组中的所有行上运行:
data = <example table>
grouped = data.groupby("A")
filtered = grouped.filter(lambda x: x["B"] == x["B"].max())
所以我理想需要的是一些过滤器,它会遍历组中的所有行。
感谢您的帮助!
P.S。是否还有方法只删除组中的行而不返回DataFrame
对象?
答案 0 :(得分:37)
您只需在apply
对象上使用groupby
即可。我修改了你的示例数据,使其更加清晰:
import pandas
from io import StringIO
csv = StringIO("""index,A,B
0,1,0.0
1,1,3.0
2,1,6.0
3,2,0.0
4,2,5.0
5,2,7.0""")
df = pandas.read_csv(csv, index_col='index')
groups = df.groupby(by=['A'])
print(groups.apply(lambda g: g[g['B'] == g['B'].max()]))
打印哪些:
A B
A index
1 2 1 6
2 4 2 7
答案 1 :(得分:17)
编辑:我刚刚通过方法使用.transform
组了解了一种更简洁的方法:
def get_max_rows(df):
B_maxes = df.groupby('A').B.transform(max)
return df[df.B == B_maxes]
B_maxes
是一个系列,其编号与原始df
完全相同,包含每个B
组的最大值A
。您可以将许多函数传递给transform方法。我认为一旦他们输出相同长度的标量或向量。您甚至可以将一些字符串作为常用函数名称传递,例如'median'
。
这与Paul H的方法略有不同,A&#39; A&#39;不会成为结果中的索引,但您可以在之后轻松设置。
import numpy as np
import pandas as pd
df_lots_groups = pd.DataFrame(np.random.rand(30000, 3), columns = list('BCD')
df_lots_groups['A'] = np.random.choice(range(10000), 30000)
%timeit get_max_rows(df_lots_groups)
100 loops, best of 3: 2.86 ms per loop
%timeit df_lots_groups.groupby('A').apply(lambda df: df[ df.B == df.B.max()])
1 loops, best of 3: 5.83 s per loop
编辑:
这是一个抽象,它允许您使用任何有效的比较运算符和任何有效的groupby方法从组中选择行:
def get_group_rows(df, group_col, condition_col, func=max, comparison='=='):
g = df.groupby(group_col)[condition_col]
condition_limit = g.transform(func)
df.query('condition_col {} @condition_limit'.format(comparison))
所以,例如,如果你想要在每个A组的中位数B值之上的所有行你调用
get_group_rows(df, 'A', 'B', 'median', '>')
一些例子:
%timeit get_group_rows(df_lots_small_groups, 'A', 'B', 'max', '==')
100 loops, best of 3: 2.84 ms per loop
%timeit get_group_rows(df_lots_small_groups, 'A', 'B', 'mean', '!=')
100 loops, best of 3: 2.97 ms per loop
答案 2 :(得分:3)
以下是另一个示例:使用idxmax()和.loc()
在groupby操作后过滤具有最大值的行In [465]: import pandas as pd
In [466]: df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4'],
'value' : [3,2,5,8,10,1]
})
In [467]: df
Out[467]:
mt sp value
0 S1 MM1 3
1 S1 MM1 2
2 S3 MM1 5
3 S3 MM2 8
4 S4 MM2 10
5 S4 MM2 1
### Here, idxmax() finds the indices of the rows with max value within groups,
### and .loc() filters the rows using those indices :
In [468]: df.loc[df.groupby(["mt"])["value"].idxmax()]
Out[468]:
mt sp value
0 S1 MM1 3
3 S3 MM2 8
4 S4 MM2 10
答案 3 :(得分:0)
所有这些答案都不错,但我想要以下内容:
(DataframeGroupby object) --> filter some rows out --> (DataframeGroupby object)
耸耸肩,似乎比我预期的更难,更有趣。因此,这种衬板可以满足我的要求,但这可能不是最有效的方法:)
gdf.apply(lambda g: g[g['team'] == 'A']).reset_index(drop=True).groupby(gdf.grouper.names)
工作代码示例:
import pandas as pd
def print_groups(gdf):
for name, g in gdf:
print('\n'+name)
print(g)
df = pd.DataFrame({'name': ['sue', 'jim', 'ted', 'moe'],
'team': ['A', 'A', 'B', 'B'],
'fav_food': ['tacos', 'steak', 'tacos', 'steak']})
gdf = df.groupby('fav_food')
print_groups(gdf)
steak
name team fav_food
1 jim A steak
3 moe B steak
tacos
name team fav_food
0 sue A tacos
2 ted B tacos
fgdf = gdf.apply(lambda g: g[g['team'] == 'A']).reset_index(drop=True).groupby(gdf.grouper.names)
print_groups(fgdf)
steak
name team fav_food
0 jim A steak
tacos
name team fav_food
1 sue A tacos