달나라 노트

Python Pandas : DataFrame filtering 본문

Python/Python Pandas

Python Pandas : DataFrame filtering

CosmosProject 2020. 11. 4. 20:03
728x90
반응형

 

 

DataFrame filtering

엑셀에선 필터 기능을 이용하여 내가 원하는 조건으로 row filter를 걸고 원하는 데이터들만 조작이 가능하죠.Pandas의 DataFrame도 이러한 기능을 제공합니다.

 

이러한 필터링 기능을 어떻게 이용할 수 있는지 알아봅시다.

 

먼저 test용 DataFrame을 생성합니다.

import pandas as pd

dict_name = {
    'item_id': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'item_name': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    'status': [1, 1, 1, 0, 0, 1, 0, 1, 1, 1]
}
df_name = pd.DataFrame(dict_name)

print(df_name)
print(type(df_name))
 
- Output
   item_id item_name  status
0        1         a       1
1        2         b       1
2        3         c       1
3        4         d       0
4        5         e       0
5        6         f       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
<class 'pandas.core.frame.DataFrame'>
  

item_id별 이름과, 상태를 나타내는 DataFrame입니다.




filtering을 알아보기 전에 먼저, DataFrame에서 어떤 컬럼에 존재하는 모든 데이터를 변경하고싶다면 어떻게 하면 될까요?

아래처럼 loc를 이용하면 됩니다.

df_name.loc[:, 'item_name'] = 'z'

print(df_name)

- Output
   item_id item_name  status
0        1         z       1
1        2         z       1
2        3         z       1
3        4         z       0
4        5         z       0
5        6         z       1
6        7         z       0
7        8         z       1
8        9         z       1
9       10         z       1
  

결과를 보면 item_name 컬럼의 모든 값이 z로 바뀐 것을 볼 수 있죠.




만약 item_id = 5인 행의 item_name 값을 'z'로 변경하고싶다면 어떻게 하면 될까요?

아래 예시에서처럼 loc의 행 표시 부분에 :(모든 행)이 아닌 조건을 적어주면 됩니다.

df_name.loc[(df_name['item_id'] == 5), ['item_name']] = 'z'
print(df_name)

- Output
   item_id item_name  status
0        1         a       1
1        2         b       1
2        3         c       1
3        4         d       0
4        5         z       0
5        6         f       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
  

어떻게 위와 같은 일이 가능했을지를 보려면 먼저 조건으로 적힌 'df_name['item_id'] == 5' 이것이 어떤 의미를 가지는지 볼 필요가 있습니다.

 

test = df_name['item_id'] == 5
print(test)
print(type(test))

- Output
0    False
1    False
2    False
3    False
4     True
5    False
6    False
7    False
8    False
9    False
Name: item_id, dtype: bool
<class 'pandas.core.series.Series'>
  

조건 부분이었던 'df_name['item_id'] == 5'만 따로 출력을 해 보았더니 위와 같이 index가 있으며

각 index별로 True, False값을 가진 'Series'가 반환되었습니다.

 

그리고 기존 df_name데이터에서 item_id가 5였던 행의 index가 4였기 때문에

item_id 컬럼의 값이 5인 조건을 만족하여 index 4번은 True로 표시되었습니다.

 

DataFrame은 위처럼 True, False값을 가진 Series를 loc의 행 표기 부분에 받아 True값으로 표기된 index만 골라서 반환할 수 있습니다.

 

위 예시에선 True값으로 표시된 index = 4인 행을 추출하였고 그 행의 item_id를 'z'로 바꾸게 된겁니다.




아래의 예시는 위와 동일한 예시이지만 이번에는 filtering의 조건을 다른 변수에 먼저 할당한 후 해당 변수를 loc의 row 부분에 넣었습니다.

 

결과는 동일하지만 이렇게 조건을 별도의 변수에 할당하여 관리하게된다면 여러 개의 조건을 적용할 때 편리합니다.

con = (df_name['item_id'] == 5)
df_name.loc[con, ['item_name']] = 'z'

print(df_name)

- Output
   item_id item_name  status
0        1         a       1
1        2         b       1
2        3         c       1
3        4         d       0
4        5         z       0
5        6         f       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
  




여러 개의 조건을 이용하여 filtering하고싶다면 조건끼리 |(or), &(and) 기호를 사용하여 구문해주면 됩니다.

con = (df_name['item_id'] == 5) | (df_name['item_id'] == 6)
df_name.loc[con, ['item_name']] = 'z'

print(df_name)

- Output
   item_id item_name  status
0        1         a       1
1        2         b       1
2        3         c       1
3        4         d       0
4        5         z       0
5        6         z       1
6        7         g       0
7        8         h       1
8        9         i       1
9       10         j       1
  

item_id가 5 또는 6인 행의 item_name이 'z'로 바뀌었음을 알 수 있습니다.

 

 

 

con = (df_name['item_id'] >= 5) & (df_name['status'] == 1)
df_name.loc[con, ['item_name']] = 'z'

print(df_name)

- Output
   item_id item_name  status
0        1         a       1
1        2         b       1
2        3         c       1
3        4         d       0
4        5         e       0
5        6         z       1
6        7         g       0
7        8         z       1
8        9         z       1
9       10         z       1
  

item_id >= 5이면서 status == 1인 값을 가진 행에 대해 item_name값이 모두 z로 변했음을 알 수 있습니다.




만약 item_id가 1, 3, 5, 7인 행의 item_name만 바꾸고 싶다면 어떻게 하면 될까요?

con = (df_name['item_id'] == 1) | (df_name['item_id'] == 3 | (df_name['item_id'] == 5 | (df_name['item_id'] == 7)
df_name.loc[con, ['item_name']] = 'z'

print(df_name)

- Output
   item_id item_name  status
0        1         z       1
1        2         b       1
2        3         z       1
3        4         d       0
4        5         z       0
5        6         f       1
6        7         z       0
7        8         h       1
8        9         i       1
9       10         j       1
  

위처럼 |(or)를 이용해서 각각의 조건을 모두 명시해주는 것도 하나의 방법일 수 있습니다만,

위처럼 진행할 경우 조건이 너무 길어지는 단점이 있죠.

 

따라서 우리는 Series의 isin을 사용하면됩니다.

 

 

 

df_name.loc[(df_name['item_id'].isin([1, 3, 5, 7])), ['item_name']] = 'z'

print(df_name)

- Output
   item_id item_name  status
0        1         z       1
1        2         b       1
2        3         z       1
3        4         d       0
4        5         z       0
5        6         f       1
6        7         z       0
7        8         h       1
8        9         i       1
9       10         j       1
  

위 예시를 보면 item_id가 1, 3, 5, 7인 행의 item_name이 'z'로 잘 바뀌었음을 알 수 있습니다.여기에 사용된 조건을 자세히 봅시다.

 

 

 

test_1 = df_name['item_id']
print(test_1)
print(type(test_1))


test_2 = df_name['item_id'].isin([1, 3, 5, 7])

print(test_2)
print(type(test_2))

- Output
0     1
1     2
2     3
3     4
4     5
5     6
6     7
7     8
8     9
9    10
Name: item_id, dtype: int64
<class 'pandas.core.series.Series'>

0     True
1    False
2     True
3    False
4     True
5    False
6     True
7    False
8    False
9    False
Name: item_id, dtype: bool
<class 'pandas.core.series.Series'>
  

df_name['item_id'] -> df_name의 item_id컬럼을 추출하여 Series 데이터를 반환합니다.

df_name['item_id'].isin([1, 3, 5, 7]) -> df_name['item_id']에 의해 반환된 Series 데이터에 isin함수를 적용합니다.

isin 함수는 Series에 존재하는 각각의 데이터가 주어진 list에 존재하는지를 체크하며 결과적으로 Boolean Series를 반환합니다.

그래서 test_2를 print한 결과를 보면 item_id값이 1, 3, 5, 7인 행(row)에만 True라고 명시되어있죠.

그리고 이러한 boolean series는 우리가 True값만 조회하려는 loc의 조건 Series로서 사용할 수 있기 때문에 위의 코드가 가능해지는 것입니다.




위 예시와는 조건을 반전하여 item_id가 1, 3, 5, 7가 '아닌' 행의 item_name만 바꾸고 싶다면 조건 앞에 not의 의미를 가지는 기호 ~를 붙여주면 됩니다.

df_name = pd.DataFrame(dict_name)
df_name.loc[(~df_name['item_id'].isin([1, 3, 5, 7])), ['item_name']] = 'z'

print(df_name)

- Output
   item_id item_name  status
0        1         a       1
1        2         z       1
2        3         c       1
3        4         z       0
4        5         e       0
5        6         z       1
6        7         g       0
7        8         z       1
8        9         z       1
9       10         z       1
  

위 결과를 보면 item_id가 1, 3, 5, 7이 아닌 행의 item_name컬럼 데이터가 모두 'z'로 바뀌었음을 알 수 있습니다.

 

 

 

 

 

 

 

 

728x90
반응형
Comments