All Articles

Pandas에서 Aggregate하기

이번 포스팅에서는 Pandas 의 Aggregates에 대해서 알아봅시다. Aggregates는 숫자로 구성된 특정 그룹을 설명할 수 있는 단일 숫자를 만들어내는 강력한 방법입니다. 일반적으로 평균, 표준편차, 중간값 등이 자주 사용됩니다.

Calculating Column Statistics

저번 포스팅에서는 apply 함수를 활용하여 특정 column 의 각 value 에 대해 연산을 수행하는 방법에 대해서 알아보았습니다. 이번에는 모든 value 에 대해서 연산을 수행하는 방법에 대해서 알아봅시다.

먼저 손님들의 정보를 저장한 customersDataFrame이 있다고 가정해봅시다. 만약 손님들의 나이의 중간값을 구하고자 한다면 다음과 같이 코드를 작성할 수 있습니다.

print(customers.age)
>> [23, 25, 31, 35, 35, 46, 62]
print(customers.age.median())
>> 35	# 중간값을 반환합니다.

이번에는 물류 배송 정보를 저장한 shipmentsDataFrame이 있다고 가정해봅시다. 만약 특정 배송지들이 각각 얼마나 배송되었는지를 구하고자 한다면 다음과 같이 코드를 작성할 수 있습니다.

print(shipments.state)
>> ['CA', 'CA', 'CA', 'CA', 'NY', 'NY', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ', 'NJ']
print(shipments.state.nunique())
>> 3	# unique한 값의 수를 반환합니다.

이번에는 옷가게에서 T-shirt 정보를 저장하는 inventoryDataFrame이 있다고 가정해봅시다. T-shirt 의 색깔로 어떤 것들이 있는지를 구하고자 한다면 다음과 같이 코드를 작성할 수 있습니다.

print(inventory.color)
>> ['blue', 'blue', 'blue', 'blue', 'blue', 'green', 'green', 'orange', 'orange', 'orange']
print(inventory.color.unique())
>> ['blue', 'green', 'orange']	# unique한 값들을 반환합니다.

이러한 syntax 를 일반화하면 다음과 같습니다.

df.column_name.command()

자주 쓰이는 command 를 정리한 표는 아래와 같습니다.

Command Description
mean 특정 column 의 모든 value 의 평균값
std 표준편차
median 중간값
max 특정 column 내 최대값
min 특정 column 내 최소값
count 특정 column 내 value 의 개수
nunique 특정 column 내 unique 한 value 의 개수
unique 특정 column 내 unique 한 value 의 배열

Calculating Aggregate Functions

많은 데이터를 보유하게 된다면, 데이터의 특정 부분에 대해서 aggregates 연산을 수행하고 싶을 때가 종종 있습니다. 예를 들어 다음과 같은 DataFrame이 있다고 가정해봅시다.

student assignment_name grade
Amy Assignment 1 75
Amy Assignment 2 35
Bob Assignment 1 99
Bob Assignment 2 35

각 학생들의 평균 grade 를 계산하고 싶다고 해봅시다. 이를 반복문으로도 실행할 수 있지만, Pandas 의 groupby 함수를 활용하면 매우 쉽게 연산이 가능합니다.

grades = df.groupby('student').grade.mean()
print(grades)

위 코드의 실행 결과는 다음과 같습니다.

student grade
Amy 80
Bob 90
Chris 75

.groupby 함수의 일반적인 syntax 는 다음과 같습니다.

df.groupby('column1').column2.measurement()
  • column1 은 그룹으로 만들고자 하는 column 입니다.
  • column2는 measurement 를 수행하고자 하는 column 입니다(여기서는 grade 가 됩니다).
  • measurement 는 적용하고자 하는 함수입니다(여기서는 mean 입니다).

groupby 함수는 결과값으로 DataFrame이 아닌 Series를 반환합니다. 이 때 반환된 Series의 index 는 groupby 함수에 주어진 column 의 value 이며, name 속성은 measurement 가 수행된 column 의 name 입니다.

그런데 column 의 value 를 index 로 사용하는 것보단 정수값이 주어지는 것이 일반적으로 보기 더 좋습니다. 이를 위해 reset_index() 함수를 사용할 수 있는데, 결과적으로 SeriesDataFrame으로 변환하고 기존의 index 를 column 으로 만들어버립니다.

이러한 장점으로 인해 일반적으로 groupby 함수 뒤에는 reset_index 함수가 뒤따라옵니다.

df.groupby('column1').column2.measurement()
    .reset_index()

이해를 돕기 위해 다음과 같이 찻집의 Tea 정보를 저장하는 DataFrame이 있다고 가정해봅시다.

id tea category caffeine price
0 earl grey black 38 3
1 english breakfast black 41 3
2 irish breakfast black 37 2.5
3 jasmine green 23 4.5
4 matcha green 48 5
5 camomile herbal 0 3

Tea 가 category 별로 얼마나 있는지 확인해보고자 한다면 다음과 같이 코드를 작성할 수 있습니다.

teas_counts = teas.groupby('category').id.count().reset_index()
print(teas_counts)

위 코드의 실행 결과는 다음과 같습니다.

category id
0 black 3
1 green 4
2 herbal 8
3 white 2

reset_index 를 호출함으로써 기존의 index 인 category 가 새로운 column 으로 형성된 것을 확인할 수 있습니다. 한 발짝만 더 나가자면, 각 category 별 tea 의 수를 나타내는 컬럼의 name 이 id 인데, 보기 좋게 하기 위해 column 이름을 변경해 줄 수 있습니다.

teas_counts = teas_counts.rename(columns={"id": "counts"})

이제 DataFrame은 다음과 같습니다.

category counts
0 black 3
1 green 4
2 herbal 8
3 white 2

Apply with Lambda

가끔은 mean 이나 count 말고 더 복잡한 연산을 필요로 할 때가 있습니다. 이러한 경우, apply 함수를 사용하여 파라미터로 lambda 함수를 제공할 수 있습니다. Column 연산을 수행할때 처럼 말이죠. 한가지 명심해야 할 것은 Aggregates 단계의 apply 함수의 파라미터로 주어지는 lambda 함수의 input 은 column 의 값들로 구성된 list 란 것입니다.

이해를 돕기 위해 어떤 회사의 employee 들의 id, 이름, 임금, 직종을 나타내는 다음과 같은 DataFrame이 있다고 가정해봅시다.

id name wage category
10131 Sarah Carney 39 product
14189 Heather Carey 17 design
15004 Gary Mercado 33 marketing
11204 Cora Copaz 27 design

만약 각 catogory 별로 75th percentile(75%의 employee 보다 임금이 높고 25%의 employee 보다 임금이 낮은 지점)의 임금을 구하고자 한다면 다음과 같이 코드를 작성할 수 있습니다.

# np.percentile 함수는 입력으로 주어진 list에서 특정 지점의 percentile을 계산합니다.
high_earners = df.groupby('category').wage.apply(lambda wages: np.percentile(wages, 75)).reset_index()

계산된 high_earners 는 다음과 같이 됩니다.

category wage
0 design 23
1 marketing 35
2 product 48

Multiple Groupby

가끔은 여러 column 을 group 하는 경우도 생기게 됩니다. 이는 간단히 groupby 함수의 파라미터로 column 의 name 을 list 로 전달해주면 됩니다. 예를 들어 레스토랑 체인점의 판매 기록을 저장하는 다음과 같은 DataFrame이 있다고 생각해봅시다.

Location Date Day of Week Total Sales
West Village February 1 W 400
West Village February 2 Th 450
Chelsea February 1 W 375
Chelsea February 2 Th 390

만약 각 Location 의 Day of Week 별 Total Sales 의 평균을 계산하고 싶다면 다음과 같이 코드를 작성할 수 있습니다.

df.groupby(['Location', 'Day of Week'])['Total Sales'].mean().reset_index()

결과는 다음과 같게 됩니다.

Location Day of Week Total Sales
Chelsea M 402.50
Chelsea Tu 422.75
Chelsea W 452.00
West Village M 390
West Village Tu 400

Pivot Tables

groupby 를 수행하고 난 뒤, 가끔 데이터를 저장하는 방식을 변경하고 싶을수도 있습니다. 바로 이전의 레스토랑 DataFrame의 경우, 다음과 같이 표현할 수 있다면 더 가독성이 높아질 것입니다.

Location M Tu W Th F Sa Su
Chelsea 400 390 250 275 300 150 175
West Village 300 310 350 400 390 250 200

이렇게 테이블을 reorganizing 하는 방법을 pivoting 이라고 합니다. 그리하여 새롭게 생성된 테이블을 pivot table 이라고 부릅니다. Pandas 에서는 다음과 같이 pivot 을 수행할 수 있습니다.

df.pivot(columns='ColumnToPivot',
         index='ColumnToBeRows',
         values='ColumnToBeValues')

레스토랑 DataFrame의 경우 다음과 같이 코드를 작성할 수 있습니다.

# 먼저 groupby 연산을 수행합니다.
unpivoted = df.groupby(['Location', 'Day of Week'])['Total Sales'].mean().reset_index()
# 그런 다음 pivot table을 만듭니다.
pivoted = unpivoted.pivot(
    columns='Day of Week',
    index='Location',
    values='Total Sales')

pivot 함수는 실행 결과로 DataFrame을 반환합니다. 이때 indexing 이 이상하게 만들어지는 경향이 있어서, 일반적으로 pivot 뒤에도 reset_index 를 호출해줍니다.

Review

이번 포스팅에서는 Pandas 의 Aggregates에 대한 많은 내용을 다루어보았습니다. 그 내용을 정리해보면 다음과 같습니다:

  • How to perform aggregate statistics over individual rows with the same value using groupby.
  • How to rearrange a DataFrame into a pivot table, a great way to compare data across two dimensions.

Pandas 는 공부할수록 재미있는 라이브러리인 것 같습니다. 얼른 실생활에 널려있는 데이터를 분석하고 싶어지지 않나요?😄

References