itsource

판다들은 각 그룹에서 최고 n개의 기록을 가지고 있다.

mycopycode 2022. 10. 26. 22:40
반응형

판다들은 각 그룹에서 최고 n개의 기록을 가지고 있다.

다음과 같은 팬더 DataFrame이 있다고 가정해 보겠습니다.

df = pd.DataFrame({'id':[1,1,1,2,2,2,2,3,4],'value':[1,2,3,1,2,3,4,1,1]})

다음과 같습니다.

   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

다음과 같이 각 ID에 대해 상위 2개의 레코드가 있는 새로운 Data Frame을 얻고 싶습니다.

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

그룹 내 번호부여 레코드로 할 수 있습니다.groupby:

dfN = df.groupby('id').apply(lambda x:x['value'].reset_index()).reset_index()

다음과 같습니다.

   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()).

해보셨어요?

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)

MultiIndex를 제거하고 결과를 평탄화하려면:

    id  value
0   1      1
1   1      2
2   2      1
3   2      2
4   3      1
5   4      1

0.14.1 이후로는nlargest그리고.nsmallest에서groupby오브젝트:

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에서도 이 작업을 수행할 수 있게 됩니다만, 현재로서는Series그리고.SeriesGroupBy.)

전체 데이터를 미리 정렬하는 데 시간이 많이 걸릴 수 있습니다.먼저 그룹화하여 각 그룹에 대해 topk를 수행할 수 있습니다.

g = df.groupby(['id']).apply(lambda x: x.nlargest(topk,['value'])).reset_index(drop=True)
df.groupby('id').apply(lambda x : x.sort_values(by = 'value', ascending = False).head(2).reset_index(drop = True))
  • 여기서 정렬 값 오름차순 false는 최대값과 비슷하고 True는 최소값과 비슷합니다.
  • 헤드 내부의 값은 각 그룹에 대해 표시할 값의 수를 얻기 위해 n개의 가장 큰 내부의 값과 동일합니다.
  • reset_index는 옵션이며 필요하지 않습니다.

중복된 값에 대해 작동합니다.

top-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





언급URL : https://stackoverflow.com/questions/20069009/pandas-get-topmost-n-records-within-each-group

반응형