220

假设我有这样的熊猫数据框:

>>> df = pd.DataFrame({'id':[1,1,1,2,2,2,2,3,4],'value':[1,2,3,1,2,3,4,1,1]})
>>> df
   id  value
0   1      1
1   1      2
2   1      3
3   2      1
4   2      2
5   2      3
6   2      4
7   3      1
8   4      1

我想获得一个新的 DataFrame,每个 id 都有前 2 条记录,如下所示:

   id  value
0   1      1
1   1      2
3   2      1
4   2      2
7   3      1
8   4      1

我可以通过以下方式对组内的记录进行编号:

>>> dfN = df.groupby('id').apply(lambda x:x['value'].reset_index()).reset_index()
>>> dfN
   id  level_1  index  value
0   1        0      0      1
1   1        1      1      2
2   1        2      2      3
3   2        0      3      1
4   2        1      4      2
5   2        2      5      3
6   2        3      6      4
7   3        0      7      1
8   4        0      8      1
>>> dfN[dfN['level_1'] <= 1][['id', 'value']]
   id  value
0   1      1
1   1      2
3   2      1
4   2      2
7   3      1
8   4      1

但是有没有更有效/优雅的方法来做到这一点?还有更优雅的方法来对每个组中的记录进行编号(如 SQL 窗口函数row_number())。

4

5 回答 5

258

你试过了吗df.groupby('id').head(2)

产生的输出:

>>> df.groupby('id').head(2)
       id  value
id             
1  0   1      1
   1   1      2 
2  3   2      1
   4   2      2
3  7   3      1
4  8   4      1

(请记住,您可能需要先订购/排序,具体取决于您的数据)

编辑:正如提问者所提到的,用于df.groupby('id').head(2).reset_index(drop=True)删除多索引并使结果变平。

>>> df.groupby('id').head(2).reset_index(drop=True)
    id  value
0   1      1
1   1      2
2   2      1
3   2      2
4   3      1
5   4      1
于 2013-11-19T10:46:03.760 回答
167

从 0.14.1 开始,您现在可以在对象上执行nlargest和操作:nsmallestgroupby

In [23]: df.groupby('id')['value'].nlargest(2)
Out[23]: 
id   
1   2    3
    1    2
2   6    4
    5    3
3   7    1
4   8    1
dtype: int64

您也可以在其中获得原始索引,这有点奇怪,但这可能非常有用,具体取决于您的原始索引什么。

如果你对它不感兴趣,你可以.reset_index(level=1, drop=True)完全摆脱它。

(注意:从 0.17.1 开始,您也可以在 DataFrameGroupBy 上执行此操作,但目前它仅适用于Seriesand SeriesGroupBy。)

于 2015-09-04T12:14:13.553 回答
15

有时,提前对整个数据进行排序非常耗时。我们可以先 groupby 并为每个组做 topk:

g = df.groupby(['id']).apply(lambda x: x.nlargest(topk,['value'])).reset_index(drop=True)
于 2019-11-12T03:41:23.920 回答
6
df.groupby('id').apply(lambda x : x.sort_values(by = 'value', ascending = False).head(2).reset_index(drop = True))
  • 这里排序值升序 false 给出类似于 nlargest 和 True 给出类似于 nsmallest。
  • head 内部的值与我们在 nlargest 内部给出的值相同,以获取要为每个组显示的值的数量。
  • reset_index 是可选的,不是必需的。
于 2021-03-09T17:02:36.327 回答
3

这适用于重复值

如果您在前 n 个值中有重复值,并且只需要唯一值,则可以这样做:

import pandas as pd

ifile = "https://raw.githubusercontent.com/bhishanpdl/Shared/master/data/twitter_employee.tsv"
df = pd.read_csv(ifile,delimiter='\t')
print(df.query("department == 'Audit'")[['id','first_name','last_name','department','salary']])

    id first_name last_name department  salary
24  12   Shandler      Bing      Audit  110000
25  14      Jason       Tom      Audit  100000
26  16     Celine    Anston      Audit  100000
27  15    Michale   Jackson      Audit   70000

If we do not remove duplicates, for the audit department we get top 3 salaries as 110k,100k and 100k.
If we want to have not-duplicated salaries per each department, we can do this:

(df.groupby('department')['salary']
 .apply(lambda ser: ser.drop_duplicates().nlargest(3))
 .droplevel(level=1)
 .sort_index()
 .reset_index()
)

This gives

department  salary
0   Audit   110000
1   Audit   100000
2   Audit   70000
3   Management  250000
4   Management  200000
5   Management  150000
6   Sales   220000
7   Sales   200000
8   Sales   150000





于 2021-12-22T23:19:16.793 回答