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
먼저 연도별 부산, 울산, 대구의 고용보험 가입된 사업장 수를 계산해 보자.
연도별 고용보험 가입된 사업장 수를 계산하려면
전체 자료를 연도별로 나누고
각 연도에서 모든 시구의 고용보험 가입된 사업장 수를 더해야한다.
즉, 데이터프레임을 지정된 열(연도)의 값(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()를 사용하며 이를 통해 그룹별로 요약할 수 있다.