본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 16. · 5 Views
카테고리 데이터 분석 완벽 가이드
범주형 데이터를 효과적으로 분석하는 방법을 배웁니다. value_counts()부터 시각화까지, 실무에서 바로 활용할 수 있는 pandas의 카테고리 데이터 처리 기법을 단계별로 익힙니다.
목차
1. 범주형 데이터란
어느 날 김데이터 씨는 고객 설문 데이터를 분석하는 업무를 맡았습니다. 엑셀 파일을 열어보니 "만족도" 열에는 "매우만족", "만족", "보통", "불만족"이라는 텍스트가 가득했습니다.
"이걸 어떻게 분석하지?"
범주형 데이터는 정해진 몇 가지 값 중 하나를 가지는 데이터입니다. 숫자가 아닌 문자열로 표현되는 경우가 많으며, 성별, 등급, 지역명처럼 분류를 나타냅니다.
이런 데이터를 제대로 다루는 것이 데이터 분석의 첫걸음입니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 범주형 데이터가 포함된 데이터프레임 생성
survey_data = pd.DataFrame({
'고객명': ['김철수', '이영희', '박민수', '정수진', '최영호'],
'만족도': ['만족', '매우만족', '보통', '만족', '불만족'],
'지역': ['서울', '부산', '서울', '대구', '서울']
})
# 데이터 확인
print(survey_data)
print("\n데이터 타입:")
print(survey_data.dtypes)
김데이터 씨는 데이터 분석팀에 입사한 지 2개월 된 주니어 분석가입니다. 오늘 팀장님께서 "지난달 고객 만족도 조사 결과를 정리해주세요"라는 업무를 주셨습니다.
의욕 넘치게 데이터를 열어봤지만, 숫자가 아닌 텍스트로 가득한 열들을 보고 막막해졌습니다. 바로 옆자리의 선배 박분석 씨가 화면을 슬쩍 보더니 미소를 짓습니다.
"아, 범주형 데이터 처음 다뤄보시는군요. 걱정 마세요, 이거 알고 나면 정말 쉬워요." 범주형 데이터란 무엇일까요?
쉽게 비유하자면, 범주형 데이터는 마치 설문지의 객관식 문항과 같습니다. "귀하의 만족도는?" 질문에 다섯 개의 선택지가 있고, 응답자는 그중 하나만 선택할 수 있습니다.
1번부터 5번까지의 번호를 매길 수도 있지만, 실제로는 "매우만족", "만족" 같은 명확한 범주로 표현됩니다. 이런 데이터는 우리 주변에 정말 많습니다.
혈액형(A, B, O, AB), 성별(남, 여), 학력(고졸, 대졸, 대학원졸), 상품 등급(Gold, Silver, Bronze) 등이 모두 범주형 데이터입니다. 범주형 데이터의 특징은 무엇일까요?
첫째, 가질 수 있는 값의 종류가 제한적입니다. 무한대로 많은 값을 가질 수 있는 연속형 숫자 데이터와 달리, 범주형 데이터는 미리 정해진 몇 가지 값만 가질 수 있습니다.
만족도가 "매우만족"과 "만족" 사이의 중간 값을 가질 수는 없는 것처럼 말입니다. 둘째, 순서가 있을 수도 있고 없을 수도 있습니다.
"매우만족 > 만족 > 보통 > 불만족"처럼 순서가 의미 있는 경우를 순서형(Ordinal) 범주형 데이터라고 합니다. 반면 "서울", "부산", "대구" 같은 지역명은 순서가 없는 명목형(Nominal) 범주형 데이터입니다.
셋째, 문자열로 저장되지만 내부적으로는 숫자로 처리됩니다. 컴퓨터는 문자열 비교보다 숫자 비교가 훨씬 빠릅니다.
따라서 pandas는 범주형 데이터를 효율적으로 처리하기 위해 내부적으로 정수 코드를 사용합니다. 위의 코드에서 우리는 간단한 고객 설문 데이터를 만들었습니다.
survey_data라는 데이터프레임에는 고객명, 만족도, 지역이라는 세 개의 열이 있습니다. dtypes를 확인해보면 현재는 object 타입으로 저장되어 있음을 알 수 있습니다.
object 타입은 pandas에서 문자열을 저장할 때 사용하는 기본 타입입니다. 하지만 이것은 일반 문자열로 취급되기 때문에, 범주형 데이터의 특성을 제대로 활용할 수 없습니다.
메모리도 많이 사용하고, 정렬이나 집계 연산도 비효율적입니다. "그렇다면 어떻게 해야 하나요?" 김데이터 씨가 물었습니다.
박분석 씨가 답합니다. "범주형 데이터를 제대로 다루려면, pandas의 category 타입으로 변환하고, value_counts() 같은 함수로 분석해야 합니다.
하나씩 배워봅시다." 실무에서는 고객 데이터, 상품 카테고리, 직원 직급, 프로젝트 상태 등 수많은 범주형 데이터를 다룹니다. CSV 파일을 읽어올 때 대부분 object 타입으로 들어오기 때문에, 이를 category 타입으로 변환하는 것이 성능 최적화의 첫걸음입니다.
범주형 데이터를 이해하면, 데이터의 분포를 파악하고, 그룹별 통계를 계산하고, 의미 있는 시각화를 만들 수 있습니다. 오늘 배울 내용들이 바로 그 출발점입니다.
실전 팁
💡 - 범주형 데이터는 object 타입으로 읽혀도 category 타입으로 변환하면 메모리와 속도가 개선됩니다
- 순서가 있는 범주(예: 학년, 등급)와 순서가 없는 범주(예: 지역, 색상)를 구분해서 처리하세요
2. value counts 활용
박분석 씨가 화면을 가리키며 말했습니다. "데이터 분석의 시작은 '각 값이 몇 번 나타나는가'를 파악하는 거예요.
pandas의 value_counts() 메서드를 사용하면 됩니다."
**value_counts()**는 범주형 데이터에서 각 값의 출현 횟수를 세는 메서드입니다. 빈도수가 높은 순서대로 자동 정렬되어 표시되므로, 데이터의 분포를 한눈에 파악할 수 있습니다.
가장 많이 사용되는 탐색적 데이터 분석 도구입니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 더 큰 데이터셋 생성
survey_data = pd.DataFrame({
'만족도': ['만족', '매우만족', '보통', '만족', '불만족',
'만족', '매우만족', '만족', '보통', '불만족',
'만족', '만족', '보통', '매우불만족', '만족'],
'지역': ['서울', '부산', '서울', '대구', '서울',
'서울', '부산', '서울', '인천', '대구',
'서울', '부산', '서울', '서울', '인천']
})
# 만족도별 빈도 계산
print("만족도 분포:")
print(survey_data['만족도'].value_counts())
김데이터 씨가 고개를 갸우뚱합니다. "빈도를 센다는 게 그렇게 중요한가요?" 박분석 씨가 웃으며 설명을 시작합니다.
"생각보다 훨씬 중요해요. 예를 들어 볼게요.
회사에서 고객 불만 처리 우선순위를 정한다고 합시다. 어떤 불만이 가장 많이 접수되는지 알아야 그것부터 해결할 수 있겠죠?" **value_counts()**는 마치 투표 결과를 집계하는 개표 작업과 같습니다.
투표용지에 적힌 후보자 이름을 하나씩 세어, 각 후보가 몇 표를 받았는지 계산합니다. 그리고 득표수가 많은 순서대로 순위를 매깁니다.
value_counts()도 정확히 같은 일을 합니다. 위의 코드에서 우리는 15명의 고객 응답 데이터를 만들었습니다.
survey_data['만족도'].value_counts()를 실행하면 어떤 결과가 나올까요? 결과를 보면 "만족"이 8번으로 가장 많고, "보통"이 3번, "매우만족"이 2번, "불만족"이 1번, "매우불만족"이 1번 나타났음을 알 수 있습니다.
자동으로 빈도가 높은 순서대로 정렬되어 나타나기 때문에, 어떤 응답이 가장 많은지 한눈에 파악할 수 있습니다. value_counts()의 강력한 기능들을 더 살펴보겠습니다.
첫째, normalize=True 옵션을 사용하면 비율을 계산할 수 있습니다. 전체 응답 중 "만족"이 차지하는 비율이 궁금하다면, survey_data['만족도'].value_counts(normalize=True)를 실행하면 됩니다.
결과가 0.533이라면 약 53.3%를 의미합니다. 둘째, dropna=False 옵션을 사용하면 **결측값(NaN)**도 집계에 포함됩니다.
기본적으로는 NaN 값이 제외되지만, 실무에서는 "무응답"도 중요한 정보일 수 있습니다. 셋째, sort=False 옵션을 사용하면 빈도순 정렬을 하지 않고 원본 순서를 유지합니다.
하지만 대부분의 경우 빈도순 정렬이 더 유용합니다. 김데이터 씨가 직접 코드를 실행해봅니다.
"오, 정말 간단하네요! 그런데 지역별로도 세어볼 수 있나요?" "물론이죠!" 박분석 씨가 답합니다.
survey_data['지역'].value_counts()를 실행하면, 서울 응답자가 9명으로 가장 많고, 부산이 3명, 대구와 인천이 각각 2명, 1명임을 알 수 있습니다. 실무에서는 여러 열에 대해 동시에 value_counts()를 실행하는 경우가 많습니다.
예를 들어 "지역별로 만족도 분포가 어떻게 다른가?"라는 질문에 답하려면, survey_data.groupby('지역')['만족도'].value_counts()를 사용할 수 있습니다. 이렇게 하면 서울 응답자 중 몇 명이 "만족"했는지, 부산 응답자는 어떤지 등을 파악할 수 있습니다.
value_counts()의 실전 활용 사례를 들어보겠습니다. 전자상거래 회사에서 상품 카테고리별 판매량을 분석한다고 가정해봅시다.
수십만 건의 주문 데이터가 있고, 각 주문에는 상품 카테고리가 기록되어 있습니다. orders['카테고리'].value_counts()를 실행하면, 어떤 카테고리가 가장 많이 팔리는지 즉시 알 수 있습니다.
이 정보를 바탕으로 재고 관리, 마케팅 전략, 추천 시스템 등을 개선할 수 있습니다. 하지만 주의할 점도 있습니다.
value_counts()는 단순히 횟수만 세기 때문에, 값 자체의 크기나 의미는 고려하지 않습니다. 예를 들어 "상" 등급이 10개, "중" 등급이 50개, "하" 등급이 40개라면, value_counts()는 "중"이 가장 많다고 알려줍니다.
하지만 품질 관점에서는 "상" 등급의 비율이 낮다는 것이 더 중요한 정보일 수 있습니다. 김데이터 씨가 고개를 끄덕입니다.
"이제 이해했어요. 빈도를 파악하는 게 분석의 첫걸음이군요!"
실전 팁
💡 - normalize=True로 빈도 대신 비율을 확인할 수 있습니다
- dropna=False로 결측값도 집계에 포함시킬 수 있습니다
3. 상위 N개 추출하기
김데이터 씨가 실제 데이터를 분석하다가 당황했습니다. 상품 카테고리가 무려 500개나 되는데, 전체를 다 보여주면 보고서가 너무 길어집니다.
"상위 10개만 뽑을 수는 없을까요?"
head() 메서드를 value_counts() 결과에 적용하면 상위 N개만 추출할 수 있습니다. 실무에서는 수백 개의 범주 중 주요 범주만 분석하거나 보고하는 경우가 많기 때문에, 상위 추출 기능이 필수적입니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 다양한 카테고리를 가진 데이터 생성
products = pd.DataFrame({
'카테고리': ['전자기기', '의류', '식품', '도서', '전자기기',
'의류', '전자기기', '식품', '전자기기', '가구',
'전자기기', '의류', '전자기기', '도서', '전자기기',
'스포츠', '전자기기', '뷰티', '전자기기', '의류']
})
# 전체 빈도 계산
all_counts = products['카테고리'].value_counts()
print("전체 카테고리 빈도:")
print(all_counts)
# 상위 3개만 추출
top3 = products['카테고리'].value_counts().head(3)
print("\n상위 3개 카테고리:")
print(top3)
박분석 씨가 김데이터 씨의 화면을 보더니 공감하는 표정을 짓습니다. "맞아요, 실무에서는 범주가 정말 많을 때가 있어요.
온라인 쇼핑몰 데이터를 다뤄보면 상품 카테고리만 수백 개가 넘어요." 상위 N개 추출은 마치 시상식에서 1등부터 3등까지만 시상하는 것과 같습니다. 100명이 경주에 참여했지만, 모든 순위를 발표하면 시간이 너무 오래 걸립니다.
상위 입상자만 발표하고, 나머지는 "기타"로 처리하는 것이 효율적입니다. 데이터 분석도 마찬가지입니다.
위의 코드를 보면, 20개의 상품 데이터에서 카테고리별 빈도를 계산했습니다. value_counts()를 실행하면 "전자기기"가 9번으로 압도적으로 많고, "의류"가 4번, "식품"과 "도서"가 각각 2번씩 나타납니다.
나머지 "가구", "스포츠", "뷰티"는 각각 1번씩만 나타납니다. 이때 .head(3)를 추가하면 상위 3개, 즉 "전자기기", "의류", "식품"만 결과에 나타납니다.
보고서나 대시보드에서 핵심 정보만 보여줄 때 매우 유용합니다. **head()의 반대는 tail()**입니다.
최하위 N개를 추출하고 싶다면 .tail(3)을 사용하면 됩니다. 빈도가 가장 낮은 범주들을 확인할 때 유용합니다.
예를 들어 "거의 팔리지 않는 카테고리"를 파악해서 재고를 줄이거나 카탈로그에서 제외하는 결정을 내릴 수 있습니다. 김데이터 씨가 질문합니다.
"그런데 상위 3개 외의 나머지를 '기타'로 묶어서 보여주고 싶으면 어떻게 하나요?" "좋은 질문이에요!" 박분석 씨가 답합니다. "조금 더 복잡하지만 가능해요." 실무에서는 **파레토 원칙(80-20 법칙)**을 자주 활용합니다.
전체 매출의 80%를 차지하는 상위 20% 카테고리를 집중 관리하고, 나머지는 "기타"로 처리하는 것입니다. 이렇게 하려면 다음과 같은 절차를 밟습니다.
첫째, value_counts()로 빈도를 계산합니다. 둘째, head()로 상위 N개를 추출합니다.
셋째, 원본 데이터에서 상위 N개에 속하지 않는 값들을 "기타"로 치환합니다. 넷째, 다시 value_counts()를 실행하면 상위 N개와 "기타" 범주만 남습니다.
코드로 작성하면 이렇습니다. python top3_categories = products['카테고리'].value_counts().head(3).index products['카테고리_그룹'] = products['카테고리'].apply( lambda x: x if x in top3_categories else '기타' ) print(products['카테고리_그룹'].value_counts()) 이렇게 하면 "전자기기", "의류", "식품", "기타" 네 개의 범주만 남게 됩니다.
실전 활용 사례를 살펴보겠습니다. 대형 포털 사이트의 검색어 분석을 담당한다고 가정해봅시다.
하루에 수백만 건의 검색이 이루어지고, 검색어의 종류는 수십만 개에 달합니다. 이 모든 검색어를 리포트에 담을 수는 없습니다.
따라서 상위 100개 검색어만 추출해서 트렌드를 파악하고, 나머지는 "롱테일 검색어"로 분류합니다. 또 다른 예시로, 고객 불만 접수 시스템을 생각해봅시다.
불만 유형이 수십 가지가 있지만, 실제로는 상위 5개 유형이 전체 불만의 70%를 차지합니다. 이 상위 5개 유형을 집중적으로 개선하면 고객 만족도를 크게 높일 수 있습니다.
김데이터 씨가 코드를 직접 작성해보며 익숙해집니다. "이제 데이터를 요약하는 방법을 알겠어요!" 박분석 씨가 덧붙입니다.
"데이터 분석의 핵심은 중요한 것과 덜 중요한 것을 구분하는 거예요. 상위 추출은 그 첫걸음입니다."
실전 팁
💡 - head(N)으로 상위 N개, tail(N)으로 하위 N개를 추출할 수 있습니다
- 상위 범주에 속하지 않는 값들을 "기타"로 묶으면 보고서가 깔끔해집니다
4. category 타입 변환
박분석 씨가 김데이터 씨의 코드를 보더니 말했습니다. "지금까지는 잘했는데, 데이터가 object 타입이라 메모리를 너무 많이 쓰고 있어요.
category 타입으로 바꿔봅시다."
category 타입은 pandas에서 범주형 데이터를 효율적으로 저장하는 전용 자료형입니다. 문자열을 내부적으로 정수 코드로 변환하여 메모리 사용량을 크게 줄이고, 정렬과 집계 연산 속도도 향상됩니다.
대용량 데이터 처리에 필수적입니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 데이터 생성
survey_data = pd.DataFrame({
'만족도': ['만족', '매우만족', '보통', '만족', '불만족'] * 1000,
'지역': ['서울', '부산', '서울', '대구', '서울'] * 1000
})
# 변환 전 메모리 사용량 확인
print("변환 전 메모리 사용량:")
print(survey_data.memory_usage(deep=True))
# category 타입으로 변환
survey_data['만족도'] = survey_data['만족도'].astype('category')
survey_data['지역'] = survey_data['지역'].astype('category')
# 변환 후 메모리 사용량 확인
print("\n변환 후 메모리 사용량:")
print(survey_data.memory_usage(deep=True))
김데이터 씨가 의아한 표정을 짓습니다. "지금도 잘 작동하는데 왜 타입을 바꿔야 하나요?" 박분석 씨가 설명을 시작합니다.
"지금은 데이터가 작아서 문제가 안 되지만, 실무에서는 수십만 행, 수백만 행을 다루게 돼요. 그때는 메모리가 부족해서 프로그램이 멈출 수도 있습니다." category 타입의 원리를 이해하기 위해 비유를 들어보겠습니다.
도서관에 같은 책이 100권 있다고 가정해봅시다. 각 책마다 "해리 포터와 마법사의 돌"이라는 제목을 전부 적어놓으면 종이가 엄청나게 많이 필요합니다.
하지만 이 책에 번호 "001"을 부여하고, 100권 모두 "001"이라는 번호만 적어놓으면 종이가 훨씬 적게 듭니다. 나중에 번호표를 보고 "001은 해리 포터구나"라고 찾아보면 됩니다.
pandas의 category 타입도 똑같은 방식으로 작동합니다. "만족", "매우만족", "보통" 같은 문자열을 매번 저장하는 대신, 각각에 0, 1, 2 같은 정수 코드를 부여합니다.
그리고 데이터에는 정수 코드만 저장하고, 원본 문자열은 단 한 번만 저장합니다. 위의 코드를 실행해보면 그 효과를 직접 확인할 수 있습니다.
5000개의 행을 가진 데이터프레임에서 "만족도"와 "지역" 열을 category 타입으로 변환하면, 메모리 사용량이 절반 이하로 줄어듭니다. 데이터가 클수록 효과가 더 큽니다.
category 타입의 장점은 크게 세 가지입니다. 첫째, 메모리 절약입니다.
같은 값이 반복될수록 절약 효과가 커집니다. "서울"이라는 문자열이 1만 번 나타나도, category 타입에서는 "서울"을 한 번만 저장하고 정수 코드 1만 개를 저장합니다.
둘째, 연산 속도 향상입니다. 문자열 비교보다 정수 비교가 훨씬 빠릅니다.
정렬, 그룹화, 필터링 같은 연산이 모두 빠르게 처리됩니다. 셋째, 범주 정보 유지입니다.
category 타입은 데이터에 나타나지 않은 범주도 미리 정의할 수 있습니다. 예를 들어 "매우불만족"이라는 응답이 현재 데이터에는 없지만, 범주로 미리 정의해두면 나중에 추가될 때 일관성 있게 처리할 수 있습니다.
김데이터 씨가 직접 코드를 실행해봅니다. "오, 정말 메모리가 확 줄어드네요!" 박분석 씨가 계속 설명합니다.
"변환 방법은 간단해요. astype('category')를 사용하면 됩니다." 실무에서의 활용 팁을 추가로 알려드리겠습니다.
CSV 파일을 읽어올 때부터 category 타입으로 지정할 수도 있습니다. pd.read_csv('data.csv', dtype={'만족도': 'category', '지역': 'category'})처럼 작성하면, 파일을 읽는 순간부터 효율적으로 처리됩니다.
하지만 주의할 점도 있습니다. category 타입으로 변환한 후에는 새로운 범주를 추가하기가 까다롭습니다.
예를 들어 "만족도"에 "매우만족", "만족", "보통"만 있었는데 갑자기 "최고" 같은 새로운 값이 추가되면 에러가 발생할 수 있습니다. 이럴 때는 범주를 먼저 확장해야 합니다.
또한 모든 문자열 열을 category로 변환하면 안 됩니다. 고객명, 이메일 주소, 코멘트 같은 고유값이 많은 열은 category로 변환하면 오히려 메모리가 늘어납니다.
같은 값이 반복되는 열에만 사용해야 합니다. 김데이터 씨가 정리합니다.
"반복되는 값이 많은 열은 category 타입으로 바꾸면 좋군요!"
실전 팁
💡 - astype('category')로 간단히 변환할 수 있습니다
- 고유값이 많은 열(이름, 이메일 등)은 category로 변환하지 마세요
5. 범주 순서 지정하기
김데이터 씨가 만족도 데이터를 정렬하려다가 문제를 발견했습니다. "매우만족"이 "보통"보다 뒤에 나타나는 거예요.
알파벳 순서로 정렬되고 있었습니다. "만족도는 순서가 있는데, 어떻게 해야 하나요?"
순서형 범주(Ordered Category)는 범주 간에 의미 있는 순서가 있는 경우를 말합니다. pandas에서는 CategoricalDtype을 사용해 범주의 순서를 명시적으로 지정할 수 있습니다.
순서를 지정하면 정렬, 비교, 시각화가 의도한 대로 작동합니다.
다음 코드를 살펴봅시다.
import pandas as pd
from pandas.api.types import CategoricalDtype
# 만족도 데이터 생성
survey_data = pd.DataFrame({
'만족도': ['보통', '매우만족', '불만족', '만족', '매우불만족', '보통', '만족']
})
# 순서 지정
satisfaction_order = ['매우불만족', '불만족', '보통', '만족', '매우만족']
satisfaction_type = CategoricalDtype(categories=satisfaction_order, ordered=True)
# 순서형 category로 변환
survey_data['만족도'] = survey_data['만족도'].astype(satisfaction_type)
# 정렬 (이제 만족도 순서대로 정렬됨)
print("순서대로 정렬:")
print(survey_data.sort_values('만족도'))
박분석 씨가 고개를 끄덕입니다. "맞아요, 이게 초보 분석가들이 자주 놓치는 부분이에요.
범주형 데이터 중에서도 순서가 중요한 것들이 있거든요." 순서형 범주를 이해하기 위해 비유를 들어보겠습니다. 학교 성적을 생각해봅시다.
A, B, C, D, F라는 등급이 있습니다. 이것들은 단순히 다른 문자가 아니라, A가 B보다 좋고, B가 C보다 좋다는 명확한 순서가 있습니다.
만약 학생들을 성적순으로 정렬한다면, 알파벳 순서가 아니라 A, B, C, D, F 순서여야 합니다. 만족도도 마찬가지입니다.
"매우만족"이 "만족"보다 좋고, "만족"이 "보통"보다 좋고, "보통"이 "불만족"보다 좋습니다. 이런 순서를 pandas에게 알려주지 않으면, 단순히 알파벳 순서로 처리됩니다.
위의 코드를 보면, CategoricalDtype을 사용해서 순서를 명시적으로 지정했습니다. categories 매개변수에는 범주들을 순서대로 나열하고, ordered=True로 설정하면 순서가 있는 범주형 데이터가 됩니다.
이제 sort_values('만족도')를 실행하면, "매우불만족", "불만족", "보통", "만족", "매우만족" 순서로 정렬됩니다. 데이터 분석 결과가 훨씬 자연스럽고 이해하기 쉬워집니다.
순서형 범주의 강력한 기능은 비교 연산도 가능하다는 점입니다. 예를 들어 survey_data[survey_data['만족도'] >= '만족']이라고 작성하면, "만족" 이상인 응답만 필터링됩니다.
즉, "만족"과 "매우만족"만 추출됩니다. 순서를 지정하지 않았다면 이런 비교 연산이 불가능합니다.
김데이터 씨가 감탄합니다. "와, 부등호로 비교할 수 있다니 신기해요!" 박분석 씨가 계속 설명합니다.
"이게 바로 순서형 범주의 장점이에요. 통계 분석에서도 유용하게 사용됩니다." 실무 활용 사례를 살펴보겠습니다.
직원 성과 평가 시스템을 만든다고 가정해봅시다. 평가 등급이 S, A, B, C, D로 나뉘어 있습니다.
이를 순서형 범주로 정의하면, "B등급 이상 직원의 비율"을 쉽게 계산할 수 있고, 등급별로 정렬된 리포트를 자동 생성할 수 있습니다. 또 다른 예로, 고객 등급 시스템을 생각해봅시다.
VIP, Gold, Silver, Bronze라는 등급이 있을 때, 순서를 지정하면 "Gold 이상 고객에게 쿠폰 발송" 같은 작업을 간단한 필터링으로 처리할 수 있습니다. 시각화에서도 순서형 범주가 중요합니다.
막대 그래프를 그릴 때 순서를 지정하지 않으면 알파벳 순서로 나타나서 보기 불편합니다. 하지만 순서를 지정하면 "매우불만족"부터 "매우만족"까지 자연스러운 흐름으로 표시됩니다.
순서 지정 시 주의사항이 있습니다. 첫째, categories 리스트에 모든 범주를 포함해야 합니다.
만약 "매우불만족"을 빠뜨리면 해당 값이 NaN으로 처리됩니다. 둘째, 순서는 의미가 명확할 때만 지정해야 합니다.
예를 들어 지역명("서울", "부산", "대구")은 순서가 없으므로, ordered=False로 두어야 합니다. 셋째, 한 번 순서를 지정하면 나중에 변경하기가 까다롭습니다.
따라서 처음부터 신중하게 정의해야 합니다. 김데이터 씨가 코드를 수정하며 익숙해집니다.
"이제 정렬이 제대로 되네요!"
실전 팁
💡 - CategoricalDtype(categories=[순서], ordered=True)로 순서를 지정하세요
- 순서를 지정하면 부등호(>=, <=)로 범주를 비교할 수 있습니다
6. 범주별 비율 계산
김데이터 씨가 보고서를 작성하는데, 팀장님이 말씀하셨습니다. "개수도 좋지만, 비율로 표현하면 더 이해하기 쉬워요.
전체 응답 중 몇 퍼센트인지 알려주세요."
normalize=True 옵션을 value_counts()에 추가하면 개수 대신 비율을 계산할 수 있습니다. 결과는 0과 1 사이의 소수로 나타나며, 100을 곱하면 퍼센트가 됩니다.
비율은 서로 다른 크기의 그룹을 비교할 때 특히 유용합니다.
다음 코드를 살펴봅시다.
import pandas as pd
# 만족도 데이터 생성
survey_data = pd.DataFrame({
'만족도': ['만족', '매우만족', '보통', '만족', '불만족',
'만족', '매우만족', '만족', '보통', '불만족',
'만족', '만족', '보통', '매우불만족', '만족']
})
# 개수 계산
print("개수:")
print(survey_data['만족도'].value_counts())
# 비율 계산
print("\n비율:")
print(survey_data['만족도'].value_counts(normalize=True))
# 퍼센트로 변환
print("\n퍼센트:")
print(survey_data['만족도'].value_counts(normalize=True) * 100)
박분석 씨가 웃으며 말합니다. "팀장님 말씀이 맞아요.
비율이 훨씬 직관적이거든요." 비율 계산의 중요성을 예시로 설명해보겠습니다. 두 개의 설문조사 결과가 있다고 가정해봅시다.
A 설문에는 1000명이 참여했고 "만족" 응답이 500명입니다. B 설문에는 100명이 참여했고 "만족" 응답이 60명입니다.
어느 쪽이 만족도가 높을까요? 개수만 보면 A가 500명으로 더 많습니다.
하지만 비율로 계산하면 A는 50%, B는 60%입니다. 실제로는 B의 만족도가 더 높은 것입니다.
이처럼 절대적인 개수보다 상대적인 비율이 더 의미 있는 경우가 많습니다. 위의 코드에서 normalize=True를 추가하면, 결과가 개수가 아닌 비율로 나타납니다.
"만족"이 0.533이라면, 전체 응답의 53.3%를 의미합니다. 100을 곱하면 퍼센트 형태로 표현할 수 있습니다.
value_counts(normalize=True)의 작동 원리는 간단합니다. 각 범주의 개수를 전체 개수로 나눈 값입니다.
"만족"이 8번, 전체가 15번이면, 8 / 15 = 0.533입니다. pandas가 자동으로 계산해주기 때문에, 우리는 옵션만 추가하면 됩니다.
김데이터 씨가 코드를 실행해봅니다. "오, 정말 간단하네요!" 박분석 씨가 추가 설명을 합니다.
"비율을 사용하면 다른 데이터셋과 비교하기도 쉬워요." 실무 활용 사례를 들어보겠습니다. 월별 고객 만족도를 비교한다고 가정해봅시다.
1월에는 응답자가 1000명, 2월에는 1500명, 3월에는 800명입니다. 각 월의 "만족" 응답 개수를 비교하면 의미가 없습니다.
응답자 수가 다르기 때문입니다. 하지만 비율로 계산하면 1월 60%, 2월 55%, 3월 70%처럼 공정하게 비교할 수 있습니다.
또 다른 예로, 지역별 상품 선호도를 분석한다고 가정해봅시다. 서울은 인구가 많아서 모든 상품의 구매 개수가 높게 나타납니다.
하지만 비율로 계산하면, 서울에서는 A 상품이 30%, 부산에서는 A 상품이 50%처럼 실제 선호도를 파악할 수 있습니다. 비율 계산 시 주의사항이 있습니다.
첫째, 결측값(NaN)이 있을 때 조심해야 합니다. 기본적으로 dropna=True이므로 NaN은 제외되고 비율이 계산됩니다.
만약 NaN도 포함하려면 dropna=False를 추가해야 합니다. 둘째, 비율의 합은 항상 1.0(또는 100%)입니다.
만약 head()로 상위 3개만 추출하면, 그 비율의 합은 1.0보다 작을 수 있습니다. 예를 들어 상위 3개의 비율 합이 0.8이면, 나머지 범주들이 0.2를 차지한다는 의미입니다.
셋째, 소수점 자릿수를 적절히 조정해야 합니다. 0.533333...처럼 표시되면 보기 불편하므로, round(2)를 추가해서 소수점 둘째 자리까지만 표시할 수 있습니다.
김데이터 씨가 보고서를 수정합니다. "이제 팀장님께서 이해하시기 쉬울 것 같아요!" 박분석 씨가 격려합니다.
"비율은 데이터 분석의 기본이에요. 잘 익혀두세요."
실전 팁
💡 - normalize=True로 비율을, * 100으로 퍼센트를 구할 수 있습니다
- round(2)로 소수점 자릿수를 조정하면 보기 좋습니다
7. 파이 차트로 시각화
박분석 씨가 말했습니다. "숫자로 보는 것도 좋지만, 그림으로 보면 더 직관적이에요.
파이 차트를 그려봅시다."
파이 차트(Pie Chart)는 범주별 비율을 원형 그래프로 나타내는 시각화 방법입니다. pandas의 plot() 메서드와 matplotlib을 사용하면 간단히 그릴 수 있습니다.
전체 중 각 범주가 차지하는 비중을 한눈에 파악할 수 있습니다.
다음 코드를 살펴봅시다.
import pandas as pd
import matplotlib.pyplot as plt
# 한글 폰트 설정 (한글 깨짐 방지)
plt.rc('font', family='AppleGothic') # Mac
# plt.rc('font', family='Malgun Gothic') # Windows
# 만족도 데이터 생성
survey_data = pd.DataFrame({
'만족도': ['만족', '매우만족', '보통', '만족', '불만족',
'만족', '매우만족', '만족', '보통', '불만족',
'만족', '만족', '보통', '매우불만족', '만족']
})
# 빈도 계산
satisfaction_counts = survey_data['만족도'].value_counts()
# 파이 차트 그리기
satisfaction_counts.plot(kind='pie', autopct='%1.1f%%', startangle=90)
plt.title('고객 만족도 분포')
plt.ylabel('') # y축 레이블 제거
plt.show()
김데이터 씨가 눈을 반짝입니다. "드디어 시각화네요!
이걸 배우고 싶었어요." 박분석 씨가 설명을 시작합니다. "시각화는 분석 결과를 전달하는 가장 강력한 도구예요.
특히 비전문가에게 설명할 때 필수입니다." 파이 차트의 특징을 이해하기 위해 비유를 들어보겠습니다. 파이 차트는 마치 피자를 여러 조각으로 자른 것과 같습니다.
전체 피자가 100%이고, 각 조각의 크기가 각 범주의 비율을 나타냅니다. "만족"이 53.3%라면, 피자의 절반 조금 넘는 크기로 표시됩니다.
한눈에 어떤 범주가 큰 비중을 차지하는지 알 수 있습니다. 위의 코드를 보면, plot(kind='pie')만 추가하면 파이 차트가 생성됩니다.
pandas와 matplotlib이 자동으로 색상을 지정하고, 범주 이름을 레이블로 표시합니다. plot() 메서드의 주요 옵션을 살펴보겠습니다.
첫째, autopct='%1.1f%%'는 각 조각에 퍼센트를 표시합니다. %1.1f는 소수점 첫째 자리까지 표시하라는 의미입니다.
%1.0f%%로 하면 정수로 표시됩니다. 둘째, startangle=90은 첫 번째 조각의 시작 각도를 90도로 설정합니다.
기본값은 0도인데, 90도로 하면 가장 큰 조각이 위쪽에 나타나서 보기 좋습니다. 셋째, colors=['red', 'blue', 'green']처럼 색상을 직접 지정할 수도 있습니다.
기본 색상이 마음에 들지 않을 때 유용합니다. 넷째, explode=[0.1, 0, 0, 0, 0]을 사용하면 특정 조각을 분리해서 강조할 수 있습니다.
첫 번째 조각이 약간 떨어져 나옵니다. 김데이터 씨가 직접 코드를 실행해봅니다.
"와, 정말 예쁘게 나오네요!" 박분석 씨가 주의사항을 알려줍니다. "파이 차트는 범주가 너무 많으면 보기 어려워요.
5~7개 이하일 때 사용하는 게 좋습니다." 파이 차트 vs 막대 그래프를 비교해보겠습니다. 파이 차트는 전체 대비 비율을 강조할 때 좋습니다.
"만족 응답이 절반 이상을 차지합니다"처럼 표현할 때 효과적입니다. 하지만 정확한 수치를 비교하기는 어렵습니다.
막대 그래프는 범주 간 정확한 비교에 유리합니다. "만족이 불만족보다 몇 개 더 많은가"를 쉽게 파악할 수 있습니다.
또한 범주가 많을 때도 깔끔하게 표현됩니다. 실무에서는 목적에 따라 선택합니다.
경영진 보고서에는 파이 차트가 인기 있고, 기술 보고서에는 막대 그래프가 선호됩니다. 한글 폰트 설정 주의사항이 있습니다.
matplotlib은 기본적으로 한글을 지원하지 않아서, 한글이 깨져서 네모로 표시됩니다. 따라서 코드 상단에 plt.rc('font', family='폰트명')을 추가해야 합니다.
Mac에서는 'AppleGothic', Windows에서는 'Malgun Gothic'이 기본 폰트입니다. 만약 폰트를 찾을 수 없다는 에러가 나면, 시스템에 설치된 폰트 목록을 확인하고 사용 가능한 폰트를 지정해야 합니다.
실무 활용 사례를 들어보겠습니다. 마케팅 팀에서 캠페인 효과를 보고한다고 가정해봅시다.
"이번 캠페인 후 고객 만족도가 개선되었는가?"라는 질문에 답하기 위해, 캠페인 전과 후의 만족도 분포를 파이 차트로 비교할 수 있습니다. 두 개의 파이 차트를 나란히 놓으면, "매우만족" 비율이 늘어났는지 시각적으로 명확하게 보입니다.
또 다른 예로, 제품 카테고리별 매출 비중을 임원진에게 보고할 때 파이 차트를 사용하면 "전자기기가 매출의 40%를 차지합니다"처럼 직관적으로 전달할 수 있습니다. 김데이터 씨가 만족스럽게 차트를 바라봅니다.
"이제 제대로 된 분석 보고서를 만들 수 있을 것 같아요!" 박분석 씨가 격려합니다. "오늘 배운 내용만으로도 대부분의 범주형 데이터를 다룰 수 있어요.
실전에서 많이 연습해보세요."
실전 팁
💡 - autopct='%1.1f%%'로 퍼센트를 표시하세요
- 범주가 5~7개 이하일 때 파이 차트가 효과적입니다
- 한글 폰트 설정을 잊지 마세요 (plt.rc('font', family='폰트명'))
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.