-
chapter-10 apply 메서드 활용판다스 2021. 9. 23. 00:41
10-1 간단한 함수 만들기¶
이 책은 파이썬의 기초 지식을 어느 정도 아는 독자를 대상으로 집필되었습니다. 따라서 함수는 간단히 설명하고 넘어갑니다. 함수의 기본 구조는 다음과 같습니다.
def my_function():
# 여기부터 코드를 입력합니다.<br>
그러면 제곱 함수와 n 제곱 함수를 직접 만들어보겠습니다. 제곱 함수와 n 제곱 함수는 10-2에서 apply 메서드와 함께 사용합니다. 여기에서 꼭 함수를 만들어보고 다음으로 넘어가세요.
In [3]:def my_sq(x): return x **2
2.¶
n 제곱 함수 my_exp는 다음과 같습니다.
In [4]:def my_exp(x,n): return x**n
3.¶
과정 1~2에서 만든 함수를 사용해 볼까요?
In [6]:print(my_sq(4))
16
In [7]:print(my_exp(2,4))
16
10-2 apply 메서드 사용하기 -- 기초¶
다음 실습을 통해 apply 메서드의 사용법을 알아보겠습니다. apply 메서드로 브로드캐스팅한 결과가 올바른 값인지 확인하기 위해 시리즈, 데이터프레임의 기초 연산 결과와 비교하며 실습을 진행하겠습니다.
In [8]:import pandas as pd df=pd.DataFrame({'a':[10,20,30],'b':[20,30,40]}) print(df)
a b 0 10 20 1 20 30 2 30 40
2.¶
다음은 앞에서 만든 제곱 함수를 사용하기 전에 a열을 제곱하여 얻은 결과입니다. 이결괏값과 apply 메서드를 적용한 결괏값을 비교해 보겠습니다.
In [9]:print(df['a']**2)
0 100 1 400 2 900 Name: a, dtype: int64
3.¶
다음은 apply 메서드에 제곱 함수의 이름을 전달하여 시리즈의 모든 데이터에 제곱 함수를 적용한 것입니다. 이때 apply 메서드에 전달하는 함수가 1개의 인자를 받도록 구성되어 있다면 인잣값을 생략해야 합니다. 그러면 2개의 인자를 전달해야 할 경우에는 어떻게 해야 할까요?
In [11]:sq=df['a'].apply(my_sq) print(sq)
0 100 1 400 2 900 Name: a, dtype: int64
4.¶
이제 2개의 인자를 전달받아야 하는 n 제곱 함수와 apply 메서드를 함께 사용해 보겠습니다. apply 메서드의 첫 번째 인자에는 n 제곱 함수의 이름을 전달하고 두 번째 인자에는 n 제곱 함수의 두 번째 인자를 전달합니다.
In [12]:ex =df['a'].apply(my_exp, n=2) print(ex)
0 100 1 400 2 900 Name: a, dtype: int64
In [13]:ex=df['a'].apply(my_exp, n=3) print(ex)
0 1000 1 8000 2 27000 Name: a, dtype: int64
5. 데이터프레임과 apply 메서드¶
이번에는 시리즈가 아니라 데이터프레임에 apply 메서드를 사용하는 방법을 알아보겠습니다. 먼저 데이터프레임을 다음과 같이 준비합니다.
In [14]:df=pd.DataFrame({'a':[10,20,30], 'b':[20,30,40]}) print(df)
a b 0 10 20 1 20 30 2 30 40
6.¶
새로운 함수를 만들어보겠습니다. 이번에 사용할 함수는 1개의 값을 전달받아 출력하는 함수입니다.
In [15]:def print_me(x): print(x)
7.¶
이번에는 데이터프레임에 함수를 적용해야 하기 때문에 함수를 열 방향으로 적용할지 행 방향으로 적용할지 정해야 합니다. axis 인잣값을 0이나 1로 지정하면 함수를 열 또는 행 방향으로 적용할 수 있습니다.
In [16]:print(df.apply(print_me,axis=0))
0 10 1 20 2 30 Name: a, dtype: int64 0 20 1 30 2 40 Name: b, dtype: int64 a None b None dtype: object
In [17]:print(df['a'])
0 10 1 20 2 30 Name: a, dtype: int64
In [18]:print(df['b'])
0 20 1 30 2 40 Name: b, dtype: int64
8.¶
이번에는 3개의 인자를 입력방아 평균을 계산하는 함수를 사용해 보겠습니다.
In [19]:def avg_3(x,y,z): return(x+y+z)/3
9.¶
그런데 avg_3 함수를 apply 메서드에 전달하면 'avg_3 함수는 3개의 인잣값을 필요로 하는 함수인데 1개의 인잣값만 입력받았다'는 오류 메시지가 출력됩니다. 즉, avg_3 함수에 열 단위 데이터가 절달되었고 이 값을 avg_3 함수에서 1개의 인자로 인식한 것입니다. 따라서 avg_3 함수가 열 단위로 데이터를 처리할 수 있도록 수정해야 합니다.
In [20]:print(df.apply(avg_3))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-20-60357ad98728> in <module> ----> 1 print(df.apply(avg_3)) ~/anaconda3/lib/python3.8/site-packages/pandas/core/frame.py in apply(self, func, axis, raw, result_type, args, **kwds) 7766 kwds=kwds, 7767 ) -> 7768 return op.get_result() 7769 7770 def applymap(self, func, na_action: Optional[str] = None) -> DataFrame: ~/anaconda3/lib/python3.8/site-packages/pandas/core/apply.py in get_result(self) 183 return self.apply_raw() 184 --> 185 return self.apply_standard() 186 187 def apply_empty_result(self): ~/anaconda3/lib/python3.8/site-packages/pandas/core/apply.py in apply_standard(self) 274 275 def apply_standard(self): --> 276 results, res_index = self.apply_series_generator() 277 278 # wrap results ~/anaconda3/lib/python3.8/site-packages/pandas/core/apply.py in apply_series_generator(self) 288 for i, v in enumerate(series_gen): 289 # ignore SettingWithCopy here in case the user mutates --> 290 results[i] = self.f(v) 291 if isinstance(results[i], ABCSeries): 292 # If we have a view on v, we need to make a copy because TypeError: avg_3() missing 2 required positional arguments: 'y' and 'z'
10.¶
다음은 avg_3 함수가 열 단위로 데이터를 처리할 수 있도록 개선한 avg_3_apply 함수입니다. 개선한 함수를 apply 메서드에 적용하면 잘 동작하는 것을 알 수 있습니다.
In [22]:def avg_3_apply(col): x=col[0] y=col[1] z=col[2] return (x+y+z)/3 print(df.apply(avg_3_apply))
a 20.0 b 30.0 dtype: float64
11.¶
앞의 과정에서는 데이터프레임의 행 개수가 3이라는 것을 알고 있다는 전제하에 avg_3_apply 함수를 작성했습니다. 하지만 일반적으로 for문을 이용하여 다음과 같이 작성합니다.
In [23]:def avg_3_apply(col): sum=0 for item in col: sum+=item return sum / df.shape[0]
12.¶
과정 11의 함수를 응용하면 행 방향으로 데이터를 처리하는 함수도 만들 수 있습니다. 마지막 return 문의 df.shape[0]을 df.shape[1]로 바꾸면 됩니다.
In [24]:def avg_2_apply(row): sum=0 for item in row: sum+=item return sum / df.shape[1] print(df.apply(avg_2_apply, axis=1))
0 15.0 1 25.0 2 35.0 dtype: float64
10-3 apply 메서드 사용하기 --고급¶
이번에는 조금 더 큰 데이터를 사용하여 실습을 진행해 보겠습니다. 이번에 사용할 데이터는 seaborn 라이브러리의 titanic 데이터 집합입니다.
In [25]:import seaborn as sns titanic=sns.load_dataset("titanic")
2.¶
다음은 titanic 데이터프레임의 데이터 정보를 출력한 것입니다.
In [27]:print(titanic.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 891 entries, 0 to 890 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 survived 891 non-null int64 1 pclass 891 non-null int64 2 sex 891 non-null object 3 age 714 non-null float64 4 sibsp 891 non-null int64 5 parch 891 non-null int64 6 fare 891 non-null float64 7 embarked 889 non-null object 8 class 891 non-null category 9 who 891 non-null object 10 adult_male 891 non-null bool 11 deck 203 non-null category 12 embark_town 889 non-null object 13 alive 891 non-null object 14 alone 891 non-null bool dtypes: bool(2), category(2), float64(2), int64(4), object(5) memory usage: 80.7+ KB None
3.¶
다음은 누락값의 개수를 반환하는 count_missing 함수입니다. 판다스의 isnull 메서드에 데이터프레임을 전달하면 누락값의 유무에 따라 True, False를 적용한 데이터프레임이 만들어 집니다. 이 값을 넘파이의 sum 메서드에 전달하면 누락값의 개수를 구할 수 있습니다.
In [29]:import numpy as np def count_missing(vec): null_vec=pd.isnull(vec) null_count=np.sum(null_vec) return null_count
4.¶
다음은 apply 메서드에 count_missing 함수를 전달하여 얻은 결과 입니다.
In [30]:cmis_col=titanic.apply(count_missing) print(cmis_col)
survived 0 pclass 0 sex 0 age 177 sibsp 0 parch 0 fare 0 embarked 2 class 0 who 0 adult_male 0 deck 688 embark_town 2 alive 0 alone 0 dtype: int64
5.¶
다음은 누락값의 비율을 계산하는 prop_missing 함수입니다. 과정 3에서 작성한 count_missing 함수를 이용해 데이터프레임의 누락값 개수를 구하고 size 속성을 이용해 데이터프레임의 전체 데이터 수를 구하여 나누면 누락값의 비율을 계산할 수 있습니다.
In [31]:def prop_missing(vec): num=count_missing(vec) dem=vec.size return num / dem
6. 다음은 apply 메서드에 prop_missing 함수를 적용한 것입니다.¶
In [33]:pmis_col=titanic.apply(prop_missing) print(pmis_col)
survived 0.000000 pclass 0.000000 sex 0.000000 age 0.198653 sibsp 0.000000 parch 0.000000 fare 0.000000 embarked 0.002245 class 0.000000 who 0.000000 adult_male 0.000000 deck 0.772166 embark_town 0.002245 alive 0.000000 alone 0.000000 dtype: float64
7.¶
과정 5에서 작성한 prio_missing 함수를 이용하면 누락값이 아닌 데이터의 비율도 구할 수 있습니다. 전체 비율에서 누락값으 비율을 빼면 됩니다. 과정 5~6과 같은 방법으로 apply 메서드에 prop_complete 함수를 전달하여 결과를 확인해 보세요
In [34]:def prop_complete(vec): return 1 - prop_missing(vec)
8. 데이터프레임의 누락값 처리하기 --행 방향¶
이번에는 행 방향으로 데이터를 처리해 보겠습니다. 다음은 axis를 1로 설정하여 앞에서 만든 count_missing, prop_missing, prop_complete 함수를 행 방향으로 적용하여 실행한 것입니다. 각 행의 누락밧과 누락값의 비율, 누락값이 아닌 값의 비율을 잘 계산하고 있다는 것을 알 수 있습니다.
In [35]:cmis_row = titanic.apply(count_missing, axis=1) pmis_row = titanic.apply(prop_missing, axis=1) pcom_row = titanic.apply(prop_complete, axis=1) print(cmis_row.head())
0 1 1 0 2 1 3 0 4 1 dtype: int64
In [36]:print(pmis_row.head())
0 0.066667 1 0.000000 2 0.066667 3 0.000000 4 0.066667 dtype: float64
In [37]:print(pcom_row.head())
0 0.933333 1 1.000000 2 0.933333 3 1.000000 4 0.933333 dtype: float64
9.¶
다음은 누락값의 개수를 구하여 titanic 데이터프레임에 추가한 것입니다. 데이터프레임에 num_missing 열이 추가된 것을 알 수 있습니다.
In [38]:titanic['num_missing']=titanic.apply(count_missing, axis=1) print(titanic.head())
survived pclass sex age sibsp parch fare embarked class \ 0 0 3 male 22.0 1 0 7.2500 S Third 1 1 1 female 38.0 1 0 71.2833 C First 2 1 3 female 26.0 0 0 7.9250 S Third 3 1 1 female 35.0 1 0 53.1000 S First 4 0 3 male 35.0 0 0 8.0500 S Third who adult_male deck embark_town alive alone num_missing 0 man True NaN Southampton no False 1 1 woman False C Cherbourg yes False 0 2 woman False NaN Southampton yes True 1 3 woman False C Southampton yes False 0 4 man True NaN Southampton no True 1
10.¶
과정 9에서 누락값이 있는 데이터를 데이터프레임에 추가했기 때문에 누락값이 있는 데이터만 따로 모아서 볼 수도 있습니다. 다음은 누락값이 2개 이상인 데이터를 추출한 것입니다.
In [40]:print(titanic.loc[titanic.num_missing > 1, :]. sample(10))
survived pclass sex age sibsp parch fare embarked class \ 511 0 3 male NaN 0 0 8.0500 S Third 17 1 2 male NaN 0 0 13.0000 S Second 28 1 3 female NaN 0 0 7.8792 Q Third 334 1 1 female NaN 1 0 133.6500 S First 300 1 3 female NaN 0 0 7.7500 Q Third 593 0 3 female NaN 0 2 7.7500 Q Third 828 1 3 male NaN 0 0 7.7500 Q Third 358 1 3 female NaN 0 0 7.8792 Q Third 560 0 3 male NaN 0 0 7.7500 Q Third 490 0 3 male NaN 1 0 19.9667 S Third who adult_male deck embark_town alive alone num_missing 511 man True NaN Southampton no True 2 17 man True NaN Southampton yes True 2 28 woman False NaN Queenstown yes True 2 334 woman False NaN Southampton yes False 2 300 woman False NaN Queenstown yes True 2 593 woman False NaN Queenstown no False 2 828 man True NaN Queenstown yes True 2 358 woman False NaN Queenstown yes True 2 560 man True NaN Queenstown no True 2 490 man True NaN Southampton no False 2
마무리하며¶
이 장에서는 여러 가지 실습을 통해 apply 메서드가 왜 데이터 분석에 유용한지 알아보았습니다. 내장 함수의 기능도 훌륭하지만 때로는 나만의 함수를 만들어 데이터 처리에 사용하는 것이 더 편리할 수도 있기 때문에 apply 메서드의 사용법은 반드시 알아두어야 합니다.
출처 : "do it 데이터 분석을 위한 판다스 입문"
'판다스' 카테고리의 다른 글
chapter-12 시계열 데이터 (0) 2021.09.23 chapter-11 그룹 연산 (0) 2021.09.23 chapter-9 문자열 처리하기 (0) 2021.09.23 chapter-8 판다스 자료형 (0) 2021.09.23 chapter-7 깔끔한 데이터 (0) 2021.09.23