Published on

Pandas에서 Aggregate하기

Authors

이번 포스팅에서는 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 를 정리한 표는 아래와 같습니다.

CommandDescription
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이 있다고 가정해봅시다.

studentassignment_namegrade
AmyAssignment 175
AmyAssignment 235
BobAssignment 199
BobAssignment 235

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

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

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

studentgrade
Amy80
Bob90
Chris75

.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이 있다고 가정해봅시다.

idteacategorycaffeineprice
0earl greyblack383
1english breakfastblack413
2irish breakfastblack372.5
3jasminegreen234.5
4matchagreen485
5camomileherbal03

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

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

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

categoryid
0black3
1green4
2herbal8
3white2

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

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

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

categorycounts
0black3
1green4
2herbal8
3white2

Apply with Lambda

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

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

idnamewagecategory
10131Sarah Carney39product
14189Heather Carey17design
15004Gary Mercado33marketing
11204Cora Copaz27design

만약 각 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 는 다음과 같이 됩니다.

categorywage
0design23
1marketing35
2product48

Multiple Groupby

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

LocationDateDay of WeekTotal Sales
West VillageFebruary 1W400
West VillageFebruary 2Th450
ChelseaFebruary 1W375
ChelseaFebruary 2Th390
...

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

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

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

LocationDay of WeekTotal Sales
ChelseaM402.50
ChelseaTu422.75
ChelseaW452.00
West VillageM390
West VillageTu400

Pivot Tables

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

LocationMTuWThFSaSu
Chelsea400390250275300150175
West Village300310350400390250200

이렇게 테이블을 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