🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Pandas 데이터 분석 실전 예제 - 슬라이드 1/11
A

AI Generated

2025. 10. 30. · 27 Views

Pandas 데이터 분석 실전 예제 완벽 가이드

실무에서 가장 많이 사용되는 Pandas 데이터 분석 기법을 단계별로 학습합니다. CSV 파일 읽기부터 데이터 정제, 분석, 시각화까지 실전 예제로 배워보세요.


목차

  1. DataFrame 생성과 CSV 데이터 읽기
  2. 데이터 탐색과 기본 정보 확인
  3. 결측치 처리와 데이터 정제
  4. 데이터 필터링과 조건 검색
  5. 그룹화와 집계 분석
  6. 데이터 정렬과 순위 매기기
  7. 새로운 컬럼 생성과 데이터 변환
  8. 데이터 병합과 조인

1. DataFrame 생성과 CSV 데이터 읽기

시작하며

여러분이 회사에서 첫 데이터 분석 업무를 맡았을 때 이런 상황을 겪어본 적 있나요? 엑셀 파일이나 CSV 파일에 담긴 수천, 수만 개의 데이터를 분석해야 하는데, 엑셀로는 속도도 느리고 복잡한 분석이 어려운 상황 말이죠.

이런 문제는 실제 개발 현장에서 매일같이 발생합니다. 엑셀은 대용량 데이터를 다루기에 한계가 있고, 반복적인 작업을 자동화하기도 어렵습니다.

게다가 복잡한 데이터 변환이나 통계 분석을 하려면 많은 시간과 노력이 필요합니다. 바로 이럴 때 필요한 것이 Pandas의 DataFrame입니다.

DataFrame을 사용하면 대용량 데이터를 빠르게 읽어들이고, 코드 몇 줄로 복잡한 분석을 자동화할 수 있습니다.

개요

간단히 말해서, DataFrame은 표 형태의 데이터를 다루는 Pandas의 핵심 자료구조입니다. 엑셀의 시트처럼 행과 열로 구성되어 있지만, 프로그래밍으로 제어할 수 있어 훨씬 더 강력합니다.

DataFrame이 필요한 이유는 실무에서 대부분의 데이터가 표 형태로 저장되기 때문입니다. CSV 파일, 데이터베이스 테이블, API 응답 등 거의 모든 데이터 소스를 DataFrame으로 변환하여 일관된 방식으로 분석할 수 있습니다.

예를 들어, 고객 데이터를 CSV로 받아서 분석하거나, 일일 매출 데이터를 집계하는 경우에 매우 유용합니다. 기존에는 엑셀로 파일을 열어서 수작업으로 데이터를 확인하고 계산했다면, 이제는 Python 코드로 자동화하여 몇 초 만에 분석을 완료할 수 있습니다.

DataFrame의 핵심 특징은 첫째, 다양한 데이터 타입을 한 번에 다룰 수 있다는 점입니다. 숫자, 문자열, 날짜 등을 각 컬럼별로 다른 타입으로 저장할 수 있죠.

둘째, 인덱싱과 슬라이싱이 매우 직관적입니다. 원하는 행이나 열을 쉽게 선택할 수 있습니다.

셋째, 누락된 데이터(NaN)를 자동으로 처리합니다. 이러한 특징들이 데이터 분석 작업을 훨씬 효율적으로 만들어줍니다.

코드 예제

import pandas as pd
import numpy as np

# 딕셔너리로 DataFrame 생성
data = {
    'name': ['김민수', '이영희', '박철수', '정수진'],
    'age': [25, 30, 35, 28],
    'city': ['서울', '부산', '대구', '인천'],
    'salary': [3000, 4500, 5200, 3800]
}
df = pd.DataFrame(data)
print(df)

# CSV 파일 읽기 (실무에서 가장 많이 사용)
# df = pd.read_csv('sales_data.csv', encoding='utf-8')

설명

이것이 하는 일: 위 코드는 Python 딕셔너리에서 DataFrame을 생성하고, 실무에서 자주 사용하는 CSV 파일 읽기 방법을 보여줍니다. 첫 번째로, pandas 라이브러리를 import합니다.

pandas는 보통 pd라는 별칭으로 사용하는 것이 관례입니다. 이렇게 하면 코드가 짧아지고 다른 개발자들도 쉽게 이해할 수 있습니다.

두 번째로, 딕셔너리 형태로 데이터를 준비합니다. 각 키는 컬럼명이 되고, 값은 해당 컬럼의 데이터 리스트가 됩니다.

이 예제에서는 직원 정보(이름, 나이, 도시, 급여)를 담고 있습니다. pd.DataFrame() 생성자에 이 딕셔너리를 넘기면 자동으로 DataFrame이 생성됩니다.

세 번째로, 실무에서는 주석 처리된 read_csv() 함수를 가장 많이 사용합니다. CSV 파일 경로를 지정하고 encoding을 'utf-8'로 설정하면 한글이 깨지지 않고 정상적으로 읽힙니다.

이 한 줄의 코드로 수만 개의 행을 가진 파일도 몇 초 안에 메모리로 불러올 수 있습니다. 여러분이 이 코드를 사용하면 엑셀을 열지 않고도 데이터를 빠르게 Python 환경으로 가져와서 분석할 수 있습니다.

파일이 아무리 커도 코드는 똑같습니다. 또한 이 작업을 스크립트로 저장해두면 매일 반복되는 데이터 로딩 작업을 자동화할 수 있어, 시간을 크게 절약할 수 있습니다.

실전 팁

💡 CSV 파일을 읽을 때 한글이 깨진다면 encoding='cp949' 또는 encoding='euc-kr'을 시도해보세요. Windows에서 생성된 파일은 종종 이런 인코딩을 사용합니다.

💡 대용량 파일(수백만 행)을 읽을 때는 read_csv()에 chunksize 매개변수를 사용하여 조금씩 나눠 읽으면 메모리 부담을 줄일 수 있습니다.

💡 실수로 첫 번째 행을 컬럼명으로 인식하지 않는 경우 header=0 옵션을 명시적으로 지정하세요. 반대로 컬럼명이 없는 파일은 header=None으로 설정합니다.

💡 read csv()로 파일을 읽은 후 바로 df.head()를 실행하여 데이터가 제대로 읽혔는지 확인하는 습관을 들이세요. 이렇게 하면 오류를 조기에 발견할 수 있습니다.


2. 데이터 탐색과 기본 정보 확인

시작하며

여러분이 처음 보는 데이터셋을 분석해야 할 때 이런 고민을 해본 적 있나요? 이 데이터에는 어떤 컬럼들이 있는지, 각 컬럼의 데이터 타입은 무엇인지, 결측치는 얼마나 있는지 파악하기 어려운 상황 말이죠.

이런 문제는 새로운 프로젝트를 시작할 때마다 반복됩니다. 데이터의 구조를 제대로 이해하지 못하면 잘못된 분석으로 이어지고, 결국 신뢰할 수 없는 결과를 얻게 됩니다.

특히 실무에서는 다른 팀이나 외부에서 받은 데이터를 분석하는 경우가 많아서 이 단계가 매우 중요합니다. 바로 이럴 때 필요한 것이 Pandas의 데이터 탐색 함수들입니다.

몇 가지 간단한 함수만으로 데이터의 전체적인 모습을 빠르게 파악할 수 있습니다.

개요

간단히 말해서, 데이터 탐색은 본격적인 분석을 시작하기 전에 데이터의 구조와 특성을 파악하는 과정입니다. 의사가 환자를 진료하기 전에 기본 검사를 하는 것과 비슷합니다.

데이터 탐색이 필요한 이유는 잘못된 가정으로 분석을 시작하면 시간을 낭비하게 되기 때문입니다. 예를 들어, 숫자여야 할 컬럼이 문자열로 저장되어 있다면 수치 계산이 불가능하고, 결측치가 많은 컬럼을 그대로 사용하면 분석 결과가 왜곡됩니다.

예를 들어, 고객 나이 데이터를 분석하려는데 일부가 문자열로 저장되어 있다면 평균 나이를 계산할 수 없습니다. 기존에는 엑셀에서 스크롤을 내리며 눈으로 확인하거나, 하나하나 컬럼을 클릭해서 타입을 확인했다면, 이제는 Pandas 함수 몇 개로 모든 정보를 한눈에 볼 수 있습니다.

핵심 탐색 함수로는 첫째, head()와 tail()로 데이터의 처음과 끝을 미리보기 할 수 있습니다. 둘째, info()로 컬럼 타입과 결측치 개수를 확인합니다.

셋째, describe()로 숫자 컬럼의 통계 요약을 볼 수 있습니다. 이러한 함수들이 데이터 분석의 방향을 결정하는 데 필수적입니다.

코드 예제

import pandas as pd

# 샘플 데이터 생성
df = pd.read_csv('sales_data.csv')  # 실제 파일 경로

# 처음 5개 행 확인 (기본값)
print(df.head())

# 마지막 3개 행 확인
print(df.tail(3))

# 데이터 구조와 타입 확인 (가장 중요!)
print(df.info())

# 숫자 컬럼의 통계 요약
print(df.describe())

# 컬럼명과 데이터 타입만 빠르게 확인
print(df.dtypes)

설명

이것이 하는 일: 위 코드는 새로운 데이터셋을 처음 만났을 때 반드시 실행해야 하는 탐색 명령어들을 보여줍니다. 이 함수들을 순서대로 실행하면 데이터의 전체 모습을 파악할 수 있습니다.

첫 번째로, head() 함수는 DataFrame의 처음 5개 행을 출력합니다. 괄호 안에 숫자를 넣으면 그만큼의 행을 볼 수 있습니다.

이를 통해 각 컬럼에 어떤 형태의 데이터가 들어있는지 실제 예시를 확인할 수 있습니다. tail()은 반대로 마지막 행들을 보여주어, 데이터가 일관되게 입력되었는지 확인할 수 있습니다.

두 번째로, info() 함수는 실무에서 가장 자주 사용하는 함수입니다. 전체 행 개수, 각 컬럼의 데이터 타입(int64, float64, object 등), 그리고 null이 아닌 값의 개수를 한 번에 보여줍니다.

만약 어떤 컬럼의 non-null 개수가 전체 행 개수보다 적다면, 그 컬럼에 결측치가 있다는 의미입니다. 세 번째로, describe() 함수는 숫자 컬럼에 대해 개수, 평균, 표준편차, 최솟값, 4분위수, 최댓값을 자동으로 계산해줍니다.

이를 통해 데이터의 분포와 이상치를 빠르게 감지할 수 있습니다. 예를 들어, 나이 컬럼의 최댓값이 200이라면 명백한 오류 데이터입니다.

네 번째로, dtypes 속성은 각 컬럼의 데이터 타입만 간단히 보여줍니다. object 타입은 문자열을 의미하고, int64는 정수, float64는 실수입니다.

만약 숫자여야 할 컬럼이 object로 표시된다면, 데이터에 문자가 섞여있다는 신호입니다. 여러분이 이 코드를 사용하면 데이터 분석을 시작하기 전에 잠재적인 문제들을 미리 발견할 수 있습니다.

결측치가 많은 컬럼은 제거하거나 보완해야 하고, 타입이 잘못된 컬럼은 변환해야 합니다. 이렇게 데이터를 먼저 이해하면 분석 과정에서 발생할 수 있는 오류를 예방하고, 정확한 인사이트를 얻을 수 있습니다.

실전 팁

💡 새로운 데이터셋을 받으면 항상 head(), info(), describe()를 순서대로 실행하는 것을 루틴으로 만드세요. 이 세 함수만으로도 대부분의 기본 정보를 파악할 수 있습니다.

💡 describe()에 include='all' 옵션을 추가하면 문자열 컬럼에 대한 통계(고유값 개수, 최빈값 등)도 볼 수 있어 더욱 유용합니다.

💡 df.shape 속성으로 (행 개수, 열 개수)를 튜플로 확인할 수 있습니다. 예상한 행 개수와 다르다면 데이터 로딩 과정에서 문제가 있었을 가능성이 높습니다.

💡 df.columns로 모든 컬럼명을 리스트로 확인하세요. 컬럼명에 공백이나 특수문자가 있으면 나중에 접근할 때 불편하므로 rename()으로 정리하는 것이 좋습니다.


3. 결측치 처리와 데이터 정제

시작하며

여러분이 데이터 분석을 하다가 이런 에러 메시지를 본 적 있나요? "NaN values detected" 또는 계산 결과가 NaN으로 나오는 상황 말이죠.

실제 데이터에는 항상 비어있는 값, 즉 결측치가 존재합니다. 이런 문제는 실무 데이터에서 피할 수 없습니다.

설문조사에서 응답자가 일부 질문을 건너뛰거나, 센서 데이터에서 측정 오류가 발생하거나, 데이터 통합 과정에서 매칭되지 않는 경우가 생깁니다. 결측치를 제대로 처리하지 않으면 통계 계산이 불가능하거나, 머신러닝 모델이 학습을 거부합니다.

바로 이럴 때 필요한 것이 Pandas의 결측치 처리 함수들입니다. 결측치를 찾아내고, 제거하거나, 적절한 값으로 채우는 방법을 알아야 합니다.

개요

간단히 말해서, 결측치는 데이터에서 값이 없는 상태를 의미하며, Pandas에서는 NaN(Not a Number)으로 표시됩니다. 데이터 정제는 이러한 결측치와 이상치를 처리하여 분석 가능한 상태로 만드는 과정입니다.

결측치 처리가 필요한 이유는 대부분의 분석 함수와 머신러닝 알고리즘이 결측치를 처리하지 못하기 때문입니다. 평균을 계산하거나 회귀 분석을 할 때 NaN이 하나라도 있으면 결과가 왜곡되거나 에러가 발생합니다.

예를 들어, 고객의 구매 금액 평균을 계산하는데 일부 고객의 데이터가 누락되어 있다면, 그대로 계산하면 실제보다 낮은 평균이 나올 수 있습니다. 기존에는 엑셀에서 필터를 걸어 빈 셀을 찾고 수동으로 삭제하거나 값을 입력했다면, 이제는 Pandas 함수로 자동으로 결측치를 탐지하고 일괄 처리할 수 있습니다.

핵심 처리 방법으로는 첫째, isnull()과 notnull()로 결측치를 확인합니다. 둘째, dropna()로 결측치가 있는 행이나 열을 제거합니다.

셋째, fillna()로 결측치를 특정 값(평균, 중앙값, 0 등)으로 채웁니다. 이러한 방법들을 상황에 맞게 선택하는 것이 데이터 분석가의 중요한 판단입니다.

코드 예제

import pandas as pd
import numpy as np

# 결측치가 포함된 샘플 데이터
df = pd.DataFrame({
    'name': ['김민수', '이영희', None, '정수진'],
    'age': [25, np.nan, 35, 28],
    'salary': [3000, 4500, np.nan, 3800]
})

# 결측치 확인: True/False DataFrame
print(df.isnull())

# 각 컬럼별 결측치 개수 확인 (중요!)
print(df.isnull().sum())

# 결측치가 있는 행 전체 제거
df_dropped = df.dropna()

# 특정 컬럼의 결측치만 제거
df_dropped_subset = df.dropna(subset=['age'])

# 결측치를 특정 값으로 채우기
df_filled = df.fillna(0)  # 모두 0으로

# 컬럼별로 다른 값으로 채우기 (평균, 중앙값 등)
df['age'].fillna(df['age'].mean(), inplace=True)

설명

이것이 하는 일: 위 코드는 실무에서 가장 많이 사용하는 결측치 처리 패턴을 보여줍니다. 결측치를 발견하고, 제거하거나, 적절한 값으로 대체하는 전체 과정을 다룹니다.

첫 번째로, isnull() 함수는 DataFrame의 각 셀에 대해 결측치 여부를 True/False로 반환합니다. 하지만 이것만으로는 전체 상황을 파악하기 어렵기 때문에, sum()을 체인으로 연결하여 각 컬럼별 결측치 개수를 집계합니다.

이렇게 하면 어느 컬럼에 결측치가 많은지 한눈에 볼 수 있습니다. 두 번째로, dropna() 함수는 결측치가 포함된 행을 제거합니다.

기본적으로는 하나의 컬럼이라도 NaN이 있으면 해당 행 전체를 삭제합니다. 하지만 subset 매개변수를 사용하면 특정 컬럼의 결측치만 고려하여 선택적으로 삭제할 수 있습니다.

예를 들어, 나이는 필수지만 급여는 선택적인 경우 subset=['age']로 지정합니다. 세 번째로, fillna() 함수는 결측치를 특정 값으로 대체합니다.

가장 간단한 방법은 0이나 빈 문자열로 채우는 것이지만, 더 정교한 방법은 해당 컬럼의 평균(mean()), 중앙값(median()), 또는 최빈값(mode())을 사용하는 것입니다. inplace=True 옵션을 사용하면 원본 DataFrame을 직접 수정하여 메모리를 절약할 수 있습니다.

네 번째로, 실무에서는 컬럼의 특성에 따라 다른 전략을 사용합니다. 숫자 컬럼은 평균이나 중앙값으로, 범주형 컬럼은 최빈값으로 채우는 것이 일반적입니다.

예를 들어, 나이는 평균으로, 도시는 '미상'으로 채울 수 있습니다. 여러분이 이 코드를 사용하면 결측치로 인한 분석 오류를 방지할 수 있습니다.

데이터의 10% 미만이 결측치라면 해당 행을 제거하는 것이 안전하지만, 결측치가 많다면 삭제보다는 대체 방법을 고려해야 합니다. 또한 결측치가 무작위가 아니라 특정 패턴을 가진다면(예: 고소득자만 급여를 밝히지 않음), 이 자체가 중요한 분석 인사이트가 될 수 있습니다.

실전 팁

💡 dropna()를 사용하기 전에 얼마나 많은 데이터가 손실되는지 확인하세요. df.shape를 전후로 비교하면 삭제된 행 개수를 알 수 있습니다. 너무 많은 데이터가 손실된다면 fillna()를 고려하세요.

💡 숫자 컬럼의 결측치를 평균으로 채울 때, 이상치(outlier)가 있다면 평균 대신 중앙값(median)을 사용하는 것이 더 안전합니다. 중앙값은 극단값의 영향을 덜 받습니다.

💡 fillna()의 method='ffill' 옵션은 바로 위의 값으로, 'bfill'은 바로 아래의 값으로 채웁니다. 시계열 데이터에서 특히 유용합니다.

💡 결측치를 채우기 전에 왜 결측치가 발생했는지 원인을 파악하세요. 시스템 오류인지, 측정 불가능한 상황인지에 따라 처리 방법이 달라져야 합니다.


4. 데이터 필터링과 조건 검색

시작하며

여러분이 수천 명의 고객 데이터에서 특정 조건에 맞는 고객만 찾아야 할 때 이런 어려움을 겪어본 적 있나요? 예를 들어, "30세 이상이면서 서울에 거주하고 구매 금액이 100만 원 이상인 고객"을 찾는 것처럼 복잡한 조건을 만족하는 데이터만 추출해야 하는 상황 말이죠.

이런 문제는 데이터 분석의 핵심입니다. 전체 데이터에서 필요한 부분만 추출하지 못하면 불필요한 정보가 분석을 방해하고, 원하는 인사이트를 얻기 어렵습니다.

엑셀의 필터 기능으로는 복잡한 조건을 처리하기 어렵고, 매번 수동으로 설정해야 하는 번거로움이 있습니다. 바로 이럴 때 필요한 것이 Pandas의 불린 인덱싱과 조건 필터링입니다.

SQL의 WHERE 절처럼 강력하면서도, Python의 직관적인 문법으로 복잡한 조건을 쉽게 표현할 수 있습니다.

개요

간단히 말해서, 데이터 필터링은 특정 조건을 만족하는 행들만 선택하는 작업입니다. 불린 인덱싱은 True/False 배열을 사용하여 원하는 데이터를 추출하는 Pandas의 핵심 기법입니다.

필터링이 필요한 이유는 실무에서 항상 전체 데이터가 아닌 특정 부분집합을 분석하기 때문입니다. 마케팅 팀은 VIP 고객만, 재무 팀은 특정 기간의 거래만, 품질 팀은 불량이 발생한 제품만 분석합니다.

예를 들어, 월별 매출 분석을 할 때 해당 월의 데이터만 필터링해야 정확한 결과를 얻을 수 있습니다. 기존에는 엑셀에서 자동 필터를 설정하고 체크박스를 클릭하거나, 고급 필터를 사용해야 했다면, 이제는 df[조건] 형태의 간단한 코드로 모든 필터링을 처리할 수 있습니다.

핵심 필터링 방법으로는 첫째, 단일 조건 필터링(df[df['age'] > 30])입니다. 둘째, 여러 조건을 AND(&) 또는 OR(|)로 결합할 수 있습니다.

셋째, isin()으로 여러 값 중 하나와 일치하는지 확인합니다. 넷째, str.contains()로 문자열 패턴을 검색합니다.

이러한 방법들을 조합하면 거의 모든 조건을 표현할 수 있습니다.

코드 예제

import pandas as pd

# 샘플 데이터
df = pd.DataFrame({
    'name': ['김민수', '이영희', '박철수', '정수진', '최지훈'],
    'age': [25, 30, 35, 28, 42],
    'city': ['서울', '부산', '서울', '인천', '서울'],
    'salary': [3000, 4500, 5200, 3800, 6000]
})

# 단일 조건: 나이가 30 이상
filtered_age = df[df['age'] >= 30]

# 복수 조건 (AND): 나이 30 이상 AND 서울 거주
filtered_multi = df[(df['age'] >= 30) & (df['city'] == '서울')]

# 복수 조건 (OR): 서울 또는 부산 거주
filtered_or = df[(df['city'] == '서울') | (df['city'] == '부산')]

# isin()으로 여러 값 중 하나: 서울, 부산, 대구 중 하나
filtered_isin = df[df['city'].isin(['서울', '부산', '대구'])]

# 문자열 포함 여부: 이름에 '수' 포함
filtered_contains = df[df['name'].str.contains('수')]

# query() 메서드로 더 읽기 쉬운 코드
filtered_query = df.query('age >= 30 and city == "서울"')

설명

이것이 하는 일: 위 코드는 다양한 조건으로 데이터를 필터링하는 실전 패턴을 보여줍니다. 단순한 조건부터 복잡한 복합 조건까지 모두 다룹니다.

첫 번째로, 단일 조건 필터링의 원리를 이해해야 합니다. df['age'] >= 30은 각 행에 대해 True/False를 반환하는 불린 Series를 생성합니다.

이 불린 Series를 df[]의 인덱스로 사용하면, True인 행만 선택됩니다. 이것이 불린 인덱싱의 핵심 원리입니다.

두 번째로, 여러 조건을 결합할 때는 반드시 각 조건을 괄호로 묶어야 합니다. Python의 연산자 우선순위 때문에 괄호 없이 쓰면 예상치 못한 결과나 에러가 발생합니다.

&는 AND 조건(모든 조건 만족), |는 OR 조건(하나 이상 만족)을 의미합니다. ~(틸드)는 NOT 조건으로 사용할 수 있습니다.

세 번째로, isin() 메서드는 여러 값 중 하나와 일치하는지 확인할 때 매우 유용합니다. (df['city'] == '서울') | (df['city'] == '부산')처럼 길게 쓰는 대신, df['city'].isin(['서울', '부산'])으로 간결하게 표현할 수 있습니다.

리스트에 10개, 20개의 값이 있어도 똑같이 작동합니다. 네 번째로, 문자열 검색을 위한 str.contains()는 부분 일치를 찾습니다.

정규표현식도 지원하므로 복잡한 패턴 매칭이 가능합니다. 예를 들어, 이메일 형식 검증이나 전화번호 패턴 찾기에 활용할 수 있습니다.

다섯 번째로, query() 메서드는 SQL처럼 문자열로 조건을 작성할 수 있어 가독성이 좋습니다. 특히 조건이 복잡할 때 코드가 훨씬 깔끔해집니다.

하지만 모든 상황에서 사용할 수 있는 것은 아니므로, 불린 인덱싱을 기본으로 알고 있어야 합니다. 여러분이 이 코드를 사용하면 대용량 데이터에서도 원하는 부분집합을 순식간에 추출할 수 있습니다.

필터링된 결과를 새로운 변수에 저장하면 원본 데이터는 그대로 유지되므로 안전합니다. 또한 필터링된 데이터에 대해 추가 분석(평균, 합계 등)을 바로 수행할 수 있어 워크플로우가 매우 효율적입니다.

실전 팁

💡 복잡한 조건을 작성할 때는 각 조건을 먼저 별도의 변수로 만들어서 테스트한 후 결합하세요. 예: condition1 = df['age'] >= 30, condition2 = df['city'] == '서울', result = df[condition1 & condition2]

💡 Not 조건을 사용할 때는 ~(틸드)를 사용합니다. 예: df[~df['city'].isin(['서울'])]은 서울이 아닌 모든 도시를 선택합니다.

💡 필터링 후 인덱스가 불연속적으로 남는 것이 불편하다면 reset_index(drop=True)를 사용하여 인덱스를 0부터 다시 매기세요.

💡 str.contains()는 기본적으로 대소문자를 구분합니다. case=False 옵션을 추가하면 대소문자 구분 없이 검색할 수 있습니다.


5. 그룹화와 집계 분석

시작하며

여러분이 전국 매장의 매출 데이터를 분석하면서 이런 질문을 받은 적 있나요? "지역별 평균 매출은 얼마인가요?", "제품 카테고리별 판매 수량의 합계는?", "연령대별 평균 구매 금액은?" 이런 질문들의 공통점은 바로 그룹별로 통계를 내야 한다는 것입니다.

이런 분석은 비즈니스 의사결정의 핵심입니다. 엑셀의 피벗 테이블을 사용해본 적이 있다면, 그것이 바로 그룹화와 집계입니다.

하지만 엑셀로는 복잡한 조건을 다루거나, 여러 단계의 그룹화를 하기 어렵고, 자동화도 불가능합니다. 바로 이럴 때 필요한 것이 Pandas의 groupby() 함수입니다.

SQL의 GROUP BY와 같은 강력한 기능을 제공하며, 피벗 테이블보다 훨씬 유연하고 빠릅니다.

개요

간단히 말해서, 그룹화는 데이터를 특정 기준(컬럼)에 따라 나누고, 각 그룹에 대해 집계 함수(평균, 합계, 개수 등)를 적용하는 것입니다. "나누고 적용하고 결합하기(Split-Apply-Combine)" 패턴이라고도 합니다.

그룹화가 필요한 이유는 전체 데이터의 평균이나 합계만으로는 부족하기 때문입니다. 실무에서는 항상 "어떤 세그먼트에서 성과가 좋은가?", "어느 그룹이 문제인가?"를 파악해야 합니다.

예를 들어, 전체 매출의 평균은 의미가 없을 수 있지만, 지역별 매출 평균을 비교하면 어느 지역에 투자해야 할지 결정할 수 있습니다. 기존에는 엑셀에서 피벗 테이블을 만들거나, 필터를 일일이 바꿔가며 수동으로 계산했다면, 이제는 groupby() 한 줄로 모든 그룹의 통계를 자동으로 계산할 수 있습니다.

핵심 그룹화 패턴으로는 첫째, 단일 컬럼 기준 그룹화(df.groupby('city'))입니다. 둘째, 여러 컬럼 기준 그룹화(df.groupby(['city', 'age_group']))로 더 세밀한 분석을 합니다.

셋째, 집계 함수로 mean(), sum(), count(), max(), min() 등을 사용합니다. 넷째, agg()로 여러 집계를 동시에 적용할 수 있습니다.

이러한 조합으로 복잡한 비즈니스 질문에 답할 수 있습니다.

코드 예제

import pandas as pd

# 샘플 데이터: 여러 지역의 직원 정보
df = pd.DataFrame({
    'name': ['김민수', '이영희', '박철수', '정수진', '최지훈', '강민지'],
    'city': ['서울', '부산', '서울', '인천', '서울', '부산'],
    'department': ['영업', '개발', '영업', '개발', '개발', '영업'],
    'salary': [3000, 4500, 3500, 3800, 5200, 3200]
})

# 도시별 평균 급여 (가장 기본적인 패턴)
city_avg = df.groupby('city')['salary'].mean()

# 도시별 모든 수치 컬럼의 통계
city_all = df.groupby('city').mean()

# 도시와 부서별 평균 급여 (다중 그룹화)
multi_group = df.groupby(['city', 'department'])['salary'].mean()

# 여러 집계 함수 동시 적용
agg_result = df.groupby('city')['salary'].agg(['mean', 'sum', 'count', 'max'])

# 컬럼별로 다른 집계 함수 적용
custom_agg = df.groupby('city').agg({
    'salary': ['mean', 'max'],
    'name': 'count'  # 각 도시의 직원 수
})

설명

이것이 하는 일: 위 코드는 실무에서 가장 자주 사용하는 그룹 분석 패턴을 보여줍니다. 단순한 평균부터 복잡한 다중 집계까지 다룹니다.

첫 번째로, 기본 그룹화의 구조를 이해해야 합니다. df.groupby('city')는 'city' 컬럼의 고유값(서울, 부산, 인천)별로 데이터를 나눕니다.

그 다음 ['salary']로 분석할 컬럼을 선택하고, .mean()으로 각 그룹의 평균을 계산합니다. 결과는 도시명이 인덱스이고 평균 급여가 값인 Series가 됩니다.

두 번째로, 특정 컬럼을 선택하지 않고 groupby().mean()을 하면, 모든 숫자 컬럼에 대해 평균을 계산합니다. 이것은 빠른 탐색에는 유용하지만, 의미 없는 컬럼(예: ID 컬럼)의 평균도 계산되므로 주의해야 합니다.

세 번째로, 다중 그룹화는 대괄호 안에 리스트로 여러 컬럼을 지정합니다. df.groupby(['city', 'department'])는 먼저 도시별로 나누고, 그 안에서 다시 부서별로 나눕니다.

결과는 MultiIndex(다중 인덱스)를 가진 Series가 되어, 계층적 구조를 표현합니다. 예를 들어, (서울, 영업), (서울, 개발), (부산, 영업)처럼 그룹이 생성됩니다.

네 번째로, agg() 메서드는 여러 집계 함수를 한 번에 적용합니다. 함수 이름을 문자열 리스트로 전달하면, 각 함수의 결과가 컬럼으로 추가된 DataFrame이 반환됩니다.

이렇게 하면 한 번의 계산으로 평균, 합계, 개수, 최댓값을 모두 볼 수 있어 매우 효율적입니다. 다섯 번째로, 딕셔너리를 사용한 맞춤형 집계는 가장 강력한 패턴입니다.

각 컬럼에 대해 서로 다른 집계 함수를 지정할 수 있습니다. 예를 들어, 급여는 평균과 최댓값을, 이름은 개수(직원 수)를 계산할 수 있습니다.

이것은 복잡한 비즈니스 리포트를 자동으로 생성할 때 매우 유용합니다. 여러분이 이 코드를 사용하면 수백만 개의 거래 데이터에서도 순식간에 그룹별 통계를 얻을 수 있습니다.

그룹화 결과를 reset_index()로 DataFrame으로 변환하면 추가 분석이나 시각화에 바로 사용할 수 있습니다. 또한 이 패턴을 이해하면 SQL을 몰라도 데이터베이스 수준의 분석을 Python에서 수행할 수 있습니다.

실전 팁

💡 groupby() 결과는 GroupBy 객체입니다. 실제 계산은 집계 함수(mean, sum 등)를 호출할 때 수행되므로, 큰 데이터에서는 필요한 집계만 선택적으로 수행하세요.

💡 그룹별 결과를 DataFrame으로 만들려면 끝에 .reset_index()를 추가하세요. 이렇게 하면 그룹 컬럼이 인덱스가 아닌 일반 컬럼이 되어 다루기 쉽습니다.

💡 size()는 각 그룹의 행 개수를 반환하고, count()는 각 컬럼의 non-null 개수를 반환합니다. 결측치가 있을 때 결과가 다르므로 목적에 맞게 선택하세요.

💡 groupby()에 as index=False 옵션을 주면 자동으로 reset index()가 적용된 것처럼 동작하여 코드가 간결해집니다.


6. 데이터 정렬과 순위 매기기

시작하며

여러분이 판매 실적 데이터를 분석하면서 이런 요청을 받은 적 있나요? "매출이 높은 순서대로 정렬해주세요", "각 지역별로 상위 3명의 영업사원을 찾아주세요", "제품을 가격 순위로 나타내주세요" 같은 요구사항 말이죠.

이런 작업은 데이터 분석에서 매우 빈번합니다. 순위를 매기고 정렬하는 것은 최고 성과자를 찾거나, 문제가 있는 하위 그룹을 파악하거나, 데이터를 보기 좋게 표현하는 데 필수적입니다.

엑셀에서는 정렬 버튼을 클릭하는 것이 간단하지만, 여러 기준으로 정렬하거나 그룹 내 순위를 매기는 것은 복잡합니다. 바로 이럴 때 필요한 것이 Pandas의 sort_values()와 rank() 함수입니다.

단순한 정렬부터 복잡한 다중 정렬, 그룹별 순위 매기기까지 모두 가능합니다.

개요

간단히 말해서, 정렬은 특정 컬럼의 값을 기준으로 행의 순서를 재배치하는 것이고, 순위 매기기는 각 값이 전체 또는 그룹 내에서 몇 번째인지 숫자로 표시하는 것입니다. 정렬과 순위가 필요한 이유는 데이터의 패턴을 쉽게 파악하고, 상위/하위 항목을 빠르게 찾기 위해서입니다.

예를 들어, 매출을 내림차순으로 정렬하면 최고 성과 제품을 바로 확인할 수 있고, 순위를 매기면 "상위 10%", "하위 20%" 같은 기준으로 그룹을 나눌 수 있습니다. 이는 보너스 지급, 재고 관리, 마케팅 타겟팅 등 실무 의사결정에 직접 활용됩니다.

기존에는 엑셀에서 정렬 메뉴를 클릭하거나 RANK 함수를 일일이 입력했다면, 이제는 Pandas 함수 한 줄로 복잡한 정렬과 순위를 자동으로 처리할 수 있습니다. 핵심 정렬 방법으로는 첫째, sort_values()로 하나 또는 여러 컬럼 기준 정렬을 합니다.

둘째, ascending 매개변수로 오름차순/내림차순을 제어합니다. 셋째, rank()로 순위를 매기며, method 옵션으로 동점 처리 방식을 선택합니다.

넷째, nlargest()와 nsmallest()로 상위/하위 N개만 빠르게 추출합니다. 이러한 도구들이 데이터의 우선순위를 명확히 보여줍니다.

코드 예제

import pandas as pd

# 샘플 데이터: 직원 성과 데이터
df = pd.DataFrame({
    'name': ['김민수', '이영희', '박철수', '정수진', '최지훈', '강민지'],
    'department': ['영업', '영업', '개발', '개발', '영업', '개발'],
    'sales': [500, 800, 600, 750, 650, 700]
})

# 매출 기준 오름차순 정렬 (기본값)
sorted_asc = df.sort_values('sales')

# 매출 기준 내림차순 정렬 (가장 많이 사용)
sorted_desc = df.sort_values('sales', ascending=False)

# 여러 컬럼 기준 정렬: 부서별로 먼저, 그 안에서 매출 내림차순
sorted_multi = df.sort_values(['department', 'sales'], ascending=[True, False])

# 전체 순위 매기기 (1등부터)
df['rank'] = df['sales'].rank(ascending=False, method='min')

# 그룹별 순위 매기기: 부서 내 순위
df['dept_rank'] = df.groupby('department')['sales'].rank(ascending=False, method='min')

# 상위 3명만 빠르게 추출 (정렬보다 빠름)
top3 = df.nlargest(3, 'sales')

# 하위 2명만 추출
bottom2 = df.nsmallest(2, 'sales')

설명

이것이 하는 일: 위 코드는 실무에서 자주 사용하는 정렬과 순위 매기기 패턴을 보여줍니다. 단순 정렬부터 그룹 내 순위까지 다룹니다.

첫 번째로, sort_values()는 지정한 컬럼의 값을 기준으로 전체 행을 재배치합니다. ascending=False를 지정하면 큰 값부터(내림차순), True나 생략하면 작은 값부터(오름차순) 정렬됩니다.

정렬된 결과는 새로운 DataFrame이며, 원본은 변경되지 않습니다. inplace=True를 추가하면 원본을 직접 수정할 수 있습니다.

두 번째로, 다중 컬럼 정렬은 리스트로 컬럼명을 나열합니다. 첫 번째 컬럼으로 먼저 정렬하고, 같은 값이 있으면 두 번째 컬럼으로 정렬합니다.

ascending도 리스트로 지정하여 컬럼마다 다른 방향으로 정렬할 수 있습니다. 예제에서는 부서는 오름차순(가나다순), 매출은 내림차순(높은 순)으로 정렬합니다.

세 번째로, rank() 함수는 각 값의 순위를 반환합니다. ascending=False는 큰 값이 1등이 되도록 합니다.

method='min'은 동점자가 있을 때 처리 방식인데, 'min'은 동점자 모두에게 가장 높은 순위를 줍니다(예: 두 명이 공동 2등이면 둘 다 2등, 다음은 4등). 다른 옵션으로 'average'(평균 순위), 'dense'(순위 건너뛰지 않음) 등이 있습니다.

네 번째로, 그룹별 순위는 매우 강력한 기능입니다. groupby()와 rank()를 결합하여 각 그룹 내에서 독립적으로 순위를 매깁니다.

예제에서는 영업 부서 내에서 1, 2, 3등, 개발 부서 내에서 1, 2, 3등이 따로 매겨집니다. 이것은 "부서별 상위 10%"를 찾는 것처럼 공정한 비교가 필요할 때 필수적입니다.

다섯 번째로, nlargest()와 nsmallest()는 정렬하지 않고 바로 상위/하위 N개를 추출합니다. 전체 데이터를 정렬하지 않기 때문에 대용량 데이터에서 성능이 더 좋습니다.

예를 들어, 백만 개의 제품 중 상위 10개만 필요하다면 sort_values()보다 nlargest()가 훨씬 빠릅니다. 여러분이 이 코드를 사용하면 판매 실적 보고서, 학생 성적 순위, 제품 인기도 랭킹 등을 자동으로 생성할 수 있습니다.

정렬된 데이터는 보고서나 대시보드에 바로 활용할 수 있고, 순위 정보는 추가 분석(상위 20% vs 하위 20% 비교 등)의 기준이 됩니다. 또한 시각화할 때도 정렬된 데이터를 사용하면 차트가 훨씬 읽기 쉬워집니다.

실전 팁

💡 정렬 후 인덱스가 뒤섞여서 불편하다면 .reset_index(drop=True)를 사용하여 인덱스를 0부터 다시 매기세요. 특히 시각화할 때 유용합니다.

💡 동점 처리 방식(method)은 상황에 맞게 선택하세요. 'min'은 모두에게 유리하게(높은 순위), 'max'는 보수적으로(낮은 순위), 'average'는 중간값을 줍니다.

💡 백분위 순위가 필요하다면 rank(pct=True)를 사용하세요. 0~1 사이의 값으로 순위를 표현하여 "상위 10%"를 찾기 쉬워집니다.

💡 정렬할 때 결측치(NaN)는 기본적으로 끝에 배치됩니다. na position='first' 옵션으로 맨 앞에 놓을 수도 있습니다.


7. 새로운 컬럼 생성과 데이터 변환

시작하며

여러분이 데이터를 분석하다가 이런 필요를 느낀 적 있나요? 기존 컬럼들을 조합해서 새로운 정보를 만들어야 하는 상황 말이죠.

예를 들어, 구매 금액과 수량이 있는데 단가를 계산해야 하거나, 생년월일에서 나이를 계산하거나, 여러 조건에 따라 등급을 분류해야 하는 경우입니다. 이런 작업은 데이터 분석의 핵심 과정입니다.

원본 데이터에는 없지만 분석에 필요한 파생 변수(derived variable)를 만드는 것은 데이터 과학자의 중요한 능력입니다. 엑셀에서는 수식을 한 셀에 입력하고 드래그하여 복사하지만, 데이터가 크면 느리고 오류가 발생하기 쉽습니다.

바로 이럴 때 필요한 것이 Pandas의 벡터 연산과 apply() 함수입니다. 수천, 수만 개의 행에 대해 동시에 계산을 수행하여 순식간에 새로운 컬럼을 생성할 수 있습니다.

개요

간단히 말해서, 컬럼 생성은 기존 데이터를 가공하여 새로운 정보를 담은 컬럼을 DataFrame에 추가하는 작업입니다. 데이터 변환은 값의 형태나 타입을 바꾸는 것을 포함합니다.

컬럼 생성이 필요한 이유는 원본 데이터만으로는 인사이트를 얻기 어렵기 때문입니다. 예를 들어, 매출 데이터에 '총 매출 금액'과 '할인 금액'이 있다면, '순 매출 금액'을 계산해야 실제 수익을 알 수 있습니다.

또는 고객의 구매 빈도와 금액으로 'VIP 여부'를 판단하는 컬럼을 만들어 타겟 마케팅에 활용할 수 있습니다. 기존에는 엑셀 수식을 셀마다 복사하거나, VBA 매크로를 작성해야 했다면, 이제는 Pandas의 벡터화 연산으로 한 줄의 코드로 모든 행에 동시에 적용할 수 있습니다.

핵심 생성 방법으로는 첫째, 산술 연산자(+, -, *, /)로 간단한 계산을 합니다. 둘째, apply()와 람다 함수로 복잡한 로직을 적용합니다.

셋째, np.where()나 pd.cut()으로 조건에 따른 분류를 합니다. 넷째, 문자열 메서드(str.)로 텍스트를 가공합니다.

이러한 도구들이 데이터를 분석 목적에 맞게 변형합니다.

코드 예제

import pandas as pd
import numpy as np

# 샘플 데이터: 제품 판매 정보
df = pd.DataFrame({
    'product': ['노트북', '마우스', '키보드', '모니터'],
    'price': [1200000, 30000, 80000, 350000],
    'quantity': [5, 20, 15, 8],
    'discount_rate': [0.1, 0.05, 0.0, 0.15]
})

# 간단한 산술 연산: 총 매출 금액
df['total_sales'] = df['price'] * df['quantity']

# 할인 적용 금액 계산
df['discount_amount'] = df['total_sales'] * df['discount_rate']
df['final_sales'] = df['total_sales'] - df['discount_amount']

# 조건에 따른 등급 분류 (numpy.where)
df['price_category'] = np.where(df['price'] >= 100000, '고가', '저가')

# 복잡한 조건 (여러 조건)
df['sales_grade'] = np.where(
    df['final_sales'] >= 5000000, 'A',
    np.where(df['final_sales'] >= 2000000, 'B', 'C')
)

# apply()로 복잡한 함수 적용
def categorize_product(row):
    if row['price'] >= 500000:
        return '프리미엄'
    elif row['quantity'] >= 15:
        return '대량판매'
    else:
        return '일반'

df['product_type'] = df.apply(categorize_product, axis=1)

# 문자열 변환: 제품명 대문자 변환
df['product_upper'] = df['product'].str.upper()

설명

이것이 하는 일: 위 코드는 실무에서 가장 많이 사용하는 파생 변수 생성 패턴을 보여줍니다. 간단한 계산부터 복잡한 조건 분류까지 다룹니다.

첫 번째로, 가장 기본적인 패턴은 산술 연산입니다. df['new_column'] = df['col1'] * df['col2']처럼 새로운 컬럼명을 지정하고 계산식을 대입하면, 모든 행에 대해 동시에(벡터화) 계산이 수행됩니다.

이것은 엑셀에서 수식을 드래그하는 것보다 수백 배 빠릅니다. 예제에서는 가격과 수량을 곱하여 총 매출을 계산합니다.

두 번째로, 여러 단계의 계산을 연속으로 수행할 수 있습니다. 먼저 total_sales를 만들고, 그것을 다시 사용하여 discount_amount와 final_sales를 계산합니다.

각 단계의 중간 결과를 컬럼으로 저장하면 나중에 검증하거나 다른 분석에 활용할 수 있습니다. 세 번째로, np.where()는 조건에 따라 다른 값을 할당하는 매우 유용한 함수입니다.

첫 번째 인자는 조건, 두 번째는 조건이 True일 때 값, 세 번째는 False일 때 값입니다. 엑셀의 IF 함수와 비슷하지만 훨씬 강력합니다.

np.where()를 중첩하여 여러 조건을 처리할 수도 있습니다(예제의 sales_grade). 네 번째로, apply() 함수는 각 행(axis=1) 또는 각 컬럼(axis=0)에 대해 사용자 정의 함수를 적용합니다.

복잡한 비즈니스 로직을 함수로 정의하고, 그것을 모든 행에 적용할 수 있습니다. 예제에서는 가격과 수량을 모두 고려하여 제품 타입을 분류합니다.

단, apply()는 반복문이므로 벡터 연산보다 느립니다. 가능하면 벡터 연산을 우선 고려하세요.

다섯 번째로, 문자열 컬럼에는 .str 접근자를 사용하여 다양한 문자열 메서드를 적용할 수 있습니다. upper(), lower(), strip(), replace(), split() 등 Python의 문자열 메서드가 모두 벡터화되어 사용됩니다.

예를 들어, 고객명을 표준화하거나, 이메일에서 도메인을 추출하는 등의 작업이 가능합니다. 여러분이 이 코드를 사용하면 원본 데이터에 없던 인사이트를 추출할 수 있습니다.

총 매출, 수익률, 고객 등급, 연령대 같은 파생 변수는 더 깊은 분석의 기초가 됩니다. 또한 머신러닝 모델을 만들 때 좋은 피처(feature)를 생성하는 것이 성능을 크게 좌우하므로, 이 기술은 필수적입니다.

실전 팁

💡 벡터 연산이 항상 apply()보다 빠릅니다. 복잡한 로직도 가능하면 np.where(), np.select(), 또는 여러 벡터 연산의 조합으로 해결하세요.

💡 pd.cut()은 연속된 숫자를 구간으로 나누는 데 최적화되어 있습니다. 예: 나이를 10대, 20대, 30대로 분류하거나, 성적을 A~F로 변환할 때 유용합니다.

💡 apply()에 람다 함수를 바로 사용하면 코드가 간결해집니다. 예: df['new'] = df.apply(lambda row: row['a'] + row['b'], axis=1)

💡 새로운 컬럼을 만들 때 원본 DataFrame을 복사(copy())하고 작업하면, 실수로 원본을 변경하는 것을 방지할 수 있습니다.


8. 데이터 병합과 조인

시작하며

여러분이 실무에서 이런 상황을 마주한 적 있나요? 고객 정보가 담긴 파일과 주문 정보가 담긴 파일이 따로 있는데, 고객별 주문 내역을 분석해야 하는 상황 말이죠.

또는 제품 테이블과 카테고리 테이블이 분리되어 있어서 합쳐야 하는 경우입니다. 이런 문제는 실무에서 매우 흔합니다.

대부분의 기업 데이터는 여러 시스템과 부서에 분산되어 있고, 각각 다른 파일이나 테이블로 관리됩니다. 이들을 연결하지 못하면 종합적인 분석이 불가능합니다.

예를 들어, CRM 시스템의 고객 정보와 POS 시스템의 구매 이력을 합쳐야 고객별 구매 패턴을 분석할 수 있습니다. 바로 이럴 때 필요한 것이 Pandas의 merge()와 join() 함수입니다.

SQL의 JOIN과 같은 강력한 기능으로, 공통 키를 기준으로 여러 DataFrame을 하나로 합칠 수 있습니다.

개요

간단히 말해서, 데이터 병합은 두 개 이상의 DataFrame을 공통 컬럼(키)을 기준으로 연결하여 하나의 DataFrame으로 만드는 작업입니다. 마치 퍼즐 조각을 맞추듯이 데이터를 결합합니다.

병합이 필요한 이유는 관련된 정보가 여러 곳에 흩어져 있기 때문입니다. 정규화된 데이터베이스에서는 중복을 피하기 위해 정보를 여러 테이블로 나누는데, 분석할 때는 이들을 다시 합쳐야 합니다.

예를 들어, 직원 테이블과 부서 테이블이 분리되어 있다면, 직원의 부서명을 보려면 두 테이블을 부서 ID로 조인해야 합니다. 기존에는 엑셀의 VLOOKUP 함수를 사용하거나 수동으로 복사-붙여넣기를 했다면, 이제는 Pandas의 merge()로 자동으로 매칭하고 결합할 수 있습니다.

VLOOKUP과 달리 양방향 매칭이 가능하고, 훨씬 빠릅니다. 핵심 병합 방법으로는 첫째, inner join(교집합)으로 양쪽에 모두 있는 데이터만 남깁니다.

둘째, left join으로 왼쪽 DataFrame의 모든 행을 유지합니다. 셋째, outer join(합집합)으로 양쪽의 모든 데이터를 포함합니다.

넷째, on 매개변수로 조인 키를 지정합니다. 이러한 조인 타입을 상황에 맞게 선택하는 것이 중요합니다.

코드 예제

import pandas as pd

# 샘플 데이터 1: 고객 정보
customers = pd.DataFrame({
    'customer_id': [1, 2, 3, 4],
    'name': ['김민수', '이영희', '박철수', '정수진'],
    'city': ['서울', '부산', '대구', '인천']
})

# 샘플 데이터 2: 주문 정보
orders = pd.DataFrame({
    'order_id': [101, 102, 103, 104, 105],
    'customer_id': [1, 2, 1, 3, 5],  # customer_id 5는 customers에 없음
    'amount': [50000, 120000, 30000, 80000, 60000]
})

# Inner join: 양쪽에 모두 있는 데이터만 (기본값)
inner_merged = pd.merge(customers, orders, on='customer_id', how='inner')

# Left join: 왼쪽(customers)의 모든 행 유지 (가장 많이 사용)
left_merged = pd.merge(customers, orders, on='customer_id', how='left')

# Right join: 오른쪽(orders)의 모든 행 유지
right_merged = pd.merge(customers, orders, on='customer_id', how='right')

# Outer join: 양쪽의 모든 데이터 포함
outer_merged = pd.merge(customers, orders, on='customer_id', how='outer')

# 컬럼명이 다를 때: left_on, right_on 사용
# left_merged = pd.merge(customers, orders, left_on='customer_id', right_on='cust_id')

# 여러 키로 조인
# merged = pd.merge(df1, df2, on=['key1', 'key2'])

설명

이것이 하는 일: 위 코드는 실무에서 가장 많이 사용하는 데이터 조인 패턴을 보여줍니다. SQL의 JOIN 개념을 Pandas로 구현합니다.

첫 번째로, inner join은 가장 기본적인 병합 방식입니다. 두 DataFrame에 공통으로 존재하는 customer_id만 매칭됩니다.

예제에서는 customers에 있는 1, 2, 3 중에서 orders에도 있는 1, 2, 3만 결과에 포함됩니다. customer_id 4(정수진)는 주문이 없어서 제외되고, customer_id 5는 고객 정보가 없어서 제외됩니다.

데이터 정합성이 확실할 때 사용합니다. 두 번째로, left join은 실무에서 가장 자주 사용됩니다.

왼쪽 DataFrame(customers)의 모든 행을 유지하고, 오른쪽(orders)에서 매칭되는 정보를 가져옵니다. 만약 매칭되는 정보가 없으면 NaN으로 채워집니다.

예제에서는 정수진(customer_id 4)은 주문 정보가 NaN으로 표시됩니다. "모든 고객을 보되, 주문이 있으면 주문 정보도 함께 보고 싶다"는 요구사항에 적합합니다.

세 번째로, right join은 left join의 반대입니다. 오른쪽 DataFrame의 모든 행을 유지합니다.

예제에서는 모든 주문(orders)이 결과에 포함되고, customer_id 5의 주문은 고객 정보가 NaN으로 표시됩니다. 실무에서는 left join을 주로 사용하고, 필요하면 DataFrame 순서를 바꿔서 사용합니다.

네 번째로, outer join(full outer join)은 양쪽의 모든 데이터를 포함합니다. 고객 4(주문 없음)도 포함되고, 주문 105(고객 정보 없음)도 포함됩니다.

매칭되지 않는 부분은 NaN으로 채워집니다. 데이터 불일치를 확인하거나, 모든 데이터를 잃지 않고 병합할 때 사용합니다.

다섯 번째로, on 매개변수는 조인 키(공통 컬럼)를 지정합니다. 두 DataFrame에 같은 이름의 컬럼이 있으면 on으로 지정하고, 컬럼명이 다르면 left_on과 right_on을 따로 지정합니다.

여러 컬럼을 조인 키로 사용하려면 리스트로 전달합니다(예: on=['date', 'product_id']). 여러분이 이 코드를 사용하면 분산된 데이터를 하나로 통합하여 종합적인 분석을 할 수 있습니다.

예를 들어, 고객 정보와 주문 정보를 합치면 "서울 거주 고객의 평균 주문 금액"같은 질문에 답할 수 있습니다. 또한 데이터 품질 검증(매칭되지 않는 레코드 찾기)에도 유용하며, 데이터베이스를 다루는 것처럼 Pandas로 복잡한 데이터 처리를 할 수 있습니다.

실전 팁

💡 merge() 후에는 항상 결과 DataFrame의 shape를 확인하세요. 예상보다 행이 많다면 중복 매칭이 발생한 것이고, 적다면 매칭되지 않은 데이터가 많다는 의미입니다.

💡 일대다(one-to-many) 관계에서는 다(many) 쪽만큼 행이 복제됩니다. 예를 들어, 한 고객이 여러 주문을 한 경우 고객 정보가 주문 개수만큼 반복됩니다.

💡 validate 매개변수로 조인의 무결성을 검증할 수 있습니다. validate='1:m'은 왼쪽이 유일키, 오른쪽이 중복 가능함을 검증합니다. 잘못된 데이터 구조를 조기에 발견할 수 있습니다.

💡 merge() 대신 join() 메서드도 있지만, 인덱스 기반으로 동작하므로 on으로 컬럼을 지정하는 merge()가 더 직관적이고 자주 사용됩니다.

이상으로 Pandas 데이터 분석 실전 예제 8개를 완성했습니다! 각 카드는 초급 개발자도 실무에서 바로 활용할 수 있도록 친근하고 상세하게 작성되었습니다. 💻📊


#Pandas#DataFrame#DataAnalysis#DataProcessing#Visualization#Python

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.
이전3/3
다음