def my_sq(x):
return x ** 2
2.n 제곱 함수 my_exp는 다음과 같습니다.
def my_exp(x, n):
return x ** n
3.과정 1~2에서 만든 함수를 사용해 보자.
print(my_sq(4))
16
print(my_exp(2, 4))
16
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.다음은 앞에서 만든 제곱 함수(my_exp)를 사용하기 전에 a 열을 제곱하여 얻은 결과 입니다. 이 결괏갑과 apply 메서드를 적용한 결괏값을 비교해 보겠습니다.
print(df['a'] ** 2)
0 100 1 400 2 900 Name: a, dtype: int64
3.다음은 apply 메서드에 제곱 함수의 이름(my_sq)을 전달하여 시리즈의 모든 데이터에 제곱 함수를 적용한 것입니다. 이때 apply 메서드에 전달하는 함수(my_sq)가 1개의 인자를 받도록 구성되어 있다면 인잣값을 생략해야 합니다. 그러면 2개의 인자를 전달해야 할 경우에는 어떻게 해야 할까?
sq = df['a'].apply(my_sq)
print(sq)
0 100 1 400 2 900 Name: a, dtype: int64
4.이제 2개의 인자를 전달받아야 하는 n 제곱 함수(my_exp)와 apply 메서드를 함께 사용해 보겠습니다. apply 메서드의 첫 번째 인자에는 n 제곱 함수의 이름(my_exp)을 전달하고 두 번째 인자에는 n 제곱 함수의 두 번째 인자(n)를 전달합니다.
ex = df['a'].apply(my_exp, n=2)
print(ex)
0 100 1 400 2 900 Name: a, dtype: int64
ex = df['a'].apply(my_exp, n=3)
print(ex)
0 1000 1 8000 2 27000 Name: a, dtype: int64
5.데이터프레임과 apply 메서드 이번에는 시리즈가 아니라 데이터프레임에 apply 메서드를 사용하는 방법을 알아보겠습니다. 먼저 데이터프레임을 다음과 같이 준비합니다.
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개의 값을 전달받아 출력하는 함수입니다.
def print_me(x):
print(x)
7.이번에는 데이터프레임에 함수를 적용해야 하기 때문에 함수를 열 방향으로 적용할지 행 방향으로 적용할지 정해야 합니다. axis 인잣값을 0이나 1로 지정하면 함수를 열 또는 행 방향으로 적용할 수 있습니다.
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
print(df['a'])
0 10 1 20 2 30 Name: a, dtype: int64
print(df['b'])
0 20 1 30 2 40 Name: b, dtype: int64
8.이번에는 3개의 인자를 입력받아 평균을 계산하는 함수를 사용해 보겠습니다.
def avg_3(x, y, z):
return (x + y + z) / 3
9.그런데 avg_3 함수를 apply 메서드에 전달하면 'avg_3 함수는 3개의 인잣값을 필요로 하는 함수인데 1개의 인잣값만 입력받았다'는 오류 메시지가 출력됩니다. 즉, avg_3함수에 열 단위 데이터(df['a'] 또는 df['b'])가 전달되었고 이 값을 avg_3 함수에서 1개의 인자로 인식한 것입니다. 따라서 avg_3 함수가 열 단위로 처리할 수 있도록 수정해야 합니다.
print(df.apply(avg_3))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-19-60357ad98728> in <module> ----> 1 print(df.apply(avg_3)) ~/.local/lib/python3.6/site-packages/pandas/core/frame.py in apply(self, func, axis, raw, result_type, args, **kwds) 7550 kwds=kwds, 7551 ) -> 7552 return op.get_result() 7553 7554 def applymap(self, func) -> "DataFrame": ~/.local/lib/python3.6/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): ~/.local/lib/python3.6/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 ~/.local/lib/python3.6/site-packages/pandas/core/apply.py in apply_series_generator(self) 303 for i, v in enumerate(series_gen): 304 # ignore SettingWithCopy here in case the user mutates --> 305 results[i] = self.f(v) 306 if isinstance(results[i], ABCSeries): 307 # 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 메서드에 적용하면 잘 동작하는 것을 알 수 있습니다.
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문을 이용하여 다음과 같이 작성합니다.
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}로 바꾸면 됩니다.
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
import seaborn as sns
titanic = sns.load_dataset("titanic")
2.다음은 titanic 데이터프레임의 데이터 정보를 출력한 것입니다.
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.6+ KB None
3.다음은 누락값의 개수를 반환하는 count_missing 함수입니다. 판다스의 isnull 메서드에 데이터프레임을 전달하면 누락값의 유무에 따라 True, False를 적용한 데이터프레임이 만들어집니다. 이 값을 넘파이의 sum 메서드에 전달하면 누락값의 개수를 구할 수 있습니다.
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 함수를 전달하여 얻은 결과입니다.
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 속성을 이용해 데이터프레임의 전체 데이터 수를 구하여 나누면 누락값의 비율을 계산할 수 있습니다.
def prop_missing(vec):
num = count_missing(vec)
dem = vec.size
return num / dem
6.다음은 apply 메서드에 prop_missing 함수를 적용한 것입니다.
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에서 작정한 prop_missing 함수를 이용하면 누락값이 아닌 데이터의 비율도 구할 수 있습니다. 전체 비율(1)에서 누락값의 비율을 빼면 됩니다. 과정 5~6과 같은 방법으로 apply 메서드에 prop_complete 함수를 전달하여 결과를 확인해 보자.
def prop_complete(vec):
return 1 - prop_missing(vec)
이번에는 행 방향으로 데이터를 처리해 보겠습니다. 다음은 axis를 1로 설정하여 앞에서 만든 count_missing, prop_missing, prop_complete 함수를 행 방향으로 적용하여 실행한 것입니다. 각 행의 누락값과 누락값의 비율, 누락값이 아닌 값의 비율을 잘 계산하고 있다는 것을 알 수 있습니다.
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
print(pmis_row.head())
0 0.066667 1 0.000000 2 0.066667 3 0.000000 4 0.066667 dtype: float64
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 열이 추가된 것을 알 수 있습니다.
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개 이상인 데이터를 추출한 것입니다.
print(titanic.loc[titanic.num_missing > 1, :].sample(10))
survived pclass sex age sibsp parch fare embarked class \ 593 0 3 female NaN 0 2 7.7500 Q Third 578 0 3 female NaN 1 0 14.4583 C Third 274 1 3 female NaN 0 0 7.7500 Q Third 29 0 3 male NaN 0 0 7.8958 S Third 878 0 3 male NaN 0 0 7.8958 S Third 61 1 1 female 38.0 0 0 80.0000 NaN First 859 0 3 male NaN 0 0 7.2292 C Third 201 0 3 male NaN 8 2 69.5500 S Third 235 0 3 female NaN 0 0 7.5500 S Third 792 0 3 female NaN 8 2 69.5500 S Third who adult_male deck embark_town alive alone num_missing 593 woman False NaN Queenstown no False 2 578 woman False NaN Cherbourg no False 2 274 woman False NaN Queenstown yes True 2 29 man True NaN Southampton no True 2 878 man True NaN Southampton no True 2 61 woman False B NaN yes True 2 859 man True NaN Cherbourg no True 2 201 man True NaN Southampton no False 2 235 woman False NaN Southampton no True 2 792 woman False NaN Southampton no False 2
여러가지 실습을 통해 apply 메서드가 왜 데이터 분석에 유용한지 알아보았습니다.
판다스, 시계열 데이터 (0) | 2021.04.01 |
---|---|
판다스, 그룹연산 (0) | 2021.03.31 |
판다스, 문자열 처리하기 (0) | 2021.03.30 |
판다스, 깔끔한 데이터 (1) | 2021.03.28 |
판다스, 누락값 처리하기 (0) | 2021.03.27 |