进行 groupby 时保留其他列
Posted
技术标签:
【中文标题】进行 groupby 时保留其他列【英文标题】:Keep other columns when doing groupby 【发布时间】:2014-06-17 03:37:15 【问题描述】:我在 pandas 数据帧上使用 groupby
来删除所有没有特定列最小值的行。像这样:
df1 = df.groupby("item", as_index=False)["diff"].min()
但是,如果我有两个以上的列,则其他列(例如我的示例中的 otherstuff
)将被删除。我可以使用groupby
保留这些列,还是我必须找到一种不同的方法来删除这些行?
我的数据如下:
item diff otherstuff
0 1 2 1
1 1 1 2
2 1 3 7
3 2 -1 0
4 2 1 3
5 2 4 9
6 2 -6 2
7 3 0 0
8 3 2 9
最终应该是这样的:
item diff otherstuff
0 1 1 2
1 2 -6 2
2 3 0 0
但我得到的是:
item diff
0 1 1
1 2 -6
2 3 0
我一直在查看文档,但找不到任何东西。我试过了:
df1 = df.groupby(["item", "otherstuff"], as_index=false)["diff"].min()
df1 = df.groupby("item", as_index=false)["diff"].min()["otherstuff"]
df1 = df.groupby("item", as_index=false)["otherstuff", "diff"].min()
但是这些都不起作用(我意识到最后一个语法是用于在创建组后进行聚合)。
【问题讨论】:
所以你想保留包含'diff'列最小值的整行,包括其他列的任何值。是的,这不在pandas doc 的范围内,但应该是,请提出一个 docbug。 【参考方案1】:方法#1:使用idxmin()
获取最小diff
元素的索引,然后选择那些:
>>> df.loc[df.groupby("item")["diff"].idxmin()]
item diff otherstuff
1 1 1 2
6 2 -6 2
7 3 0 0
[3 rows x 3 columns]
方法#2:按diff
排序,然后取每个item
组的第一个元素:
>>> df.sort_values("diff").groupby("item", as_index=False).first()
item diff otherstuff
0 1 1 2
1 2 -6 2
2 3 0 0
[3 rows x 3 columns]
请注意,即使行内容相同,生成的索引也会不同。
【讨论】:
第一种方法返回所有NaN
,但第二种方法有效。谢谢!
感谢您提供两种方法。我发现了多个实例,其中一个起作用而另一个不起作用,反之亦然。似乎这取决于您的元素是否为数字。两个都好。
谢谢!我发现第一个非常慢且不正确,但第二个很快并且做了我想要的。我同时使用了多个索引,这可能会搞砸。【参考方案2】:
您可以将DataFrame.sort_values
与DataFrame.drop_duplicates
一起使用:
df = df.sort_values(by='diff').drop_duplicates(subset='item')
print (df)
item diff otherstuff
6 2 -6 2
7 3 0 0
1 1 1 2
如果可能,每组有多个最小值,并希望所有最小行使用 boolean indexing
和 transform
来获得每组的最小值:
print (df)
item diff otherstuff
0 1 2 1
1 1 1 2 <-multiple min
2 1 1 7 <-multiple min
3 2 -1 0
4 2 1 3
5 2 4 9
6 2 -6 2
7 3 0 0
8 3 2 9
print (df.groupby("item")["diff"].transform('min'))
0 1
1 1
2 1
3 -6
4 -6
5 -6
6 -6
7 0
8 0
Name: diff, dtype: int64
df = df[df.groupby("item")["diff"].transform('min') == df['diff']]
print (df)
item diff otherstuff
1 1 1 2
2 1 1 7
6 2 -6 2
7 3 0 0
【讨论】:
【参考方案3】:如果有/您想要一分钟,上述答案效果很好。在我的情况下,可能有多个分钟,我希望所有行都等于 min,.idxmin()
没有给你。这有效
def filter_group(dfg, col):
return dfg[dfg[col] == dfg[col].min()]
df = pd.DataFrame('g': ['a'] * 6 + ['b'] * 6, 'v1': (list(range(3)) + list(range(3))) * 2, 'v2': range(12))
df.groupby('g',group_keys=False).apply(lambda x: filter_group(x,'v1'))
顺便说一句,.filter() 也与此问题相关,但对我不起作用。
【讨论】:
【参考方案4】:我尝试了每个人的方法,但我无法使其正常工作。相反,我一步一步地完成了这个过程,最终得到了正确的结果。
df.sort_values(by='item', inplace=True, ignore_index=True)
df.drop_duplicates(subset='diff', inplace=True, ignore_index=True)
df.sort_values(by=['diff'], inplace=True, ignore_index=True)
更多解释:
-
按所需的最小值对项目进行排序
删除要排序的列的重复项
重新排序数据,因为数据仍按最小值排序
【讨论】:
【参考方案5】:如果您知道您的所有“项目”有多个可以排序的记录,请使用duplicated
:
df.sort_values(by='diff').duplicated(subset='item', keep='first')
【讨论】:
以上是关于进行 groupby 时保留其他列的主要内容,如果未能解决你的问题,请参考以下文章
在 SparkSQL 中执行 groupBy 和 agg 时包括其他列 [重复]
如何对一列执行 pandas groupby 操作,但将另一列保留在结果数据框中