2.3. 데이터 그룹화#

앞 서 데이터프레임의 요약에서는 전체 데이터를 요약 (count, mean, 분위수 등)했었다. 예를 들어, 2020년의 부산 데이터를 요약하려면 전체 데이터에서 해당 데이터를 추출하여 사용했다. 이 절에서는 그룹별로 한번에 요약하는 방법에 대해 알아보고자 한다.

데이터프레임을 그룹으로 나눌 때 여러 개의 기준을 조합해서 나눌 수도 있다.

앞서 사용했던 고용, 산재 데이터를 불러오자.

import pandas as pd
df = pd.read_csv("https://uos-bigdata.github.io/lab_data/docs/assets/data_lab_bokji/dat_total.csv")
df
연도 시도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수
0 2010.0 광주 6.863187 52 1820 6.847419 1 1763
1 2010.0 대구 7.366627 82 2523 7.051240 7 2420
2 2010.0 대전 9.100746 59 1340 8.816680 3 1271
3 2010.0 부산 8.126126 121 3108 7.622692 11 2979
4 2010.0 서울 10.971553 343 11741 10.869985 30 11391
... ... ... ... ... ... ... ... ...
79 2021.0 대전 2.658709 434 7917 1.887973 369 8007
80 2021.0 부산 2.874647 688 17367 1.909389 660 17680
81 2021.0 서울 7.487265 2375 59009 2.020801 1410 58700
82 2021.0 울산 3.442591 273 5095 2.080108 262 5193
83 2021.0 인천 2.604997 703 15648 1.916362 444 15615

84 rows × 8 columns

먼저 연도별 부산, 울산, 대구의 고용보험 가입된 사업장 수를 계산해 보자.

연도별 고용보험 가입된 사업장 수를 계산하려면

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

  2. 각 연도에서 모든 시구의 고용보험 가입된 사업장 수를 더해야한다.

즉, 데이터프레임을 지정된 열(연도)의 값(2010, 2021, ..)에 따라서 그룹으로 나누어 전체 사업장을 계산해야 한다.

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

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

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

이제 데이터프레임 df 를 연도 고용_가입된사업장수를 이용하여 그룹화된 데이터프레임 df_cpn을 만들어 보자.

df_com = df.groupby( by=["연도"] )

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

df_com
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001EF31AD30A0>
type(df_com)
pandas.core.groupby.generic.DataFrameGroupBy

그룹별 요약#

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

결과는 그룹으로 지정된 연도별로 모든 산재보험, 고용보험의 음식 사업장 수를 더해서 연도별 음식 사업장 수를 계산해준다.

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

df_com.sum()
C:\Users\jieun\AppData\Local\Temp\ipykernel_59076\2213740449.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_com.sum()
산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수
연도
2010.0 58.287574 783 23943 56.292972 58 23077
2011.0 59.151566 844 27919 57.321363 80 27129
2012.0 52.286663 972 35896 48.572728 82 34950
2013.0 44.299320 1105 39981 43.001126 69 38983
2014.0 38.887830 1125 47431 37.977763 101 46476
2015.0 37.365511 1362 55839 36.505305 116 54763
2016.0 36.272548 1844 64731 33.330179 147 63063
2017.0 33.248882 1898 73785 30.579104 156 72086
2018.0 29.255056 4077 96645 27.738977 182 91377
2019.0 26.530127 3790 112878 25.065422 203 110597
2020.0 22.946598 3835 117522 21.554175 273 113735
2021.0 24.416186 5566 127862 13.526563 4112 128238

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

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

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

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

df_com.sum().reset_index()
C:\Users\jieun\AppData\Local\Temp\ipykernel_59076\1907006016.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_com.sum().reset_index()
연도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수
0 2010.0 58.287574 783 23943 56.292972 58 23077
1 2011.0 59.151566 844 27919 57.321363 80 27129
2 2012.0 52.286663 972 35896 48.572728 82 34950
3 2013.0 44.299320 1105 39981 43.001126 69 38983
4 2014.0 38.887830 1125 47431 37.977763 101 46476
5 2015.0 37.365511 1362 55839 36.505305 116 54763
6 2016.0 36.272548 1844 64731 33.330179 147 63063
7 2017.0 33.248882 1898 73785 30.579104 156 72086
8 2018.0 29.255056 4077 96645 27.738977 182 91377
9 2019.0 26.530127 3790 112878 25.065422 203 110597
10 2020.0 22.946598 3835 117522 21.554175 273 113735
11 2021.0 24.416186 5566 127862 13.526563 4112 128238

이제 연도별로 산재보험 가입된 사업장 수 정보를 가진 새로운 데이터프레임 df_year를 만들어 보자.

df_year = df_com.sum().reset_index()
df_year
C:\Users\jieun\AppData\Local\Temp\ipykernel_59076\495942065.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_com.sum().reset_index()
연도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수
0 2010.0 58.287574 783 23943 56.292972 58 23077
1 2011.0 59.151566 844 27919 57.321363 80 27129
2 2012.0 52.286663 972 35896 48.572728 82 34950
3 2013.0 44.299320 1105 39981 43.001126 69 38983
4 2014.0 38.887830 1125 47431 37.977763 101 46476
5 2015.0 37.365511 1362 55839 36.505305 116 54763
6 2016.0 36.272548 1844 64731 33.330179 147 63063
7 2017.0 33.248882 1898 73785 30.579104 156 72086
8 2018.0 29.255056 4077 96645 27.738977 182 91377
9 2019.0 26.530127 3790 112878 25.065422 203 110597
10 2020.0 22.946598 3835 117522 21.554175 273 113735
11 2021.0 24.416186 5566 127862 13.526563 4112 128238

새로운 정보#

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

잠깐!

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

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

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

예시로, 보험 하나만 가입된 사업장 수를 파악하기 위해 산재보험만 가입된 사업장 수(“산재만_가입된사업장수”)와 고용보험만 가입된 사업장 수(“고용만_가입된사업장수”)를 sum(axis=1)을 통해 계산해보자.

df_year["하나만 가입된 사업장수"] = df_year[["산재만_가입된사업장수","고용만_가입된사업장수"]].sum(axis=1)
df_year
연도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수 하나만 가입된 사업장수
0 2010.0 58.287574 783 23943 56.292972 58 23077 841
1 2011.0 59.151566 844 27919 57.321363 80 27129 924
2 2012.0 52.286663 972 35896 48.572728 82 34950 1054
3 2013.0 44.299320 1105 39981 43.001126 69 38983 1174
4 2014.0 38.887830 1125 47431 37.977763 101 46476 1226
5 2015.0 37.365511 1362 55839 36.505305 116 54763 1478
6 2016.0 36.272548 1844 64731 33.330179 147 63063 1991
7 2017.0 33.248882 1898 73785 30.579104 156 72086 2054
8 2018.0 29.255056 4077 96645 27.738977 182 91377 4259
9 2019.0 26.530127 3790 112878 25.065422 203 110597 3993
10 2020.0 22.946598 3835 117522 21.554175 273 113735 4108
11 2021.0 24.416186 5566 127862 13.526563 4112 128238 9678

이번에는 고용보험에 가입한 사업장에 대한 백분율(percentage)를 새롭게 만들어 보자.

df_year['percent'] = df_year['고용만_가입된사업장수'] / df_year['고용_가입된사업장수'] *100
df_year
연도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수 하나만 가입된 사업장수 percent
0 2010.0 58.287574 783 23943 56.292972 58 23077 841 0.251332
1 2011.0 59.151566 844 27919 57.321363 80 27129 924 0.294887
2 2012.0 52.286663 972 35896 48.572728 82 34950 1054 0.234621
3 2013.0 44.299320 1105 39981 43.001126 69 38983 1174 0.177000
4 2014.0 38.887830 1125 47431 37.977763 101 46476 1226 0.217316
5 2015.0 37.365511 1362 55839 36.505305 116 54763 1478 0.211822
6 2016.0 36.272548 1844 64731 33.330179 147 63063 1991 0.233100
7 2017.0 33.248882 1898 73785 30.579104 156 72086 2054 0.216408
8 2018.0 29.255056 4077 96645 27.738977 182 91377 4259 0.199175
9 2019.0 26.530127 3790 112878 25.065422 203 110597 3993 0.183549
10 2020.0 22.946598 3835 117522 21.554175 273 113735 4108 0.240032
11 2021.0 24.416186 5566 127862 13.526563 4112 128238 9678 3.206538

작업을 하다가 필요없는 열이 생성되었을 때는 drop() 함수를 이용해 삭제할 수 있다. 여기서 axis=0은 행을 뜻하며, axis=1이 뜻하는 것은 열을 뜻한다. 밑의 코드는 산재_가입확률과 산재_가입X의 열을 전체 삭제한다는 의미이다.

df_year.drop(['percent'],axis=1)
연도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수 하나만 가입된 사업장수
0 2010.0 58.287574 783 23943 56.292972 58 23077 841
1 2011.0 59.151566 844 27919 57.321363 80 27129 924
2 2012.0 52.286663 972 35896 48.572728 82 34950 1054
3 2013.0 44.299320 1105 39981 43.001126 69 38983 1174
4 2014.0 38.887830 1125 47431 37.977763 101 46476 1226
5 2015.0 37.365511 1362 55839 36.505305 116 54763 1478
6 2016.0 36.272548 1844 64731 33.330179 147 63063 1991
7 2017.0 33.248882 1898 73785 30.579104 156 72086 2054
8 2018.0 29.255056 4077 96645 27.738977 182 91377 4259
9 2019.0 26.530127 3790 112878 25.065422 203 110597 3993
10 2020.0 22.946598 3835 117522 21.554175 273 113735 4108
11 2021.0 24.416186 5566 127862 13.526563 4112 128238 9678

여러 개의 그룹#

산재, 고용보험 가입 데이터의 그룹화 기준을 시도, 연도로 적용해보자.

df.groupby( by =["시도", "연도"]).mean().reset_index()
시도 연도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수
0 광주 2010.0 6.863187 52.0 1820.0 6.847419 1.0 1763.0
1 광주 2011.0 7.361302 57.0 2181.0 7.359587 10.0 2133.0
2 광주 2012.0 6.922905 56.0 2685.0 7.187380 4.0 2615.0
3 광주 2013.0 5.565035 71.0 3106.0 5.800198 2.0 3028.0
4 광주 2014.0 5.285354 72.0 3571.0 5.138937 2.0 3498.0
... ... ... ... ... ... ... ... ...
79 인천 2017.0 5.003619 230.0 8013.0 4.661026 22.0 7800.0
80 인천 2018.0 4.082546 622.0 11109.0 3.808002 26.0 10323.0
81 인천 2019.0 3.764131 554.0 12986.0 3.648073 22.0 12639.0
82 인천 2020.0 3.125181 551.0 13812.0 3.018436 19.0 13235.0
83 인천 2021.0 2.604997 703.0 15648.0 1.916362 444.0 15615.0

84 rows × 8 columns

그룹화 기준을 연도, 시도로 바꾸어보자.

df.groupby( by =["연도", "시도"]).mean().reset_index()
연도 시도 산재_평균근로자수 산재만_가입된사업장수 산재_가입된사업장수 고용_평균근로자수 고용만_가입된사업장수 고용_가입된사업장수
0 2010.0 광주 6.863187 52.0 1820.0 6.847419 1.0 1763.0
1 2010.0 대구 7.366627 82.0 2523.0 7.051240 7.0 2420.0
2 2010.0 대전 9.100746 59.0 1340.0 8.816680 3.0 1271.0
3 2010.0 부산 8.126126 121.0 3108.0 7.622692 11.0 2979.0
4 2010.0 서울 10.971553 343.0 11741.0 10.869985 30.0 11391.0
... ... ... ... ... ... ... ... ...
79 2021.0 대전 2.658709 434.0 7917.0 1.887973 369.0 8007.0
80 2021.0 부산 2.874647 688.0 17367.0 1.909389 660.0 17680.0
81 2021.0 서울 7.487265 2375.0 59009.0 2.020801 1410.0 58700.0
82 2021.0 울산 3.442591 273.0 5095.0 2.080108 262.0 5193.0
83 2021.0 인천 2.604997 703.0 15648.0 1.916362 444.0 15615.0

84 rows × 8 columns

요약#

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

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

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