Do it! 판다스 입문

Chapter 10 apply 메서드 활용

판다조아 2022. 6. 25. 16:13

간단한 함수 만들기

이 책은 파이썬의 기초 지식을 어느 정도 아는 독자를 대상으로 집필되었습니다. 따라서 함수는 간단히 설명하고 넘어갑니다. 함수의 기본 구조는 다음과 같습니다.

def my_function( ):
    # 여기부터 코드를 입력합니다.

 

그러면 제곱 함수와 n 제곱 함수를 직접 만들어보겠습니다. 제곱 함수와 n 제곱 함수는 10-2에서 apply 메서드와 함께 사용합니다. 여기에서 꼭 함수를 만들어보고 다음으로 넘어가세요.

 

 

제곱 함수와 n 제곱 함수 만들기

1. 제곱 함수는 다음과 같습니다.

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

 

 

 

 

 

apply 메서드 사용하기 - 기초

다음 실습을 통해 apply 메서드의 사용법을 알아보겠습니다. apply 메서드로 브로드캐스팅한 결과가 올바른 값인지 확인하기 위해 시리즈, 데이터프레임의 기초 연산 결과와 비교하며 실습을 진행하겠습니다.

 

 

시리즈와 데이터프레임에 apply 메서드 사용하기

1. 시리즈와 apply 메서드

먼저 실습에 사용할 데이터프레임을 준비합니다.

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-28-60357ad98728> in <module>( )
----> 1 print(df.apply(avg_3))

TypeError: ("avg_3( ) missing 2 required positional arguments: 'y' and 'z'", 'oc-curred at index a')

 

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

 

 

 

 

 

apply 메서드 사용하기 - 고급

이번에는 조금 더 큰 데이터를 사용하여 실습을 진행해 보겠습니다. 이번에 사용할 데이터는 seaborn 라이브러리의 titanic 데이터 집합입니다.

 

 

데이터프레임의 누락값을 처리한 다음 apply 메서드 사용하기

1. 데이터프레임의 누락값 처리하기 - 열 방향

이번에도 seaborn 라이브러리에 있는 데이터를 사용합니다. seaborn 라이브러리에 있는 titanic 데이터 집합을 불러와 변수 titanic에 저장합니다.

import seaborn as sns

titanic = sns.load_dataset("titanic")

 

2. 다음은 titanic 데이터프레임의 데이터 정보를 출력한 것입니다.

print(titanic.info( ))

<class 'pandas.core.fram.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data clumns (total 15 columns):
survived            891 non-null int64
pclass               891 non-null int64
sex                    891 non-null object
age                   714 non-null float64
sibsp                 891 non-null int64
parch                891 non-null int64
fare                   891 non-null float64
embarked         889 non-null object
class                 891 non-null category
who                   891 non-null object
adult_male        891 non-null bool
deck                  203 non-null category
embark_town    889 non-null object
alive                  891 non-null object
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                     0
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)

 

8. 데이터프레임의 누락값 처리하기 - 행 방향

이번에는 행 방향으로 데이터를 처리해 보겠습니다. 다음은 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      Ture                      1

 

10. 과정 9에서 누락값이 있는 데이터를 데이터프레임에 추가했기 때문에 누락값이 있는 데이터만 따로 모아서 볼 수도 있습니다. 다음은 누락값이 2개 이상인 데이터를 추출한 것입니다.

print(titanic.loc[titanic.num_missing > 1, :].sample(10))

        survived    pclass        sex      age    sibsp    parch           fare   embarked       class    who    \
732             0            2       male    NaN           0           0      0.0000                 S   Second   man
101             0            3       male    NaN           0           0      7.8958                 S       Third   man
766             0            1       male    NaN           0           0    39.6000                 C        First   man
709             1            3       male    NaN           1           1    15.2458                 C       Third   man
107             1            3       male    NaN           0           0      7.7750                 S       Third   man
563             0            3       male    NaN           0           0      8.0500                 S       Third   man
629             0            3       male    NaN           0           0      7.7333                 Q       Third   man
531             0            3       male    NaN           0           0      7.2292                 C       Third   man
718             0            3       male    NaN           0           0    15.5000                 Q       Third   man
511             0            3       male    NaN           0           0      8.0500                 S       Third   man

         adult_male    deck   embark_town   alive    alone   num_missing
732             True    NaN    Southampton      no      True                      2
101             True    NaN    Southampton      no      True                      2
766             True    NaN        Cherbourg      no      True                      2
709             True    NaN        Cherbourg     yes    False                      2
107             True    NaN    Southampton     yes     Ture                      2
563             True    NaN    Southampton      no      True                      2
629             True    NaN     Queenstown      no      True                      2
531             True    NaN        Cherbourg      no      True                      2
718             True    NaN     Queenstown      no      True                      2
511             True    NaN    Southampton      no      True                      2

 

 

 

 

 

마무리하며

이 장에서는 여러 가지 실습을 통해 apply 메서드가 왜 데이터 분석에 유용한지 알아보았습니다. 내장 함수의 기능도 훌륭하지만 때로는 나만의 함수를 만들어 데이터 처리에 사용하는 것이 더 편리할 수도 있기 때문에 apply 메서드의 사용법은 반드시 알아두어야 합니다.

 

 

 

 

 

출처 : "판다스 입문"