YOGYUI

pandas - 중복된 요소 넘버링하기 본문

Software/Python

pandas - 중복된 요소 넘버링하기

요겨 2021. 12. 15. 11:10
반응형

pandas - numbering duplicated elements

 

pandas 데이터프레임(DataFrame) 객체의 특정 열(column)의 값들이 중복될 경우, 중복된 행(row)에 고유번호를 순차적으로 매겨보자 (간단한 개념인데 한국말로 정리하는게 더 어렵다)

1. 데이터 준비하기

간단하게 가상의 구매 목록을 하나 만들어주자

from datetime import date
import pandas as pd

df = pd.DataFrame([
    {'구매일': date(2021,12,1), '품목': '계란', '수량': 1, '가격': 3000},
    {'구매일': date(2021,12,1), '품목': '라면', '수량': 1, '가격': 1000},
    {'구매일': date(2021,12,1), '품목': '우유', '수량': 2, '가격': 2500},
    {'구매일': date(2021,12,4), '품목': '케찹', '수량': 1, '가격': 2000},
    {'구매일': date(2021,12,4), '품목': '마요네즈', '수량': 1, '가격': 2000},
    {'구매일': date(2021,12,5), '품목': '두부', '수량': 1, '가격': 1000},
    {'구매일': date(2021,12,8), '품목': '쌀', '수량': 1, '가격': 10000},
    {'구매일': date(2021,12,8), '품목': '고추장', '수량': 1, '가격': 4000},
    {'구매일': date(2021,12,8), '품목': '된장', '수량': 1, '가격': 3500},
    {'구매일': date(2021,12,8), '품목': '커피', '수량': 1, '가격': 20000},
    {'구매일': date(2021,12,10), '품목': '콜라', '수량': 1, '가격': 1000},
    {'구매일': date(2021,12,10), '품목': '사이다', '수량': 1, '가격': 1000}
])
df = df[['구매일', '품목', '수량', '가격']]
In [1]:df
Out[1]: 
           구매일    품목  수량    가격
0   2021-12-01    계란   1  3000
1   2021-12-01    라면   1  1000
2   2021-12-01    우유   2  2500
3   2021-12-04    케찹   1  2000
4   2021-12-04  마요네즈   1  2000
5   2021-12-05    두부   1  1000
6   2021-12-08    계란   1  3000
7   2021-12-08    계란   1  3000
8   2021-12-08    계란   1  3000
9   2021-12-08    계란   1  3000
10  2021-12-10    계란   1  3000
11  2021-12-10    계란   1  3000

'구매일' 칼럼에 중복이 발생되게 데이터프레임을 만들었다 (장보러갔을 때 1회당 여러 제품을 사는 상황을 가정)

2. 값 중복 확인

DataFrame, Series 객체의 duplicated 메서드를 사용하면 값 중복 여부를 확인할 수 있다

In [2]: df.duplicated('구매일', keep='first')
Out[2]: 
0     False
1      True
2      True
3     False
4      True
5     False
6     False
7      True
8      True
9      True
10    False
11     True
dtype: bool

keep 인자는 'first', 'last', False 셋 중 하나로, 중복되는 데이터 중 첫번째/마지막 데이터를 중복되지 않은 것으로 판단할지 여부를 결정한다

In [3]:df.duplicated('구매일', keep='last')
Out[3]: 
0      True
1      True
2     False
3      True
4     False
5     False
6      True
7      True
8      True
9     False
10     True
11    False
dtype: bool

In [4]: df.duplicated('구매일', keep=False)
Out[4]: 
0      True
1      True
2      True
3      True
4      True
5     False
6      True
7      True
8      True
9      True
10     True
11     True
dtype: bool

drop_duplicates 메서드를 사용하면 중복된 데이터를 제거할 수 있다 

이 포스팅에서 원하는 작업은 아니므로 패스~

3. 중복된 행 개수 구하기

중복된 행의 개수를 구해보자

for문을 사용하는 알고리즘으로 구현하면 다음과 같다

temp = None
result = []
for i in range(len(df)):
    if temp is None:
        temp = {df.iloc[i]['구매일']: 1}
        result.append(temp)
    else:
        if list(temp.keys())[0] == df.iloc[i]['구매일']:
            temp[df.iloc[i]['구매일']] += 1
        else:
            temp = {df.iloc[i]['구매일']: 1}
            result.append(temp)

(급하게 짜다보니 너무 허접한 경항이?! ㅎㅎ)

In [5]: result
Out[5]: 
[{datetime.date(2021, 12, 1): 3},
 {datetime.date(2021, 12, 4): 2},
 {datetime.date(2021, 12, 5): 1},
 {datetime.date(2021, 12, 8): 4},
 {datetime.date(2021, 12, 10): 2}]

중복된 '구매일'에 대한 행들의 개수가 각각 3/2/1/4/2개로 제대로 카운팅되었다

 

pandas DataFrame 객체의 groupby 메서드를 활용하면 위 과정을 간단하게 구현할 수 있다

(SQL 개발자라면 너무나 익숙한 ㅎㅎ)

In [6]: df.groupby('구매일')
Out[6]: <pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001D93FC239E8>

groupby를 호출하면 DataFrameGroupBy 객체가 생성이 되는데, groupby 객체에 적용할 수 있는 메서드가 굉장히 많으니 한번 살펴보길 바란다

https://pandas.pydata.org/docs/reference/groupby.html

 

GroupBy — pandas 1.3.5 documentation

previous pandas.api.indexers.VariableOffsetWindowIndexer.get_window_bounds

pandas.pydata.org

여기서는 집계된 그룹들의 개수를 합산하기 위해 count 메서드를 호출하면 

In [7]: df.groupby('구매일').count()
Out[7]: 
            품목  수량  가격
구매일                   
2021-12-01   3   3   3
2021-12-04   2   2   2
2021-12-05   1   1   1
2021-12-08   4   4   4
2021-12-10   2   2   2

구매일을 기준으로 각 열별로 레코드가 몇 개씩인지를 합산해준다 (SQL GROUP BY COUNT랑 동일)

In [8]: df.groupby('구매일').count()[df.columns[1]]
Out[8]: 
구매일
2021-12-01    3
2021-12-04    2
2021-12-05    1
2021-12-08    4
2021-12-10    2
Name: 품목, dtype: int64

요렇게 깔끔하게 정리할 수도 있다

 

4. 중복되는 데이터에 고유번호 부여

for문 활용 알고리즘으로 구현하고자 한다면 

index = 0
temp = None
result = []
for i in range(len(df)):
    if temp is None:
        temp = df.iloc[i]['구매일']
        index = 1
    else:
        if temp == df.iloc[i]['구매일']:
            index += 1
        else:
            temp = df.iloc[i]['구매일']
            index = 1
    result.append(index)
In [9]: result
Out[9]: [1, 2, 3, 1, 2, 1, 1, 2, 3, 4, 1, 2]

앞서 살펴본 groupby를 활용하면 한줄로 구현할 수 있다

In [10]: df.groupby('구매일').cumcount() + 1
Out[10]: 
0     1
1     2
2     3
3     1
4     2
5     1
6     1
7     2
8     3
9     4
10    1
11    2
dtype: int64

그룹화된 객체에 cumcount 메서드 (cumulative count)를 호출하면 중복되는 요소별로 순차적으로 카운팅한 Series 객체를 얻을 수 있다 (zero-based이므로 1을 더해줬다)

 

끝~!

 

[참고]

https://stackoverflow.com/questions/39481609/number-duplicates-sequentially-in-pandas-dataframe

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.duplicated.html

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.groupby.html

https://pandas.pydata.org/docs/reference/api/pandas.core.groupby.DataFrameGroupBy.cumcount.html

 

반응형