2.4. 데이터프레임의 그룹화#

앞서 데이터프레임의 요약에서는 전체 데이터를 요약(count, mean, 분위수 등)했었다. 예를 들어, 2021년의 강남구 데이터의 데이터를 요약하려면 전체 데이터에서 해당 데이터를 추출하여 사용했다. 이 절에서는 그룹별로 한번에 요약하는 방법에 대해 알아보고자 한다. 살펴볼 내용은 다음과 같다.

  • 그룹별 요약

  • 여러 개의 그룹

  • 다양한 집계들

간단한 예제#

먼저 예제 데이터로 실습해보자.

다음과 같은 간단한 데이터프레임 df 을 고려해 보자. 열 school 은 학교, sex는 성, score 는 점수를 나타낸다고 하자.

import pandas as pd
test = pd.DataFrame( { 
    "school" : ["A", "A", "A", "A", "B", "B", "B"], 
    "sex" : ["M", "M", "F", "F", "M", "F", "F"],
    "score" : [100, 98, 34, 83, 56, 90, 65]
})
test
school sex score
0 A M 100
1 A M 98
2 A F 34
3 A F 83
4 B M 56
5 B F 90
6 B F 65

이제 데이터프레임 df 에서 학교별, 성별로 성적의 평균을 구해보자.

먼저 데이터프레임 df 을 학교별, 성별로 나누어 그룹회된 데이터프레임을 만들고 각 그룹에 대한 성적의 평균을 구해보자.

test.groupby( by =["school", "sex"]).mean()
score
school sex
A F 58.5
M 99.0
B F 77.5
M 56.0

이렇게 데이터프레임은 groupby() 메소드를 이용하여 여러 개의 그룹으로 나누어 요약할 수 있다.

만약 그룹으로 나누는데 사용하는 열의 자료형식이 숫자면 어떤 일이 벌어질까. 자료의 범주가 숫자로 나타나는 경우가 많은데 이 경우 그룹화에 대하여 알아보자.

먼저 위에서 데이터프레임 df 과 동일한 자료를 가지지만 df2 는 여자는 1, 남자는 0 으로 자료가 저장되어 있다고 하자.

test2 = pd.DataFrame( { 
    "school" : ["A", "A", "A", "A", "B", "B", "B"], 
    "sex" : [0, 0, 1, 1, 0, 0, 1],
    "score" : [100, 98, 34, 83, 56, 90, 65]
})
test2
school sex score
0 A 0 100
1 A 0 98
2 A 1 34
3 A 1 83
4 B 0 56
5 B 0 90
6 B 1 65

메소드 groupby() 는 숫자로 구성된 열도 나타난 숫자에 따라서 그룹화를 해준다.

test2.groupby( by =["school", "sex"]).mean()
score
school sex
A 0 99.0
1 58.5
B 0 73.0
1 65.0

그룹화에 사용된 열들을 인덱스에서 다시 열로 바꾸고 싶으면 reset_index()를 적용하자.

test2.groupby( by =["school", "sex"]).mean().reset_index()
school sex score
0 A 0 99.0
1 A 1 58.5
2 B 0 73.0
3 B 1 65.0

화재출동 데이터#

화재출동 데이터(fire_calling_summary.csv)을 이용해 간단한 화재출동 현황을 요약하려 한다. 2017년부터 2021년의 5년간 서울시 모든 구의 화재출동 데이터입니다.

import pandas as pd
df = pd.read_csv("https://uos-bigdata.github.io/lab_data/docs/assets/data_lab_fire/fire_calling.csv", encoding='cp949')
df
화재발생연도 시군구 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
0 2017 강남구 NaN 11 1565258 502 129 120
1 2017 강동구 NaN 12 418593 269 63 73
2 2017 강북구 NaN 6 339146 186 51 41
3 2017 강서구 3.0 22 706871 364 89 81
4 2017 관악구 3.0 20 654690 286 82 69
... ... ... ... ... ... ... ... ...
120 2021 용산구 NaN 5 296793 176 47 36
121 2021 은평구 3.0 8 875722 160 57 42
122 2021 종로구 NaN 12 465499 192 48 54
123 2021 중구 NaN 16 2780374 171 41 49
124 2021 중랑구 1.0 12 345257 213 63 59

125 rows × 8 columns

먼저 Nan값을 0으로 대체해준다.

df = df.fillna(0)
df
화재발생연도 시군구 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
0 2017 강남구 0.0 11 1565258 502 129 120
1 2017 강동구 0.0 12 418593 269 63 73
2 2017 강북구 0.0 6 339146 186 51 41
3 2017 강서구 3.0 22 706871 364 89 81
4 2017 관악구 3.0 20 654690 286 82 69
... ... ... ... ... ... ... ... ...
120 2021 용산구 0.0 5 296793 176 47 36
121 2021 은평구 3.0 8 875722 160 57 42
122 2021 종로구 0.0 12 465499 192 48 54
123 2021 중구 0.0 16 2780374 171 41 49
124 2021 중랑구 1.0 12 345257 213 63 59

125 rows × 8 columns

먼저 연도별 5개 의 화재로 인한 출동횟수를 계산해 보자.

연도별 출동횟수를 계산하려면

  1. 전체 자료를 연도별로 나누고

  2. 각 연도에서 모든 시군구의 출동횟수를 더해야한다.

즉, 데이터프레임을 지정된 열(화재발생연도)의 값(2017, 2018, ..)에 따라서 그룹으로 나누어 전국 가구수를 계산해야 한다.

메소드 groupby( by=...)는 지정된 열의 값으로 데이터프레임을 그룹(group)으로 나누어 준다. 메소드 groupby( by=...)데이터프레임을 물리적으로 그룹을 나누어 주는 것은 아니고 메소드에서 지정된 그룹을 데이터프레임에 적용하는 기능을 한다.

  • by= 는 그룹을 나눌 때 사용되는 열을 리스트 형식으로 지정한다.

  • 메소드 groupby( by=...) 로 만들어진 데이터프레임의 자료 형식은 DataFrameGroupBy이다. 따라서 보통 데이터프레임에 적용되는 메소드들을 사용할 수 없는 경우가 있다.

이제 데이터프레임 df 를 연도 화재발생연도를 이용하여 그룹화된 데이터프레임 df_grp을 만들어 보자.

df_grp = df.groupby( by=["화재발생연도"] )

그룹화된 데이터프레임 fire_grp 은 보통 데이터프레임과 다르게 출력해주는 내용이 없다. 그 이유는 그룹화된 데이터프레임은 그룹에 대한 정보만 가진 특별한 자료의 형식을 가진다.

df_grp
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7ff6293389d0>
type(df_grp)
pandas.core.groupby.generic.DataFrameGroupBy

그룹별 요약#

이제 아래의 코드가 어떤 결과를 내는지 보자.

결과는 그룹으로 지정된 연도별로 모든 출동횟수를 더해서 연도별 출동횟수를 계산해준다.

  • 주의할 점은 메소드 sum()axis= 선택문이 없다. 그 이유는 그룹화된 데이터프레임의 sum() 메소드는 언제나 그룹에 속한 모든 자료를 더해준다.

df_grp.sum()
/var/folders/3k/d8g2lfdn57b665jktbg_sglm0000gn/T/ipykernel_13278/3701876493.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  df_grp.sum()
사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
화재발생연도
2017 37.0 246 15323890 5978 1465 1496
2018 53.0 307 21239984 6368 1748 1657
2019 37.0 361 92409010 5881 1476 1478
2020 37.0 236 17438606 5088 1244 1274
2021 37.0 280 18268346 4951 1367 1186

위의 결과를 보면 만들어진 데이터 프레임에서 다음과 같은 특성이 보인다.

  • 열이름이 한 칸 위로 올라가 있다. 이는 문자로 된 열이름(column name)이 비어있다는 것이다.

  • 행인덱스가 화재발생연도로 되어있으며 숫자로 된 행인덱스가 없는 것을 알 수 있다.

우리가 사용할 수 있는 유용한 데이터프레임을 만들기 위해서는 인덱스의 재구성이 필요하다. 메소드 reset_index()를 사용해 보자.

df_grp.sum().reset_index()
/var/folders/3k/d8g2lfdn57b665jktbg_sglm0000gn/T/ipykernel_13278/2650935280.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  df_grp.sum().reset_index()
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
0 2017 37.0 246 15323890 5978 1465 1496
1 2018 53.0 307 21239984 6368 1748 1657
2 2019 37.0 361 92409010 5881 1476 1478
3 2020 37.0 236 17438606 5088 1244 1274
4 2021 37.0 280 18268346 4951 1367 1186

이제 연도별로 출동횟수 정보를 가진 새로운 데이터프레임 df_year를 만들어 보자.

df_year = df_grp.sum().reset_index()
df_year
/var/folders/3k/d8g2lfdn57b665jktbg_sglm0000gn/T/ipykernel_13278/3188372753.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  df_year = df_grp.sum().reset_index()
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
0 2017 37.0 246 15323890 5978 1465 1496
1 2018 53.0 307 21239984 6368 1748 1657
2 2019 37.0 361 92409010 5881 1476 1478
3 2020 37.0 236 17438606 5088 1244 1274
4 2021 37.0 280 18268346 4951 1367 1186

새로운 정보#

이제 연도별로 나타난 정보에 대한 모든 열을 더하면 전체 값이 나온다. 전체 값을 새로운 변수 total 로 데이터프레임 fire_year에 추가하자.

df_year["total"] = df_year.sum(axis=1)
df_year
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름 total
0 2017 37.0 246 15323890 5978 1465 1496 15335129.0
1 2018 53.0 307 21239984 6368 1748 1657 21252135.0
2 2019 37.0 361 92409010 5881 1476 1478 92420262.0
3 2020 37.0 236 17438606 5088 1244 1274 17448505.0
4 2021 37.0 280 18268346 4951 1367 1186 18278188.0

!잠깐

위에서 사용한 메소드 sum(axis=1) 는 모든 열 들을 더해주는 작업을 수행하는데 년도를 나타내는 열 화재발생연도 도 같이 결과에 더해졌다. 그리고 필요하지 않은 데이터까지 더해진 것을 볼 수 있다.

앞에서 본 것 처럼 sum(axis=1) 는 자동적으로 문자열을 포함한 열을 제외해주는데 이러한 자동 기능을 자주 이용하면 위와 같은 실수를 범할 수 있다. 왜냐하면 오류가 나오지 않기 떄문이다.

안전한 프로그래밍을 위하여 합을 구할 모든 열의 이름을 가진 리스트로 슬라이싱을 먼저 하고 메소드 sum(axis=1) 를 적용하자.

df_year["여름겨울출동횟수"] = df_year[ ["출동횟수_겨울", "출동횟수_여름"] ].sum(axis=1)
df_year
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름 total 여름겨울출동횟수
0 2017 37.0 246 15323890 5978 1465 1496 15335129.0 2961
1 2018 53.0 307 21239984 6368 1748 1657 21252135.0 3405
2 2019 37.0 361 92409010 5881 1476 1478 92420262.0 2954
3 2020 37.0 236 17438606 5088 1244 1274 17448505.0 2518
4 2021 37.0 280 18268346 4951 1367 1186 18278188.0 2553

이제 출동횟수에 대한 백분율(percentage)를 새롭게 만들어 보자.

df_year['percent'] = df_year['여름겨울출동횟수'] / df_year['출동횟수'] *100
df_year
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름 total 여름겨울출동횟수 percent
0 2017 37.0 246 15323890 5978 1465 1496 15335129.0 2961 49.531616
1 2018 53.0 307 21239984 6368 1748 1657 21252135.0 3405 53.470477
2 2019 37.0 361 92409010 5881 1476 1478 92420262.0 2954 50.229553
3 2020 37.0 236 17438606 5088 1244 1274 17448505.0 2518 49.488994
4 2021 37.0 280 18268346 4951 1367 1186 18278188.0 2553 51.565340

편의상 percent 열을 소수점 2째 자리에서 반올림해보자 (실습)

접기/펼치기

df_year['percent'] = df_year['percent'].round(2)

df_year

여러 개의 그룹#

화재출동 데이터의 그룹화 기준을 시군구, 화재발생연도로 적용해보자.

먼저 데이터를 나누어보자. 그룹화에 사용할 5개 구를 선택해서 df5으로 저장한다. 이 경우 은평구를 기준으로 나머지 구들을 추출하였다.

df5 = df[df['시군구'] >= '은평구']
df5
화재발생연도 시군구 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
21 2017 은평구 0.0 3 218200 159 51 32
22 2017 종로구 1.0 3 1077665 234 55 69
23 2017 중구 5.0 14 485392 198 48 47
24 2017 중랑구 2.0 5 332366 196 53 38
46 2018 은평구 5.0 10 419503 214 58 47
47 2018 종로구 14.0 22 574300 254 71 70
48 2018 중구 0.0 23 1257005 275 76 74
49 2018 중랑구 2.0 8 201421 254 72 55
71 2019 은평구 3.0 20 2412769 196 62 34
72 2019 종로구 4.0 16 801094 232 60 63
73 2019 중구 3.0 17 74077097 213 51 39
74 2019 중랑구 1.0 9 322650 210 54 49
96 2020 은평구 2.0 6 504788 192 48 46
97 2020 종로구 2.0 5 639751 217 50 49
98 2020 중구 0.0 10 1284422 185 41 54
99 2020 중랑구 2.0 12 229566 225 54 57
121 2021 은평구 3.0 8 875722 160 57 42
122 2021 종로구 0.0 12 465499 192 48 54
123 2021 중구 0.0 16 2780374 171 41 49
124 2021 중랑구 1.0 12 345257 213 63 59

인덱스를 초기화해준다.

df5.reset_index(drop = True, inplace = True)
df5
화재발생연도 시군구 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
0 2017 은평구 0.0 3 218200 159 51 32
1 2017 종로구 1.0 3 1077665 234 55 69
2 2017 중구 5.0 14 485392 198 48 47
3 2017 중랑구 2.0 5 332366 196 53 38
4 2018 은평구 5.0 10 419503 214 58 47
5 2018 종로구 14.0 22 574300 254 71 70
6 2018 중구 0.0 23 1257005 275 76 74
7 2018 중랑구 2.0 8 201421 254 72 55
8 2019 은평구 3.0 20 2412769 196 62 34
9 2019 종로구 4.0 16 801094 232 60 63
10 2019 중구 3.0 17 74077097 213 51 39
11 2019 중랑구 1.0 9 322650 210 54 49
12 2020 은평구 2.0 6 504788 192 48 46
13 2020 종로구 2.0 5 639751 217 50 49
14 2020 중구 0.0 10 1284422 185 41 54
15 2020 중랑구 2.0 12 229566 225 54 57
16 2021 은평구 3.0 8 875722 160 57 42
17 2021 종로구 0.0 12 465499 192 48 54
18 2021 중구 0.0 16 2780374 171 41 49
19 2021 중랑구 1.0 12 345257 213 63 59

시군구를 기준으로 각 열의 평균 (mean)과 합계 (sum)를 구해보자.

df5.groupby( by =["시군구"]).mean()
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
시군구
은평구 2019.0 2.6 9.4 886196.4 184.2 55.2 40.2
종로구 2019.0 4.2 11.6 711661.8 225.8 56.8 61.0
중구 2019.0 1.6 16.0 15976858.0 208.4 51.4 52.6
중랑구 2019.0 1.6 9.2 286252.0 219.6 59.2 51.6
df5.groupby( by =["시군구"]).sum()
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
시군구
은평구 10095 13.0 47 4430982 921 276 201
종로구 10095 21.0 58 3558309 1129 284 305
중구 10095 8.0 80 79884290 1042 257 263
중랑구 10095 8.0 46 1431260 1098 296 258

화재발생연도를 기준으로 각 열의 평균 (mean)과 합계 (sum)를 구해보자.

df5.groupby( by =["화재발생연도"]).mean()
/var/folders/3k/d8g2lfdn57b665jktbg_sglm0000gn/T/ipykernel_13278/890159447.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  df5.groupby( by =["화재발생연도"]).mean()
사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
화재발생연도
2017 2.00 6.25 528405.75 196.75 51.75 46.50
2018 5.25 15.75 613057.25 249.25 69.25 61.50
2019 2.75 15.50 19403402.50 212.75 56.75 46.25
2020 1.50 8.25 664631.75 204.75 48.25 51.50
2021 1.00 12.00 1116713.00 184.00 52.25 51.00
df5.groupby( by =["화재발생연도"]).sum()
/var/folders/3k/d8g2lfdn57b665jktbg_sglm0000gn/T/ipykernel_13278/2243519833.py:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.sum is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
  df5.groupby( by =["화재발생연도"]).sum()
사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
화재발생연도
2017 8.0 25 2113623 787 207 186
2018 21.0 63 2452229 997 277 246
2019 11.0 62 77613610 851 227 185
2020 6.0 33 2658527 819 193 206
2021 4.0 48 4466852 736 209 204

그룹화 기준을 2개 이상으로 줄 수도 있다. 이 경우 시군구화재발생연도로 해보자.

df5.groupby( by =["시군구", "화재발생연도"]).mean()
사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
시군구 화재발생연도
은평구 2017 0.0 3.0 218200.0 159.0 51.0 32.0
2018 5.0 10.0 419503.0 214.0 58.0 47.0
2019 3.0 20.0 2412769.0 196.0 62.0 34.0
2020 2.0 6.0 504788.0 192.0 48.0 46.0
2021 3.0 8.0 875722.0 160.0 57.0 42.0
종로구 2017 1.0 3.0 1077665.0 234.0 55.0 69.0
2018 14.0 22.0 574300.0 254.0 71.0 70.0
2019 4.0 16.0 801094.0 232.0 60.0 63.0
2020 2.0 5.0 639751.0 217.0 50.0 49.0
2021 0.0 12.0 465499.0 192.0 48.0 54.0
중구 2017 5.0 14.0 485392.0 198.0 48.0 47.0
2018 0.0 23.0 1257005.0 275.0 76.0 74.0
2019 3.0 17.0 74077097.0 213.0 51.0 39.0
2020 0.0 10.0 1284422.0 185.0 41.0 54.0
2021 0.0 16.0 2780374.0 171.0 41.0 49.0
중랑구 2017 2.0 5.0 332366.0 196.0 53.0 38.0
2018 2.0 8.0 201421.0 254.0 72.0 55.0
2019 1.0 9.0 322650.0 210.0 54.0 49.0
2020 2.0 12.0 229566.0 225.0 54.0 57.0
2021 1.0 12.0 345257.0 213.0 63.0 59.0

그룹화 기준을 화재발생연도, 시군구로 바꾸어보자.

df5.groupby( by =["화재발생연도", "시군구"]).mean()
사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
화재발생연도 시군구
2017 은평구 0.0 3.0 218200.0 159.0 51.0 32.0
종로구 1.0 3.0 1077665.0 234.0 55.0 69.0
중구 5.0 14.0 485392.0 198.0 48.0 47.0
중랑구 2.0 5.0 332366.0 196.0 53.0 38.0
2018 은평구 5.0 10.0 419503.0 214.0 58.0 47.0
종로구 14.0 22.0 574300.0 254.0 71.0 70.0
중구 0.0 23.0 1257005.0 275.0 76.0 74.0
중랑구 2.0 8.0 201421.0 254.0 72.0 55.0
2019 은평구 3.0 20.0 2412769.0 196.0 62.0 34.0
종로구 4.0 16.0 801094.0 232.0 60.0 63.0
중구 3.0 17.0 74077097.0 213.0 51.0 39.0
중랑구 1.0 9.0 322650.0 210.0 54.0 49.0
2020 은평구 2.0 6.0 504788.0 192.0 48.0 46.0
종로구 2.0 5.0 639751.0 217.0 50.0 49.0
중구 0.0 10.0 1284422.0 185.0 41.0 54.0
중랑구 2.0 12.0 229566.0 225.0 54.0 57.0
2021 은평구 3.0 8.0 875722.0 160.0 57.0 42.0
종로구 0.0 12.0 465499.0 192.0 48.0 54.0
중구 0.0 16.0 2780374.0 171.0 41.0 49.0
중랑구 1.0 12.0 345257.0 213.0 63.0 59.0

다양한 집계들#

  • 셈 척도: 갯수 (count), 합산 (sum)

  • 중심척도: 평균 (mean), 중위수 (median)

  • 산포척도: 최댓값/최솟값 (max/min), 분산/표준편차 (var, std), 백분위 (quantile)

  • 기초통계량 (describe)

위와 같이 여러 집계들이 있는데, 자료가 범주형인지 수치형인지에 따라 그 쓰임이 나뉜다.

  • 범주형 자료: 셈 (count, percent)

  • 수치형 자료: 셈 (sum), 중심 (mean, median), 산포 (man, min, var, std, quantile)

예를 들어, 화재출동 데이터의 경우 자료의 형태에 따라 다음과 같이 열을 나눌 수 있다.

  • 범주형 자료: 화재발생연도시군구

  • 수치형 자료: 화재발생연도시군구를 제외한 나머지 열

그러면 그룹화와 집계를 위해 다음과 같은 문제를 고려하자

  • 문제: 시군구별 평균 재산피해금액과 총 출동횟수

이 문제를 해결하기 위해 다음과 같이 설계할 수 있다.

  • 그룹화: 시군구

  • 계산하고 싶은 열: 재산피해금액, 출동횟수

  • 집계함수: sum, mean

그러면 df5를 다시 집계해보자. 방금 전에 했던 방법과 같은 방법으로 각 집계를 구하면 제대로 집계가 이루어지지 않거나 불필요한 정보가 생긴다는 문제가 있었다.

df5.groupby(['시군구']).sum()
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
시군구
은평구 10095 13.0 47 4430982 921 276 201
종로구 10095 21.0 58 3558309 1129 284 305
중구 10095 8.0 80 79884290 1042 257 263
중랑구 10095 8.0 46 1431260 1098 296 258
df5.groupby(['시군구']).mean()
화재발생연도 사망자수 부상자수 재산피해금액 출동횟수 출동횟수_겨울 출동횟수_여름
시군구
은평구 2019.0 2.6 9.4 886196.4 184.2 55.2 40.2
종로구 2019.0 4.2 11.6 711661.8 225.8 56.8 61.0
중구 2019.0 1.6 16.0 15976858.0 208.4 51.4 52.6
중랑구 2019.0 1.6 9.2 286252.0 219.6 59.2 51.6

이처럼 한번에 여러 컬럼을 다르게 연산해야 할 때가 있다. 다음과 같이 agg메소드를 사용하면 가능하다. 이 때 {계산하고 싶은 이름: 집계함수}와 같이 입력한다.

df5.groupby(['시군구']).agg({"재산피해금액" : "mean", "출동횟수" : "sum"})
재산피해금액 출동횟수
시군구
은평구 886196.4 921
종로구 711661.8 1129
중구 15976858.0 1042
중랑구 286252.0 1098

이번에는 연도와 시군구별 평균 재산피해금액과 총 출동횟수를 계산해보자.

df5.groupby(['화재발생연도', '시군구']).agg({"재산피해금액" : "mean", "출동횟수" : "sum"})
재산피해금액 출동횟수
화재발생연도 시군구
2017 은평구 218200.0 159
종로구 1077665.0 234
중구 485392.0 198
중랑구 332366.0 196
2018 은평구 419503.0 214
종로구 574300.0 254
중구 1257005.0 275
중랑구 201421.0 254
2019 은평구 2412769.0 196
종로구 801094.0 232
중구 74077097.0 213
중랑구 322650.0 210
2020 은평구 504788.0 192
종로구 639751.0 217
중구 1284422.0 185
중랑구 229566.0 225
2021 은평구 875722.0 160
종로구 465499.0 192
중구 2780374.0 171
중랑구 345257.0 213

이렇게 만들어진 집계데이터를 활용해보자.

집계를 하면서 열 이름이 행의 인덱스로 들어갔다. 이를 reset_index로 인덱스 초기화를 해서 다시 열 이름으로 만들어 활용한다.

df_summ = df5.groupby(['화재발생연도', '시군구']).agg({"재산피해금액" : "mean", "출동횟수" : "sum"})
df_summ = df_summ.reset_index()
df_summ
화재발생연도 시군구 재산피해금액 출동횟수
0 2017 은평구 218200.0 159
1 2017 종로구 1077665.0 234
2 2017 중구 485392.0 198
3 2017 중랑구 332366.0 196
4 2018 은평구 419503.0 214
5 2018 종로구 574300.0 254
6 2018 중구 1257005.0 275
7 2018 중랑구 201421.0 254
8 2019 은평구 2412769.0 196
9 2019 종로구 801094.0 232
10 2019 중구 74077097.0 213
11 2019 중랑구 322650.0 210
12 2020 은평구 504788.0 192
13 2020 종로구 639751.0 217
14 2020 중구 1284422.0 185
15 2020 중랑구 229566.0 225
16 2021 은평구 875722.0 160
17 2021 종로구 465499.0 192
18 2021 중구 2780374.0 171
19 2021 중랑구 345257.0 213

요약#

  • 여러 개의 열을 이용하여 새로운 열을 만드는 방법은 열을 선택하고 산술적인 연산을 적용해 준다.

  • 열에 대한 요약 통계는 다양한 메소드(예를 들어 sum(), mean() 등)을 적용하며 선택문 axis=0 을 이용한다.

  • 자료의 그룹화는 groupby() 를 사용하며 이를 통해 그룹별로 요약할 수 있다.

함께해봅시다

df자료에 대해 다음의 문제를 해결해보자.

  1. 재산피해금액총합이 가장 많은 시군구재산피해금액평균이 가장 큰 시군구는 어디인가요? 동일한 시군구인가요?

  2. 출동횟수가 가장 많았던 때는 언제, 어느 구인가요? 00년도 00구로 알려주세요.

  3. 여름겨울을 합산하여 가장 출동이 많았던 때는 어느 구인가요? 그리고 출동횟수의 편차가 가장 큰 구는 어느 구인가요? 00구로 알려주세요.