[pandas] 수치형/연속형 데이터 구간 나누기

최근에 파이썬 pandas에서 특정 컬럼에 대해 범위에 따라 구간을 나누고, 그 구간에 속한 값들의 평균을 구할 수 있냐는 질문을 받았다.

(구간, 범주, 구역, 경계…? 어떤 표현이 좋은지 모르겠지만 아무튼 히스토그램 그릴 때처럼 그렇게 나누는 거.)

수치형, 연속형 데이터를 범주형 데이터로 바꾸는 과정인 셈이다.


우선 엑셀에서 읽어온 데이터가 이런 모양으로 생겼다고 해보자.

import pandas as pd
df = pd.read_excel("ex.xlsx")

내가 그냥 random으로 숫자를 넣은 거다. 요약을 보면

df.describe()

최소 150, 최대 199라는 걸 알았다.

이제 해보자. 10단위로 끊어서 구간을 구분하고, 그 안의 값들의 평균을 구하기.

난 두 가지 방법으로 해보았다.


방법 1. range와 for 반복문을 활용한다.

range 괄호 안에 (시작, 끝, 간격)을 차례로 입력해서 for 반복문을 돌릴 수 있다.

150~ 199 사이에서 10 단위로 반복문을 돌고, 데이터프레임에서 행을 조건에 따라 필터링해서, 평균을 출력했다.

for x in range(150, 200, 10):
    start = x
    end = x+10
    temp = df[(df["height"] >= start) & (df["height"] < end)]
    print("{}이상 {}미만 : {}".format(start, end, temp["height"].mean()))

결과는 아래와 같다.

150이상 160미만 : 155.0625
160이상 170미만 : 164.83333333333334
170이상 180미만 : 174.92307692307693
180이상 190미만 : 185.1
190이상 200미만 : 194.1

성공!

방법 2. pandas의 pd.cut()을 활용해 구간을 나누고 groupby()를 사용한다.

이번엔 애초에 구간을 나눠서 레이블링을 우선 해놓는 전략이다.

우선 구간을 나누는 기준점을 리스트를 만들어 놓아야 한다.

bins = list(range(150, 201, 10))
[150, 160, 170, 180, 190, 200]

여기서 중요한 건, 맨 끝 edge인 200도 포함해야 한다는 것.

그리고 이 기준점 사이에 들어갈 구간에 대한 레이블도 만들어놓자.

bins_label = [str(x)+"이상 "+str(x+10)+"미만" for x in bins]
['150이상 160미만',
 '160이상 170미만',
 '170이상 180미만',
 '180이상 190미만',
 '190이상 200미만',
 '200이상 210미만']

맨 끝에 200~210이 있네. 이건 나중에 버리면 된다.

이제 pd.cut()을 사용해서 값에 따라 구간을 나눌 수 있다.

df["level"] = pd.cut(df["height"], bins, right=False, labels=bins_label[:-1])

여기서 right=False로 지정한 이유는 특정 값 “미만”으로 구역을 나누기 때문이다. 그리고 5개 구간으로 나뉘기 때문에 labels를 지정할 때 미리 만들어 놓았떤 bins_label에서 맨 끝 값은 제외했다. (이렇게 안 하면 숫자가 안 맞아서 오류가 난다. 구간이 5개인데 레이블은 왜 6개냐고.)

아무튼 이렇게 다시 df를 찍어보면

구간에 따라 레이블을 붙인 컬럼이 생긴 걸 볼 수 있다.

이제 groupby를 사용해서 평균을 구하면

첫 번째 방법으로 구한 값과 동일하다.


나도 이메일로 질문을 받은 덕분에 이렇게 한 번 정리하고 넘어가게 되네.

질문은 언제나 환영입니다.

저도 파이썬 잘 못하지만요.

추천 글


댓글 남기기