Python Library/Pandas

[Pandas - Python] Pandas 라이브러리와 데이터 검색(조건)

바보1 2022. 2. 7. 18:34

저번 시간까지 기본, loc, iloc를 이용해서 데이터를 검색하는 법에 대해 공부했었습니다.

 

이번 시간부터는 특정한 조건에 해당하는 데이터를 검색하는 방법을 알아보겠습니다.

 

생각보다 어렵지 않습니다.

 

import pandas as pd
df = pd.read_excel('score.xlsx', index_col='지원번호')
df
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
2번	정대만	북산고	184	40	35	50	55	25	Java
3번	송태섭	북산고	168	80	75	70	80	75	Javascript
4번	서태웅	북산고	187	40	60	70	75	80	NaN
5번	강백호	북산고	188	15	20	10	35	10	NaN
6번	변덕규	능남고	202	80	100	95	85	80	C
7번	황태산	능남고	188	55	65	45	40	35	PYTHON
8번	윤대협	능남고	190	100	85	90	95	95	C#

  • 기본적인 조건

예를 들어 키가 185 이상인 조건을 걸어보겠습니다.

 

df['키'] >= 185
지원번호
1번     True
2번    False
3번    False
4번     True
5번     True
6번     True
7번     True
8번     True
Name: 키, dtype: bool

이렇게 키가 185 이상인 데이터에 대한 True, False가 나옵니다.

 

이를 이용해서 filt(필터) 변수를 만들어보겠습니다.

 

filt = df['키'] >= 185
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
4번	서태웅	북산고	187	40	60	70	75	80	NaN
5번	강백호	북산고	188	15	20	10	35	10	NaN
6번	변덕규	능남고	202	80	100	95	85	80	C
7번	황태산	능남고	188	55	65	45	40	35	PYTHON
8번	윤대협	능남고	190	100	85	90	95	95	C#

이렇게 하시면 키가 185이상인 데이터를 뽑아올 수 있습니다.

 

filt에는 위의 True, False 값이 들어가있겠죠?

 

굳이 filt를 쓰고 싶지 않으시다면,

 

df[df['키'] >= 185]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
4번	서태웅	북산고	187	40	60	70	75	80	NaN
5번	강백호	북산고	188	15	20	10	35	10	NaN
6번	변덕규	능남고	202	80	100	95	85	80	C
7번	황태산	능남고	188	55	65	45	40	35	PYTHON
8번	윤대협	능남고	190	100	85	90	95	95	C#

이렇게 하셔도 됩니다.

 

만약에 키가 185 미만인 데이터를 검색하고 싶으시면, filt 앞에 ~(물결)을 넣어주시면 됩니다. 그러면 True는 False가 되고, False는 True가 됩니다.

 

df[~filt] # filt를 역으로 적용
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
2번	정대만	북산고	184	40	35	50	55	25	Java
3번	송태섭	북산고	168	80	75	70	80	75	Javascript

이렇게 하시면 키가 185 미만인 데이터를 검색합니다.

 

이렇게 정수 형태 뿐만 아니라 다른 형태의 데이터에 대해서도 조건을 추가할 수 있습니다.

 

filt = df['이름'].isin(['채치수', '강백호'])
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
5번	강백호	북산고	188	15	20	10	35	10	NaN

isin에 대한 내용은 뒤에서 설명하니까 지금은 그냥 굳이 정수가 아니어도 된다 정도로 알고 있으시면 될 것 같습니다.


  • loc와 응용해서 검색

 

df.loc[df['키'] >= 185, '수학']
지원번호
1번    100
4번     70
5번     10
6번     95
7번     45
8번     90
Name: 수학, dtype: int64

이렇게 하시면 키가 185 이상인 데이터들에서 수학 과목의 성적을 가지고 옵니다.

 

df.loc[df['키']>=185, ['수학', '과학', '사회']]
	수학	과학	사회
지원번호			
1번	100	95	85
4번	70	75	80
5번	10	35	10
6번	95	85	80
7번	45	40	35
8번	90	95	95

이렇게 하시면 키가 185 이상인 데이터들에서 수학, 과학, 사회 과목을 가지고 옵니다.

 

근데 사실 이런 방법 굳이 loc를 안 써도 되긴 합니다.

 

df[df['키'] >= 185][['수학','과학','사회']]

이렇게 해도 되긴합니다...ㅎㅎ...

 


  • and, or 조건을 사용하기

키가 185 이상이고, 학교가 북산고인 데이터를 추출해보겠습니다.

 

filt = (df['키'] >= 185) & (df['학교'] == '북산고')
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
4번	서태웅	북산고	187	40	60	70	75	80	NaN
5번	강백호	북산고	188	15	20	10	35	10	NaN

이렇게 &를 사용하시면 됩니다. 

 

당연히 or은 '|' 를 사용해주시면 됩니다. 참고로 and, or 아닙니다!!

 

filt = (df['키'] < 170) | (df['키'] > 200)
df.loc[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
3번	송태섭	북산고	168	80	75	70	80	75	Javascript
6번	변덕규	능남고	202	80	100	95	85	80	C

이렇게 | 를 사용해주시면 됩니다!


  • str를 이용한 조건

이름에 '송'으로 시작하는 데이터를 추출해보겠습니다.

 

filt = df['이름'].str.startswith('송')
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
3번	송태섭	북산고	168	80	75	70	80	75	Javascript

이렇게 데이터를 string으로 바꿔서 startswith함수를 이용할 수 있습니다.

 

contains 함수도 쓸 수 있습니다.

 

filt = df['이름'].str.contains('태') # 태가 들어가는 사람을 뽑음
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
3번	송태섭	북산고	168	80	75	70	80	75	Javascript
4번	서태웅	북산고	187	40	60	70	75	80	NaN
7번	황태산	능남고	188	55	65	45	40	35	PYTHON

이렇게 하시면 이름에 '태'가 들어가는 데이터를 추출할 수 있습니다.

 

isin에 대해 한 번 알아보겠습니다. isin은 특정 데이터가 안에 있는지를 확인할 수 있습니다.

 

 

langs = ['Python', 'Java']
filt = df['SW특기'].isin(langs) #SW특기가 Python이거나 Java인 사람을 뽑음
df[filt]
이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
2번	정대만	북산고	184	40	35	50	55	25	Java

이렇게 하시면 SW특기가 Python이거나 Java인 데이터를 추출합니다.

 

근데 여기서 df를 확인하시면, 

 

	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
2번	정대만	북산고	184	40	35	50	55	25	Java
3번	송태섭	북산고	168	80	75	70	80	75	Javascript
4번	서태웅	북산고	187	40	60	70	75	80	NaN
5번	강백호	북산고	188	15	20	10	35	10	NaN
6번	변덕규	능남고	202	80	100	95	85	80	C
7번	황태산	능남고	188	55	65	45	40	35	PYTHON
8번	윤대협	능남고	190	100	85	90	95	95	C#

7번 황태산의 SW특기는 PYTHON입니다. 즉 isin은 대문자와 소문자를 구별한다는 뜻입니다

 

여기서 str의 lower()함수를 이용할 수 있습니다.

 

langs = ['python', 'java']
filt = df['SW특기'].str.lower().isin(langs)
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
1번	채치수	북산고	197	90	85	100	95	85	Python
2번	정대만	북산고	184	40	35	50	55	25	Java
7번	황태산	능남고	188	55	65	45	40	35	PYTHON

이렇게 str.lower()로 모든 SW특기를 소문자로 만든 후, isin을 이용해주시면 됩니다!

 

만약 제가 SW특기에 Java가 들어가있는 모든 데이터를 추출하려면 어떻게 해야할까요?

 

제가 데이터를 모두 안다는 가정하에는,

 

langs = ['Javascript', 'Java']
filt = df['SW특기'].isin(langs)
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
2번	정대만	북산고	184	40	35	50	55	25	Java
3번	송태섭	북산고	168	80	75	70	80	75	Javascript

이렇게 하시면 되지만, 보통 저희는 모든 데이터를 모르기 때문에, isin을 이용할 수 없습니다.

 

따라서 contains를 이용해야합니다.

 

filt = df['SW특기'].str.contains('Java')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_6808/876867708.py in <module>
      1 filt = df["SW특기"].str.contains('Java')
----> 2 df[filt]

~\anaconda3\lib\site-packages\pandas\core\frame.py in __getitem__(self, key)
   3446 
   3447         # Do we have a (boolean) 1d indexer?
-> 3448         if com.is_bool_indexer(key):
   3449             return self._getitem_bool_array(key)
   3450 

~\anaconda3\lib\site-packages\pandas\core\common.py in is_bool_indexer(key)
    137                     # Don't raise on e.g. ["A", "B", np.nan], see
    138                     #  test_loc_getitem_list_of_labels_categoricalindex_with_na
--> 139                     raise ValueError(na_msg)
    140                 return False
    141             return True

ValueError: Cannot mask with non-boolean array containing NA / NaN values

오류가 발생하네요..

 

NA/NaN 데이터를 처리할 수 없기 때문에 이런 오류가 뜨는 것 같습니다.

 

어떡할까요?

 

이때 NA/ NaN 데이터를 따로 처리해주는 인자를 추가해주면 됩니다.

 

df['SW특기'].str.contains('Java', na=True) #Nan 데이터에 대해서 True로 바꿔줌
지원번호
1번    False
2번     True
3번     True
4번     True
5번     True
6번    False
7번    False
8번    False
Name: SW특기, dtype: bool

이렇게 해주시면, 모든 na를 처리할 때, True로 처리합니다.

 

아래처럼 쓰시면 됩니다!

 

filt = df['SW특기'].str.contains('Java', na=False)
df[filt]
	이름	학교	키	국어	영어	수학	과학	사회	SW특기
지원번호									
2번	정대만	북산고	184	40	35	50	55	25	Java
3번	송태섭	북산고	168	80	75	70	80	75	Javascript

이렇게 na=True/False를 해주시면 na를 True로 처리하던가, False로 처리하던가 둘 중에 하나를 합니다.


1. 요약

 

조건을 사용하시면 정수뿐만 아니라 다양한 형태의 조건을 걸 수 있습니다.

 

and, or을 사용하고 싶으면 &와 |를 사용하시면 됩니다.

 

또한 loc와 연동해서 사용할 수도 있습니다.

 

string에 대한 특별한 조건을 부여하고 싶으시면 df['column name'].str 를 쓰신 후 string의 함수를 쓰시면 됩니다.

 

이때, contains는 Na/NaN에 해당하는 값을 처리하지 못하므로 na = True/False를 사용해주셔야 합니다.

 

감사합니다.

 

지적 환영합니다.

 

 

 

참고 : 나도코딩