파이썬 기반 데이터분석을 위하여 .groupby() 메서드를 활용한 Pandas 객체의 요약을 알아본다.
※ 본 내용에서 사용하는 production_monthly_1990.csv 파일은 별도로 다운로드 받아야 한다.
※ production_monthly_1990.csv 다운받기 [클릭]
개요
Pandas 객체의 특정 변수를 요약하고자 하는 경우 .sum()이나 .mean() 같은 메서드로 여러 요약 수치를 산출한다. 여기서 더 나아가 다른 변수를 기준으로 이런 요약 수치를 산출하는 작업이 필요할 수 있다. 예를 들어 월별 총 매출, 사용자별 구매상품 종류수 그리고 일별 최저기온 같은 수치가 있을 수 있다. 이러한 수치를 산출하고자 할 때는 월별로 데이터를 별도로 필터링할 수 있지만 이런 작업을 할 경우 중복되는 코드가 많아지거나 반복문을 써야하는 불편함이 있을 수 있다. 이 불편함을 해소해주는 메서드 중 하나가 .groupby() 메서드이다. 이제 groupby() 메서드에 대해 알아보도록 하자.
실습
실습을 위해 데이터를 준비하자.
1 | df = pd.read_csv("production_monthly_1990.csv") |
| year | month | volumn | |
|---|---|---|---|
| 0 | 1990 | 1 | 1199.87 |
| 1 | 1990 | 2 | 1214.54 |
만약 .groupby() 메서드를 사용하지 않는다면 다음과 같이 개별 코드를 작성하거나 반복문을 사용해서 처리해야 하니 참고하도록 하자.
1 | [df.loc[df["year"] == 1990, "volumn"].mean(), |
기본형
.groupby() 메서드는 메서드 내부에 데이터프레임의 특정 변수를 기준 변수로 해서 해당 변수를 기준으로 다른 변수의 요약 연산을 실시할 수 있도록 지원하는 메서드이다.

기준 변수의 경우 그 개수가 하나일 경우 단순 문자열로 입력하면 되나, 두 개 이상인 경우 변수명을 리스트로 감싼다. 그리고 요약 연산의 대상이 되는 변수는 메서드 뒤에 대괄호를 사용하여 명시하며 기준 변수와 같이 두 개 이상의 변수를 지정하는 경우 변수명을 리스트로 감싸야 한다. 마지막으로 연산 규칙 또는 알고리즘은 대괄호 뒤에 적절한 메서드(.mean(), .agg() 등)를 사용하여 지정하며 Pandas 시리즈 객체에서 지원하는 메서드를 사용할 수 있다.
이제 기준 변수와 대상 변수가 각각 1개 할당된 형태의 기본형 코드로 .groupby() 메서드를 사용해보자. 연도(year)별 생산량(volumn)의 평균(mean)을 구하기 위한 코드는 다음과 같다.
1 | df.groupby("year")["volumn"].mean() |
월(month)별 생산량(volumn)의 평균(mean)을 구하기 위한 코드는 다음과 같다.
1 | df.groupby("month")["volumn"].mean() |
실행 결과를 보면 기준변수가 결과 시리즈 객체의 인덱스에 위치한 것을 볼 수 있다. 그래서 인덱스 기반의 연산을 하지 않는다면 다음과 같이 .reset_index()를 사용해서 데이터프레임으로 객체 유형을 변경하기도 한다.
1 | df.groupby("month")["volumn"].mean().reset_index() |
| month | volumn | |
|---|---|---|
| 0 | 1 | 1320.243571 |
| 1 | 2 | 1301.081786 |
| 2 | 3 | 1238.140000 |
| 3 | 4 | 1182.153571 |
| 4 | 5 | 1166.146429 |
| 5 | 6 | 1192.766071 |
| 6 | 7 | 1188.485000 |
| 7 | 8 | 1279.288929 |
| 8 | 9 | 1349.205556 |
| 9 | 10 | 1493.850000 |
| 10 | 11 | 1517.335185 |
| 11 | 12 | 1511.517778 |
기준 변수 2개
이번에는 .groupby() 메서드에 2개의 기준 변수를 넣어보자. 데이터가 연도별 월별 데이터이기 때문에 기준변수에 연도와 월 변수를 넣는 것은 의미가 없어 2000년도 부터 최근까지 데이터를 1, 나머지를 0으로 하는 “is_2k” 변수를 추가로 만들고 진행하도록 한다.
1 | df["is_2k"] = (df["year"] >= 2000) + 0 |
| year | month | volumn | is_2k | |
|---|---|---|---|---|
| 0 | 1990 | 1 | 1199.87 | 0 |
| 1 | 1990 | 2 | 1214.54 | 0 |
기준 변수에 두 개 이상의 변수를 지정할 경우 리스트 객체로 만들어줘야 하기에 대괄호로 기준 변수명을 다음과 같이 묶어준다.
1 | df.groupby(["is_2k", "year"])["volumn"].mean() |
상기 결과를 보면 생성되는 시리즈 객체의 인덱스가 다중 인덱스(multi index)인 것을 알 수 있다. 역시 데이터프레임으로 변경하려면 .reset_index()를 사용한다.
1 | df.groupby(["is_2k", "year"])["volumn"].mean().reset_index().head(14) |
| is_2k | year | volumn | |
|---|---|---|---|
| 0 | 0 | 1990 | 1265.956667 |
| 1 | 0 | 1991 | 1316.827500 |
| 2 | 0 | 1992 | 1231.887500 |
| 3 | 0 | 1993 | 1255.200000 |
| 4 | 0 | 1994 | 1255.126667 |
| 5 | 0 | 1995 | 1270.157500 |
| 6 | 0 | 1996 | 1322.317500 |
| 7 | 0 | 1997 | 1347.964167 |
| 8 | 0 | 1998 | 1425.135000 |
| 9 | 0 | 1999 | 1396.554167 |
| 10 | 1 | 2000 | 1462.280000 |
| 11 | 1 | 2001 | 1446.630000 |
| 12 | 1 | 2002 | 1385.381667 |
| 13 | 1 | 2003 | 1379.460833 |
대상 변수 2개
이번에는 .groupby() 메서드 다음에 2개의 대상 변수를 지정해보자. 그 전에 “vol2” 라는 수치형 변수를 하나 더 만들자.
1 | df["vol2"] = round(df["volumn"] / 50) |
| year | month | volumn | is_2k | vol2 | |
|---|---|---|---|---|---|
| 0 | 1990 | 1 | 1199.87 | 0 | 24.0 |
| 1 | 1990 | 2 | 1214.54 | 0 | 24.0 |
다음과 같이 코드를 작성할 수 있다.
1 | df.groupby("month")[["volumn", "vol2"]].mean() |
| volumn | vol2 | |
|---|---|---|
| month | ||
| 1 | 1320.243571 | 26.392857 |
| 2 | 1301.081786 | 26.000000 |
| 3 | 1238.140000 | 24.857143 |
| 4 | 1182.153571 | 23.678571 |
| 5 | 1166.146429 | 23.214286 |
| 6 | 1192.766071 | 23.892857 |
| 7 | 1188.485000 | 23.750000 |
| 8 | 1279.288929 | 25.607143 |
| 9 | 1349.205556 | 27.037037 |
| 10 | 1493.850000 | 29.962963 |
| 11 | 1517.335185 | 30.370370 |
| 12 | 1511.517778 | 30.296296 |
이렇게 대상변수가 2개 이상인 경우는 데이터프레임 객체를 반환한다.
.agg() 메서드 기본
앞에는 .mean() 메서드만 사용했지만 한 번에 다양한 메서드를 사용하거나 Pandas에서 지원하지 않는 산술연산을 대상 변수에 적용하고자 할 때는 .agg() 메서드를 .groupby() 메서드에 이어서 사용할 수 있다.
다음과 같이 대상 변수가 하나이면서 .agg() 메서드에 하나의 연산 규칙만 지정하면 시리즈 객체가 반환되는 것을 볼 수 있다.
1 | df.groupby("month")["volumn"].agg("mean") |
대상 변수가 하나이면서 .agg() 메서드에 두 개 이상의 연산 규칙만 지정하면 데이터프레임 객체가 반환되는 것을 볼 수 있다. 그리고 연산 규칙이 2개 이상일 경우 리스트 객체를 사용해야하며 다음의 코드와 같다.
1 | df.groupby("month")["volumn"].agg(["min", "max"]) |
| min | max | |
|---|---|---|
| month | ||
| 1 | 1078.80 | 1494.82 |
| 2 | 1067.80 | 1487.11 |
| 3 | 1026.72 | 1423.16 |
| 4 | 956.52 | 1391.38 |
| 5 | 963.02 | 1355.22 |
| 6 | 954.78 | 1384.00 |
| 7 | 987.75 | 1361.88 |
| 8 | 1062.75 | 1436.13 |
| 9 | 1126.87 | 1507.80 |
| 10 | 1368.72 | 1646.03 |
| 11 | 1390.49 | 1643.83 |
| 12 | 1221.21 | 1678.98 |
.agg() + 사용자 정의 함수
기본 메서드에서 제공하는 연산이 아닌 연산 규칙을 적용하고자 할 때는 사용자 정의 함수를 활용한 연산을 할 수 있다. 입력값의 평균을 반올림하여 소수점 첫째 자리까지 산출하는 사용자 정의 함수인 udf_mean_r1()을 생성하고 이를 .agg() 메서드에 적용해보자.
1 | def udf_mean_r1(x): |
기존 Pandas 메서드를 .agg() 메서드에 넣은 코드는 메서드명을 문자열로 넣었지만 사용자 정의 함수는 문자열이 아닌 함수 객체 그대로를 넣는 것을 볼 수 있다. 만약 문자열을 넣는다면 다음과 같은 에러를 확인할 수 있다.
1 | df.groupby("month")["volumn"].agg("udf_mean_r1") |
.agg() + lambda 함수
사용자 정의 함수 외에도 일회성 함수인 람다(lambda) 함수를 .agg() 메서드에 적용할 수 있다. 다음과 같이 코드를 작성해보자.
1 | df.groupby("month")["volumn"].agg(lambda x: round(x.mean(), 1)) |
한 줄 정도의 간단한 일회성 연산규칙을 사용하고자 할 때는 이렇게 람다 함수를 사용하는 것이 좋으며, 사용자 정의 함수를 사용하는 것보다 코드가 간결해질 수 있다. 하지만 여러줄의 복잡한 연산규칙을 사용하고자 할 때는 사용자 정의 함수를 권장한다.
.agg() + dictionary
.agg() 메서드에 딕셔너리를 사용하면 대상 변수 각각 대해 다양한 연산규칙을 적용할 수 있다. 이 방법을 사용하게 되면 2개 이상의 객체를 .groupby() 메서드를 사용해서 만들고 각 객체를 하나로 join하는 과정을 간단하고 쉽게 처리할 수 있다. 일단 기존의 데이터에는 연산 대상으로 지정할 변수가 “volumn” 하나 밖에 없기 때문에 “volumn” 변수를 100으로 나눈 몫을 기반으로 “v2” 변수를 새로 만들어주고 진행하도록 한다. 그럼 다음과 같이 코드를 작성해보자.
1 | df["v2"] = df["volumn"] // 100 |
| year | month | volumn | v2 | |
|---|---|---|---|---|
| 0 | 1990 | 1 | 1199.87 | 11.0 |
| 1 | 1990 | 2 | 1214.54 | 12.0 |
이제 딕셔너리를 사용한 코드를 작성해보자.
1 | df.groupby("month")[["volumn", "v2"]].agg({"volumn": "max", "v2": "min"}) |
| volumn | v2 | |
|---|---|---|
| month | ||
| 1 | 1494.82 | 10.0 |
| 2 | 1487.11 | 10.0 |
| 3 | 1423.16 | 10.0 |
| 4 | 1391.38 | 9.0 |
| 5 | 1355.22 | 9.0 |
| 6 | 1384.00 | 9.0 |
| 7 | 1361.88 | 9.0 |
| 8 | 1436.13 | 10.0 |
| 9 | 1507.80 | 11.0 |
| 10 | 1646.03 | 13.0 |
| 11 | 1643.83 | 13.0 |
| 12 | 1678.98 | 12.0 |
대상 변수가 2개 이상인 경우 각각에 대해 서로 다른 연산규칙을 적용할 때 대상 변수는 딕셔너리의 key로 지정하고 연산규칙은 value로 지정하면 된다. 이때 연산규칙은 기존 메서드를 사용하는 경우 문자열로 지정하거나 사용자 정의 함수를 사용할 수 있다.
다음은 사용자 정의 함수를 사용한 예제이다.
1 | df.groupby("month")[["volumn", "v2"]].agg({"volumn": "max", "v2": udf_mean_r1}) |
| volumn | v2 | |
|---|---|---|
| month | ||
| 1 | 1494.82 | 12.7 |
| 2 | 1487.11 | 12.5 |
| 3 | 1423.16 | 11.9 |
| 4 | 1391.38 | 11.2 |
| 5 | 1355.22 | 11.1 |
| 6 | 1384.00 | 11.5 |
| 7 | 1361.88 | 11.4 |
| 8 | 1436.13 | 12.3 |
| 9 | 1507.80 | 13.0 |
| 10 | 1646.03 | 14.4 |
| 11 | 1643.83 | 14.6 |
| 12 | 1678.98 | 14.5 |