ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Chapter 11-1~11-2 데이터 집계, 데이터 변환
    Do it! 판다스 입문 2022. 6. 25. 17:56

    데이터 집계하기 - groupby 메서드

    02장에서 갭마인더 데이터 집합으로 각 연도의 평균 수명을 구했던 것을 기억하나요? 수집한 데이터를 바탕으로 평균이나 합 등을 구하여 의미 있는 값을 도출해 내는 것을 '집계'라고 합니다. 데이터를 집계하면 전체 데이터를 요약, 정리하여 볼 수 있기 때문에 데이터 분석이 훨씬 편해지죠. 그러면 groupby 메서드로 평균값을 구하는 과정을 통해 데이터 집계가 무엇인지 알아보겠습니다.

     

     

    groupby 메서드로 평균값 구하기

    1. 먼저 갭마인더 데이터 집합을 불러옵니다.

    import pandas as pd
    df = pd.read_csv('../data/gapminder.tsv', sep='\t')

     

    2. 다음은 year 열을 기준으로 데이터를 그룹화한 다음 lifeExp 열의 평균을 구한 것입니다.

    avg_life_exp_by_year = df.groupby('year').lifeExp.mean( )
    print(avg_life_exp_by_year)

    year
    1952      49.057620
    1957      51.507401
    1962      53.609249
    ...
    1997      65.014676
    2002      65.694923
    2007      67.007423
    Name: lifeExp, dtype: float64

     

     

     

     

     

    분할-반영-결합 과정 살펴보기 - groupby 메서드

    앞에서 groupby 메서드를 사용해 lifeExp 열의 연도별 평균값을 구했습니다. 그러면 실제로 groupby 메서드는 어떤 과정을 통해 데이터를 집계할까요? groupby 메서드 자체를 분해하여 살펴보는 것은 불가능하기 때문에 비슷한 연산을 수행하는 메서드를 순서대로 실행하며 알아보겠습니다.

     

     

    분할-반영-결합 과정 살펴보기

    1. 실제로 groupby 메서드에 life 열을 전달하면 가장 먼저 연도별로 데이터를 나누는 과정이 진행됩니다. 다음은 year 열의 데이터를 중복 없이 추출한 것입니다. groupby 메서드에 열 이름을 전달하면 이런 '분할' 작업이 먼저 일어난다고 이해하세요.

    years = df.year.unique( )
    print(years)

    [1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 2002 2007]

     

    2. 그런 다음에는 연도별로 평균값을 구합니다. 그러려면 일단 각 연도별로 데이터를 추출해야겠죠? 다음은 1952년의 데이터를 추출한 것입니다. 이 과정을 '반영' 작업의 한 부분이라고 이해하세요.

    y1952 = df.loc[df.year == 1952, :]
    print(y1952.head( ))

                 country    continent    year    lifeExp               pop        gdpPercap
    0    Afghanistan           Asia    1952    28.801       8425333      779.445314
    12         Albania      Europe    1952    55.230       1282697    1601.056136
    24          Algeria         Africa    1952    43.077      9279525    2449.008185
    36          Angola         Africa    1952    30.015      4232095    3520.610273
    48      Argentina   Americas    1952    62.485    17876956    5911.315053

     

    3. 아직 lifeExp 열의 평균값을 구하지 않았습니다. 다음은 과정 2에서 추출한 1952년의 데이터에서 lifeExp 열의 평균값을 구한 것입니다. 이 과정도 '반영' 작업의 한 부분입니다.

    y1952_mean = y1952.lifeExp.mean( )
    print(y1952_mean)

    49.057619718309866

     

    4. 과정 2~3을 반복하여 남은 연도의 평균값을 구하면 비로소 '반영' 작업이 끝납니다.

    y1957 = df.loc[df.year == 1957, :]
    y1957_mean = y1957.lifeExp.mean( )
    print(y1957_mean)

    51.50740112676054

    ...

    y2007 = df.loc[df.year == 2007, :]
    y2007_mean = y2007.lifeExp.mean( )
    print(y2007_mean)

    67.00742253521126

     

    5. 마지막으로 연도별로 계산한 lifeExp의 평균값을 합칩니다. 바로 이 과정이 '결합' 작업입니다.

    df2 = pd.DataFrame({"year":[1952, 1957, 1962, 2007],
                       "":[y1952_mean, y1957_mean, y1962_mean, y2007_mean]})
    print(df2)

          year
    0   1952   49.057620
    1   1957   51.507401
    2   1962   53.609249
    3   2007   67.007423

     

     

     

     

     

    groupby 메서드와 함께 사용하는 집계 메서드

    다음은 집계 메서드를 정리한 표입니다. 이후에 수행할 실습에서 자주 등장하는 메서드이니 미리 읽어보고 넘어가는 것이 좋습니다.

     

     

    집계 메서드

    메서드 설명
    count 누락값을 제외한 데이터 수를 반환
    size 누락값을 포함한 데이터 수를 반환
    mean 평균값 반환
    std 표준편차 반환
    min 최솟값 반환
    quantile(q=0.25) 백분위수 25%
    quantile(q=0.50) 백분위수 50%
    quantile(q=0.75) 백분위수 75%
    max 최댓값 반환
    sum 전체 합 반환
    var 분산 반환
    sem 평균의 표준편차 반환
    describe 데이터 수, 평균, 표준편차, 최소값, 백분위수(25, 50, 75%), 최댓값을 모두 반환
    first 첫 번재 행 반환
    last 마지막 행 반환
    nth n번재 행 반환

     

     

     

     

     

    agg 메서드로 사용자 함수와 groupby 메서드 조합하기

    라이브러리에서 제공하는 집계 메서드로 원하는 값을 계산할 수 없는 경우에는 직접 함수를 만들어서 사용해야 합니다. 이번에는 사용자 함수와 groupby 메서드를 조합해서 사용해 보겠습니다. 사용자 함수와 groupby 메서드를 조합하려면 agg 메서드를 이용해야 합니다.

     

     

    평균값을 구하는 사용자 함수와 groupby 메서드

    1. 다음은 입력받은 열의 평균값을 구하는 함수입니다.

    def my_mean(values):
        n = len(values)
            sum = 0
        for value in values:
            sum += value
            
        return sum / n

     

    2. 다음은 과정 1에서 만든 함수를 groupby 메서드와 조합하기 위해 agg 메서드를 사용한 것입니다. 결과를 보면 mean 메서드를 사용하여 얻은 값과 동일하다는 것을 알 수 있습니다.

    agg_my_mean = df.groupby('year').lifeExp.agg(my_mean)
    print(agg_my_mean)

    year
    1952      49.057620
    1957      51.507401
    1962      53.609249
    1967      55.678290
    1972      57.647386
    1977      59.570157
    1982      61.533197
    1987      63.212613
    1992      64.160338
    1997      65.014676
    2002      65.694923
    2007      67.007423
    Name: lifeExp, dtype: float64

     

     

     

    2개의 인잣값을 받아 처리하는 사용자 함수와 grooupby 메서드

    1. 이번에는 2개의 인잣값을 받아 처리하는 사용자 정의 함수(my_mean_diff)를 만들어보겠습니다. 다음은 첫 번째 인자로 받은 열의 평균값을 구하여 두 번째 인자로 받은 값과의 차이를 계산한 다음 반환하는 함수입니다.

    def my_mean_diff(values, diff_value):
        n = len(values)
        sum = 0
        for value in values:
            sum += value
        mean = sum / n
        return mean - diff_value

     

    2. 다음은 연도별 평균 수명에서 전체 평균 수명을 뺀 값을 구하는 것입니다. agg 메서드의 첫 번째 인자에 my_mean_diff 함수를 전달하고 두 번째 인자에 전체 평균 수명값을 전달했습니다.

    global_mean = df.lifeExp.mean( )
    print(global_mean)

    59.47443936617174

    agg_mean_diff = df.groupby('year').lifeExp.agg(my_mean_diff, diff_value=global_mean)
    print(agg_mean_diff)

    year
    1952     -10.416820
    1957       -7.967038
    1962       -5.865190
    ...
    1997        5.540237
    2002        6.220483
    2007        7.532983
    Name: lifeExp, dtype: float64

     

     

     

     

     

    여러 개의 집계 메서드 한 번에 사용하기

    여러 개의 집계 메서드를 한 번에 사용하고 싶다면 어떻게 해야 할까요? 집계 메서드를 리스트나 딕셔너리에 담아 agg 메서드에 전달하면 됩니다.

     

     

    집계 메서드를 리스트, 딕셔너리에 담아 전달하기

    1. 다음은 연도별로 그룹화한 lifeExp 열의 0이 아닌 값의 개수, 평균, 표준편차를 한 번에 계산하여 출력한 것입니다. 넘파이 메서드인 count_nonzero, mean, std를 리스트에 담아 agg 메서드에 전달했습니다.

    import numpy as np
    gdf = df.groupby('year').lifeExp.agg([np.count_nonzero, np.mean, np.std])
    print(gdf)

               count_nonzero           mean               std
    year  
    1952                  142.0   49.057620   12.225956
    1957                  142.0   51.507401   12.231286
    1962                  142.0   53.609249   12.097245
    1967                  142.0   55.678290   11.718858
    1972                  142.0   57.647386   11.381953
    1977                  142.0   59.570157   11.227229
    1982                  142.0   61.533197   10.770618
    1987                  142.0   63.212613   10.556285
    1992                  142.0   64.160338   11.227380
    1997                  142.0   65.014676   11.559439
    2002                  142.0   65.694923   12.279823
    2007                  142.0   67.007423   12.073021

     

    2. 이번에는 집계 메서드를 딕셔너리에 담아 agg 메서드에 전달해 보겠습니다. 딕셔너리의 키로 집계 메서드를 적용할 열 이름을 전달하고 딕셔너리의 값으로 집계 메서드를 전달하면 됩니다.

    gdf_dict = df.groupby('year').agg({'lifeExp': 'mean', 'pop': 'median', 'gdpPer-cap': 'median'})
    print(gdf_dict)

                     lifeExp                 pop       gdpPercap
    year
    1952   49.057620      3943953.0    1968.528344
    1957   51.507401      4282942.0    2173.220291
    1962   53.609249      4686039.5    2335.439533
    ...
    2002   65.694923    10372918.5    5319.804524
    2007   67.007423    10517531.0    6124.371109 

     

     

     

     

     

    데이터 변환

    11-1에서는 집계 메서드의 활용법을 알아보았습니다. 이번에는 데이터 변환 메서드에 대해 알아보겠습니다. 데이터 변환 메서드는 데이터와 메서드를 일대일로 대응시켜 계산하기 때문에 데이터의 양이 줄어들지 않습니다. 말 그대로 데이터를 변환하는 데 사용합니다.

     

     

    표준점수 계산하기

    통계 분야에서는 데이터의 평군과 표준편차의 차이를 표준점수라고 부릅니다. 표준점수를 구하면 변환한 데이터의 평균값이 0이 되고 표준편차는 1이 됩니다. 그러면 데이터가 표준화되어 서로 다른 데이터를 쉽게 비교할 수 있게 되죠. 표준점수는 통계에서 자주 사용하는 지표입니다.

     

     

    표준점수 계산하기

    1. 다음은 표준점수를 계산하는 함수입니다.

    def my_zscore(x):
        return (x - x.mean( )) / x.std( )

     

    2. 다음은 각 연도별 lifeExp 열의 표준점수를 계산한 것입니다. my_zscore 함수를 적용하기 위해 transform 메서드를 사용했습니다.

    transform_z = df.groupby('year').lifeExp.transform(my_zscore)

    print(transform_z.head( ))

    0     -1.656854
    1     -1.731249
    2     -1.786543
    3     -1.848157
    4     -1.894173
    Name: lifeExp, dtype: float64

     

    3. my_zscore 함수는 데이터를 표준화할 뿐 집계는 하지 않습니다. 즉, 데이터의 양이 줄어들지 않습니다. 다음은 원본 데이터프레임(df)의 데이터 크기와 변환한 데이터프레임(transform_z)의 데이터 크기를 비교한 것입니다.

    print(df.shape)

    (1704, 6)

    print(transform_z.shape)

    (1704, )

     

     

     

     

     

    누락값을 평균값으로 처리하기

    06장에서 누락값을 처리하는 다양한 방법에 대해 살펴봤습니다. 하지만 가끔은 누락값을 평균값으로 처리하는 것이 더 좋을 때가 있습니다. 이번에는 누락값을 평균값으로 처리하는 방법을 알아보겠습니다.

     

     

    누락값을 평균값으로 처리하기

    1. 다음은 seaborn 라이브러리의 tips 데이터 집합에서 10개의 행 데이터만 가져온 다음 total_bill 열의 값 4개를 임의로 선택하여 누락값으로 바꾼 것입니다.

    import seaborn as sns
    import numpy as np

    np.random.seed(42)

    tips_10 = sns.load_dataset('tips').sample(10)
    tips_10.loc[np.random.permutation(tips_10.index)[:4], 'total_bill'] = np.NaN
    print(tips_10)

          total_bill       tip          sex   smoker      day       time    size
    24      19.82    3.18        Male          No      Sat    Dinner        2
    6          8.77    2.00        Male          No      Sun   Dinner        2
    153      NaN    2.00        Male          No      Sun   Dineer        4
    211      NaN    5.16        Male         Yes      Sat    Dinner        4
    198      NaN    2.00    Female         Yes    Thur    Lunch        2
    176      NaN    2.00        Male         Yes     Sun    Dinner        2
    192    28.44    2.56        Male         Yes    Thur    Lunch         2
    124    12.48    2.52    Female          No    Thur    Lunch         2
    9        14.78    3.23        Male          No     Sun    Dinner        2
    101    15.38    3.00    Female         Yes       Fri    Dinner        2

     

    2. 그런데 total_bll 열의 누락값을 단순히 total_bill 열의 평균값으로 채우면 안 됩니다. 무슨 말일까요? 현재 tips_10의 데이터는 여성보다 남성이 더 많습니다. 즉, 여성과 남성을 구분하여 total_bill 열의 평균값을 구하지 않으면 여성 데이터가 남성 데이터의 영향을 많이 받아 여성의 데이터가 훼손될 수 있습니다. 다음은 성별로 그룹화한 다음 각 열의 데이터 수를 구한 것입니다. total_bill 열을 살펴보면 남성의 누락값은 3개, 여성의 누락값은 1개라는 것을 알 수 있습니다.

    count_sex = tips_10.groupby('sex').count( )
    print(count_sex)

                   total_bill    tip    smoker    day    time    size
    sex
    Male                  4      7              7        7         7        7
    Female              2      3              3        3         3        3 

     

    3. 다음은 성별을 구분하여 total_bill 열의 데이터를 받아 평균값을 구하는 함수입니다.

    def fill_na_mean(x):
        avg = x.mean( )
        return x.fillna(avg)

     

    4. 다음은 성별을 구분한 total_bill 열의 데이터를 fill_na_mean 함수에 전달하여 평균값을 구한 다음 tips_10에 새로운 열로 추가한 것입니다. 남성과 여성의 누락값을 고려하여 계산한 평균값으로 잘 채워져 있는 것을 알 수 있습니다.

    total_bill_group_mean = tips_10.groupby('sex').total_bill.transform(fill_na_mean)
    tips_10['fill_total_bill'] = total_bill_group_mean
    print(tips_10)

          total_bill       tip          sex   smoker      day       time    size   fill_total_bill
    24      19.82    3.18        Male          No      Sat    Dinner        2        19.8200
    6          8.77    2.00        Male          No      Sun   Dinner        2          8.7700
    153      NaN    2.00        Male          No      Sun   Dineer        4        17.9525
    211      NaN    5.16        Male         Yes      Sat    Dinner        4        17.9525
    198      NaN    2.00    Female         Yes    Thur    Lunch        2         13.9300
    176      NaN    2.00        Male         Yes     Sun    Dinner        2        17.9525
    192    28.44    2.56        Male         Yes    Thur    Lunch         2        28.4400
    124    12.48    2.52    Female          No    Thur    Lunch         2        12.4800
    9        14.78    3.23        Male          No     Sun    Dinner        2        14.7800
    101    15.38    3.00    Female         Yes       Fri    Dinner        2        15.3800

     

     

     

     

     

     

    출처 : "판다스 입문"

Designed by Tistory.